From f58e0d4240d9466c8947e66e42213b4867c9f94c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Thu, 11 Jan 2024 15:17:10 +0100 Subject: [PATCH] MOBILE-3947 gulp: Remove unnecessary task push --- gulp/dev-config.js | 69 ------- gulp/git.js | 237 ---------------------- gulp/jira.js | 476 --------------------------------------------- gulp/task-push.js | 280 -------------------------- gulp/url.js | 79 -------- gulp/utils.js | 120 ------------ gulpfile.js | 8 - package-lock.json | 182 ----------------- package.json | 1 - 9 files changed, 1452 deletions(-) delete mode 100644 gulp/dev-config.js delete mode 100644 gulp/git.js delete mode 100644 gulp/jira.js delete mode 100644 gulp/task-push.js delete mode 100644 gulp/url.js delete mode 100644 gulp/utils.js diff --git a/gulp/dev-config.js b/gulp/dev-config.js deleted file mode 100644 index b270bf51f..000000000 --- a/gulp/dev-config.js +++ /dev/null @@ -1,69 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -const fs = require('fs'); - -const DEV_CONFIG_FILE = '.moodleapp-dev-config'; - -/** - * Class to read and write dev-config data from a file. - */ -class DevConfig { - - constructor() { - this.loadFileData(); - } - - /** - * Get a setting. - * - * @param name Name of the setting to get. - * @param defaultValue Value to use if not found. - */ - get(name, defaultValue) { - return typeof this.config[name] != 'undefined' ? this.config[name] : defaultValue; - } - - /** - * Load file data to memory. - */ - loadFileData() { - if (!fs.existsSync(DEV_CONFIG_FILE)) { - this.config = {}; - - return; - } - - try { - this.config = JSON.parse(fs.readFileSync(DEV_CONFIG_FILE)); - } catch (error) { - console.error('Error reading dev config file.', error); - this.config = {}; - } - } - - /** - * Save some settings. - * - * @param settings Object with the settings to save. - */ - save(settings) { - this.config = Object.assign(this.config, settings); - - // Save the data in the dev file. - fs.writeFileSync(DEV_CONFIG_FILE, JSON.stringify(this.config, null, 4)); - } -} - -module.exports = new DevConfig(); diff --git a/gulp/git.js b/gulp/git.js deleted file mode 100644 index e7eacb000..000000000 --- a/gulp/git.js +++ /dev/null @@ -1,237 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -const exec = require('child_process').exec; -const fs = require('fs'); -const DevConfig = require('./dev-config'); -const Utils = require('./utils'); - -/** - * Class to run git commands. - */ -class Git { - - /** - * Create a patch. - * - * @param range Show only commits in the specified revision range. - * @param saveTo Path to the file to save the patch to. If not defined, the patch contents will be returned. - * @return Promise resolved when done. If saveTo not provided, it will return the patch contents. - */ - createPatch(range, saveTo) { - return new Promise((resolve, reject) => { - exec(`git format-patch ${range} --stdout`, (err, result) => { - if (err) { - reject(err); - return; - } - - if (!saveTo) { - resolve(result); - return; - } - - // Save it to a file. - const directory = saveTo.substring(0, saveTo.lastIndexOf('/')); - if (directory && directory != '.' && directory != '..' && !fs.existsSync(directory)) { - fs.mkdirSync(directory); - } - fs.writeFileSync(saveTo, result); - - resolve(); - }); - }); - } - - /** - * Get current branch. - * - * @return Promise resolved with the branch name. - */ - getCurrentBranch() { - return new Promise((resolve, reject) => { - exec('git branch --show-current', (err, branch) => { - if (branch) { - resolve(branch.replace('\n', '')); - } else { - reject (err || 'Current branch not found.'); - } - }); - }); - } - - /** - * Get the HEAD commit for a certain branch. - * - * @param branch Name of the branch. - * @param branchData Parsed branch data. If not provided it will be calculated. - * @return HEAD commit. - */ - async getHeadCommit(branch, branchData) { - if (!branchData) { - // Parse the branch to get the project and issue number. - branchData = Utils.parseBranch(branch); - } - - // Loop over the last commits to find the first commit messages that doesn't belong to the issue. - const commitsString = await this.log(50, branch, '%s_____%H'); - const commits = commitsString.split('\n'); - commits.pop(); // Remove last element, it's an empty string. - - for (let i = 0; i < commits.length; i++) { - const commit = commits[i]; - const match = Utils.getIssueFromCommitMessage(commit) == branchData.issue; - - if (i === 0 && !match) { - // Most recent commit doesn't belong to the issue. Stop looking. - break; - } - - if (!match) { - // The commit does not match any more, we found it! - return commit.split('_____')[1]; - } - } - - // Couldn't find the commit using the commit names, get the last commit in the integration branch. - const remote = DevConfig.get('upstreamRemote', 'origin'); - console.log(`Head commit not found using commit messages. Get last commit from ${remote}/integration`); - const hashes = await this.hashes(1, `${remote}/integration`); - - return hashes[0]; - } - - /** - * Get the URL of a certain remote. - * - * @param remote Remote name. - * @return Promise resolved with the remote URL. - */ - getRemoteUrl(remote) { - return new Promise((resolve, reject) => { - exec(`git remote get-url ${remote}`, (err, url) => { - if (url) { - resolve(url.replace('\n', '')); - } else { - reject (err || 'Remote not found.'); - } - }); - }); - } - - /** - * Return the latest hashes from git log. - * - * @param count Number of commits to display. - * @param range Show only commits in the specified revision range. - * @param format Pretty-print the contents of the commit logs in a given format. - * @return Promise resolved with the list of hashes. - */ - async hashes(count, range, format) { - format = format || '%H'; - - const hashList = await this.log(count, range, format); - - const hashes = hashList.split('\n'); - hashes.pop(); // Remove last element, it's an empty string. - - return hashes; - } - - /** - * Calls the log command and returns the raw output. - * - * @param count Number of commits to display. - * @param range Show only commits in the specified revision range. - * @param format Pretty-print the contents of the commit logs in a given format. - * @param path Show only commits that are enough to explain how the files that match the specified paths came to be. - * @return Promise resolved with the result. - */ - log(count, range, format, path) { - if (typeof count == 'undefined') { - count = 10; - } - - let command = 'git log'; - - if (count > 0) { - command += ` -n ${count} `; - } - if (format) { - command += ` --format=${format} `; - } - if (range){ - command += ` ${range} `; - } - if (path) { - command += ` -- ${path}`; - } - - return new Promise((resolve, reject) => { - exec(command, (err, result, stderr) => { - if (err) { - reject(err); - } else { - resolve(result); - } - }); - }); - } - - /** - * Return the latest titles of the commit messages. - * - * @param count Number of commits to display. - * @param range Show only commits in the specified revision range. - * @param path Show only commits that are enough to explain how the files that match the specified paths came to be. - * @return Promise resolved with the list of titles. - */ - async messages(count, range, path) { - count = typeof count != 'undefined' ? count : 10; - - const messageList = await this.log(count, range, '%s', path); - - const messages = messageList.split('\n'); - messages.pop(); // Remove last element, it's an empty string. - - return messages; - } - - /** - * Push a branch. - * - * @param remote Remote to use. - * @param branch Branch to push. - * @param force Whether to force the push. - * @return Promise resolved when done. - */ - push(remote, branch, force) { - return new Promise((resolve, reject) => { - let command = `git push ${remote} ${branch}`; - if (force) { - command += ' -f'; - } - - exec(command, (err, result, stderr) => { - if (err) { - reject(err); - } else { - resolve(); - } - }); - }); - } -} - -module.exports = new Git(); diff --git a/gulp/jira.js b/gulp/jira.js deleted file mode 100644 index 5e3474973..000000000 --- a/gulp/jira.js +++ /dev/null @@ -1,476 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -const exec = require('child_process').exec; -const https = require('https'); -const inquirer = require('inquirer'); -const fs = require('fs'); -const request = require('request'); // This lib is deprecated, but it was the only way I found to make upload files work. -const DevConfig = require('./dev-config'); -const Git = require('./git'); -const Url = require('./url'); -const Utils = require('./utils'); - -const apiVersion = 2; - -/** - * Class to interact with Jira. - */ -class Jira { - - /** - * Ask the password to the user. - * - * @return Promise resolved with the password. - */ - async askPassword() { - const data = await inquirer.prompt([ - { - type: 'password', - name: 'password', - message: `Please enter the password for the username ${this.username}.`, - }, - ]); - - return data.password; - } - - /** - * Ask the user the tracker data. - * - * @return Promise resolved with the data, rejected if cannot get. - */ - async askTrackerData() { - const data = await inquirer.prompt([ - { - type: 'input', - name: 'url', - message: 'Please enter the tracker URL.', - default: 'https://tracker.moodle.org/', - }, - { - type: 'input', - name: 'username', - message: 'Please enter your tracker username.', - }, - ]); - - DevConfig.save({ - 'tracker.url': data.url, - 'tracker.username': data.username, - }); - - return data; - } - - /** - * Build URL to perform requests to Jira. - * - * @param uri URI to add the the Jira URL. - * @return URL. - */ - buildRequestUrl(uri) { - return Utils.concatenatePaths([this.url, this.uri, '/rest/api/', apiVersion, uri]); - } - - /** - * Delete an attachment. - * - * @param attachmentId Attachment ID. - * @return Promise resolved when done. - */ - async deleteAttachment(attachmentId) { - const response = await this.request(`attachment/${attachmentId}`, 'DELETE'); - - if (response.status != 204) { - throw new Error('Could not delete the attachment'); - } - } - - /** - * Load the issue info from jira server using a REST API call. - * - * @param key Key to identify the issue. E.g. MOBILE-1234. - * @param fields Fields to get. - * @return Promise resolved with the issue data. - */ - async getIssue(key, fields) { - fields = fields || '*all,-comment'; - - await this.init(); // Initialize data if needed. - - const response = await this.request(`issue/${key}`, 'GET', {'fields': fields, 'expand': 'names'}); - - if (response.status == 404) { - throw new Error('Issue could not be found.'); - } else if (response.status != 200) { - throw new Error('The tracker is not available.') - } - - const issue = response.data; - issue.named = {}; - - // Populate the named fields in a separate key. Allows us to easily find them without knowing the field ID. - const nameList = issue.names || {}; - for (const fieldKey in issue.fields) { - if (nameList[fieldKey]) { - issue.named[nameList[fieldKey]] = issue.fields[fieldKey]; - } - } - - return issue - } - - /** - * Load the version info from the jira server using a rest api call. - * - * @return Promise resolved when done. - */ - async getServerInfo() { - const response = await this.request('serverInfo'); - - if (response.status != 200) { - throw new Error(`Unexpected response code: ${response.status}`, response); - } - - this.version = response.data; - } - - /** - * Get tracker data to push an issue. - * - * @return Promise resolved with the data. - */ - async getTrackerData() { - // Check dev-config file first. - let data = this.getTrackerDataFromDevConfig(); - - if (data) { - console.log('Using tracker data from dev-config file'); - return data; - } - - // Try to use mdk now. - try { - data = await this.getTrackerDataFromMdk(); - - console.log('Using tracker data from mdk'); - - return data; - } catch (error) { - // MDK not available or not configured. Ask for the data. - const trackerData = await this.askTrackerData(); - - trackerData.fromInput = true; - - return trackerData; - } - } - - /** - * Get tracker data from dev config file. - * - * @return Data, undefined if cannot get. - */ - getTrackerDataFromDevConfig() { - const url = DevConfig.get('tracker.url'); - const username = DevConfig.get('tracker.username'); - - if (url && username) { - return { - url, - username, - }; - } - } - - /** - * Get tracker URL and username from mdk. - * - * @return Promise resolved with the data, rejected if cannot get. - */ - getTrackerDataFromMdk() { - return new Promise((resolve, reject) => { - exec('mdk config show tracker.url', (err, url) => { - if (!url) { - reject(err || 'URL not found.'); - return; - } - - exec('mdk config show tracker.username', (error, username) => { - if (username) { - resolve({ - url: url.replace('\n', ''), - username: username.replace('\n', ''), - }); - } else { - reject(error || 'Username not found.'); - } - }); - }); - }); - } - - /** - * Initialize some data. - * - * @return Promise resolved when done. - */ - async init() { - if (this.initialized) { - // Already initialized. - return; - } - - // Get tracker URL and username. - let trackerData = await this.getTrackerData(); - - this.url = trackerData.url; - this.username = trackerData.username; - - const parsed = Url.parse(this.url); - this.ssl = parsed.protocol == 'https'; - this.host = parsed.domain; - this.uri = parsed.path; - - // Get the password. - const keytar = require('keytar'); - - this.password = await keytar.getPassword('mdk-jira-password', this.username); // Use same service name as mdk. - - if (!this.password) { - // Ask the user. - this.password = await this.askPassword(); - } - - while (!this.initialized) { - try { - await this.getServerInfo(); - - this.initialized = true; - keytar.setPassword('mdk-jira-password', this.username, this.password); - } catch (error) { - console.log('Error connecting to the server. Please make sure you entered the data correctly.', error); - if (trackerData.fromInput) { - // User entered the data manually, ask him again. - trackerData = await this.askTrackerData(); - - this.url = trackerData.url; - this.username = trackerData.username; - } - - this.password = await this.askPassword(); - } - } - } - - /** - * Check if a certain issue could be a security issue. - * - * @param key Key to identify the issue. E.g. MOBILE-1234. - * @return Promise resolved with boolean: whether it's a security issue. - */ - async isSecurityIssue(key) { - const issue = await this.getIssue(key, 'security'); - - return issue.fields && !!issue.fields.security; - } - - /** - * Sends a request to the server and returns the data. - * - * @param uri URI to add the the Jira URL. - * @param method Method to use. Defaults to 'GET'. - * @param params Params to send as GET params (in the URL). - * @param data JSON string with the data to send as POST/PUT params. - * @param headers Headers to send. - * @return Promise resolved with the result. - */ - request(uri, method, params, data, headers) { - uri = uri || ''; - method = (method || 'GET').toUpperCase(); - data = data || ''; - params = params || {}; - headers = headers || {}; - headers['Content-Type'] = 'application/json'; - - return new Promise((resolve, reject) => { - - // Build the request URL. - const url = Url.addParamsToUrl(this.buildRequestUrl(uri), params); - - // Initialize the request. - const options = { - method: method, - auth: `${this.username}:${this.password}`, - headers: headers, - }; - const buildRequest = https.request(url, options); - - // Add data. - if (data) { - buildRequest.write(data); - } - - // Treat response. - buildRequest.on('response', (response) => { - // Read the result. - let result = ''; - response.on('data', (chunk) => { - result += chunk; - }); - response.on('end', () => { - try { - result = JSON.parse(result); - } catch (error) { - // Leave it as text. - } - - resolve({ - status: response.statusCode, - data: result, - }); - }); - }); - - buildRequest.on('error', (e) => { - reject(e); - }); - - // Send the request. - buildRequest.end(); - }); - } - - /** - * Sets a set of fields for a certain issue in Jira. - * - * @param issueId Key to identify the issue. E.g. MOBILE-1234. - * @param updates Object with the fields to update. - * @return Promise resolved when done. - */ - async setCustomFields(issueId, updates) { - const issue = await this.getIssue(issueId); - const update = {'fields': {}}; - - // Detect which fields have changed. - for (const updateName in updates) { - const updateValue = updates[updateName]; - const remoteValue = issue.named[updateName]; - - if (!remoteValue || remoteValue != updateValue) { - // Map the label of the field with the field code. - let fieldKey; - - for (const id in issue.names) { - if (issue.names[id] == updateName) { - fieldKey = id; - break; - } - } - - if (!fieldKey) { - throw new Error(`Could not find the field named ${updateName}.`); - } - - update.fields[fieldKey] = updateValue; - } - } - - if (!Object.keys(update.fields).length) { - // No fields to update. - console.log('No updates required.') - return; - } - - const response = await this.request(`issue/${key}`, 'PUT', null, JSON.stringify(update)); - - if (response.status != 204) { - throw new Error(`Issue was not updated: ${response.status}`, response.data); - } - - console.log('Issue updated successfully.'); - } - - /** - * Upload a new attachment to an issue. - * - * @param key Key to identify the issue. E.g. MOBILE-1234. - * @param filePath Path to the file to upload. - * @return Promise resolved when done. - */ - async upload(key, filePath) { - - const uri = `issue/${key}/attachments`; - const headers = { - 'X-Atlassian-Token': 'nocheck', - } - - const response = await this.uploadFile(uri, 'file', filePath, headers); - - if (response.status != 200) { - throw new Error('Could not upload file to Jira issue'); - } - - console.log('File successfully uploaded.') - } - - /** - * Upload a file to Jira. - * - * @param uri URI to add the the Jira URL. - * @param fieldName Name of the form field where to put the file. - * @param filePath Path to the file. - * @param headers Headers. - * @return Promise resolved with the result. - */ - async uploadFile(uri, fieldName, filePath, headers) { - uri = uri || ''; - headers = headers || {}; - headers['Content-Type'] = 'multipart/form-data'; - - return new Promise((resolve) => { - // Add the file to the form data. - const formData = {}; - formData[fieldName] = { - value: fs.createReadStream(filePath), - options: { - filename: filePath.substr(filePath.lastIndexOf('/') + 1), - contentType: 'multipart/form-data', - }, - }; - - // Perform the request. - const options = { - url: this.buildRequestUrl(uri), - method: 'POST', - headers: headers, - auth: { - user: this.username, - pass: this.password, - }, - formData: formData, - }; - - request(options, (_err, httpResponse, body) => { - resolve({ - status: httpResponse.statusCode, - data: body, - }); - }); - }); - } -} - -module.exports = new Jira(); diff --git a/gulp/task-push.js b/gulp/task-push.js deleted file mode 100644 index 54d151dde..000000000 --- a/gulp/task-push.js +++ /dev/null @@ -1,280 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -const gulp = require('gulp'); -const inquirer = require('inquirer'); -const DevConfig = require('./dev-config'); -const Git = require('./git'); -const Jira = require('./jira'); -const Utils = require('./utils'); - -/** - * Task to push a git branch and update tracker issue. - */ -class PushTask { - - /** - * Ask the user whether he wants to continue. - * - * @return Promise resolved with boolean: true if he wants to continue. - */ - async askConfirmContinue() { - const answer = await inquirer.prompt([ - { - type: 'input', - name: 'confirm', - message: 'Are you sure you want to continue?', - default: 'n', - }, - ]); - - return answer.confirm == 'y'; - } - - /** - * Push a patch to the tracker and remove the previous one. - * - * @param branch Branch name. - * @param branchData Parsed branch data. - * @param remote Remote used. - * @return Promise resolved when done. - */ - async pushPatch(branch, branchData, remote) { - const headCommit = await Git.getHeadCommit(branch, branchData); - - if (!headCommit) { - throw new Error('Head commit not resolved, abort pushing patch.'); - } - - // Create the patch file. - const fileName = branch + '.patch'; - const tmpPatchPath = `./tmp/${fileName}`; - - await Git.createPatch(`${headCommit}...${branch}`, tmpPatchPath); - console.log('Git patch created'); - - // Check if there is an attachment with same name in the issue. - const issue = await Jira.getIssue(branchData.issue, 'attachment'); - - let existingAttachmentId; - const attachments = (issue.fields && issue.fields.attachment) || []; - for (const i in attachments) { - if (attachments[i].filename == fileName) { - // Found an existing attachment with the same name, we keep track of it. - existingAttachmentId = attachments[i].id; - break - } - } - - // Push the patch to the tracker. - console.log(`Uploading patch ${fileName} to the tracker...`); - await Jira.upload(branchData.issue, tmpPatchPath); - - if (existingAttachmentId) { - // On success, deleting file that was there before. - try { - console.log('Deleting older patch...') - await Jira.deleteAttachment(existingAttachmentId); - } catch (error) { - console.log('Could not delete older attachment.'); - } - } - } - - /** - * Run the task. - * - * @param args Command line arguments. - * @param done Function to call when done. - */ - async run(args, done) { - try { - const remote = args.remote || DevConfig.get('upstreamRemote', 'origin'); - let branch = args.branch; - const force = !!args.force; - - if (!branch) { - branch = await Git.getCurrentBranch(); - } - - if (!branch) { - throw new Error('Cannot determine the current branch. Please make sure youu aren\'t in detached HEAD state'); - } else if (branch == 'HEAD') { - throw new Error('Cannot push HEAD branch'); - } - - // Parse the branch to get the project and issue number. - const branchData = Utils.parseBranch(branch); - const keepRunning = await this.validateCommitMessages(branchData); - - if (!keepRunning) { - // Last commit not valid, stop. - console.log('Exiting...'); - done(); - return; - } - - if (!args.patch) { - // Check if it's a security issue to force patch mode. - try { - args.patch = await Jira.isSecurityIssue(branchData.issue); - - if (args.patch) { - console.log(`${branchData.issue} appears to be a security issue, switching to patch mode...`); - } - } catch (error) { - console.log(`Could not check if ${branchData.issue} is a security issue.`); - } - } - - if (args.patch) { - // Create and upload a patch file. - await this.pushPatch(branch, branchData, remote); - } else { - // Push the branch. - console.log(`Pushing branch ${branch} to remote ${remote}...`); - await Git.push(remote, branch, force); - - // Update tracker info. - console.log(`Branch pushed, update tracker info...`); - await this.updateTrackerGitInfo(branch, branchData, remote); - } - } catch (error) { - console.error(error); - } - - done(); - } - - /** - * Update git info in the tracker issue. - * - * @param branch Branch name. - * @param branchData Parsed branch data. - * @param remote Remote used. - * @return Promise resolved when done. - */ - async updateTrackerGitInfo(branch, branchData, remote) { - // Get the repository data for the project. - let repositoryUrl = DevConfig.get(branchData.project + '.repositoryUrl'); - let diffUrlTemplate = DevConfig.get(branchData.project + '.diffUrlTemplate', ''); - - if (!repositoryUrl) { - // Calculate the repositoryUrl based on the remote URL. - repositoryUrl = await Git.getRemoteUrl(remote); - } - - // Make sure the repository URL uses the regular format. - repositoryUrl = repositoryUrl.replace(/^(git@|git:\/\/)/, 'https://') - .replace(/\.git$/, '') - .replace('github.com:', 'github.com/'); - - if (!diffUrlTemplate) { - diffUrlTemplate = Utils.concatenatePaths([repositoryUrl, 'compare/%headcommit%...%branch%']); - } - - // Now create the git URL for the repository. - const repositoryGitUrl = repositoryUrl.replace(/^https?:\/\//, 'git://') + '.git'; - - // Search HEAD commit to put in the diff URL. - console.log ('Searching for head commit...'); - let headCommit = await Git.getHeadCommit(branch, branchData); - - if (!headCommit) { - throw new Error('Head commit not resolved, aborting update of tracker fields'); - } - - headCommit = headCommit.substr(0, 10); - console.log(`Head commit resolved to ${headCommit}`); - - // Calculate last properties needed. - const diffUrl = diffUrlTemplate.replace('%branch%', branch).replace('%headcommit%', headCommit); - const fieldRepositoryUrl = DevConfig.get('tracker.fieldnames.repositoryurl', 'Pull from Repository'); - const fieldBranch = DevConfig.get('tracker.fieldnames.branch', 'Pull Master Branch'); - const fieldDiffUrl = DevConfig.get('tracker.fieldnames.diffurl', 'Pull Master Diff URL'); - - // Update tracker fields. - const updates = {}; - updates[fieldRepositoryUrl] = repositoryGitUrl; - updates[fieldBranch] = branch; - updates[fieldDiffUrl] = diffUrl; - - console.log('Setting tracker fields...'); - await Jira.setCustomFields(branchData.issue, updates); - } - - /** - * Validate commit messages comparing them with the branch name. - * - * @param branchData Parsed branch data. - * @return True if value is ok or the user wants to continue anyway, false to stop. - */ - async validateCommitMessages(branchData) { - const messages = await Git.messages(30); - - let numConsecutive = 0; - let wrongCommitCandidate = null; - - for (let i = 0; i < messages.length; i++) { - const message = messages[i]; - const issue = Utils.getIssueFromCommitMessage(message); - - if (!issue || issue != branchData.issue) { - if (i === 0) { - // Last commit is wrong, it shouldn't happen. Ask the user if he wants to continue. - if (!issue) { - console.log('The issue number could not be found in the last commit message.'); - console.log(`Commit: ${message}`); - } else if (issue != branchData.issue) { - console.log('The issue number in the last commit does not match the branch being pushed to.'); - console.log(`Branch: ${branchData.issue} vs. commit: ${issue}`); - } - - return this.askConfirmContinue(); - } - - numConsecutive++; - if (numConsecutive > 2) { - // 3 consecutive commits with different branch, probably the branch commits are over. Everything OK. - return true; - - // Don't treat a merge pull request commit as a wrong commit between right commits. - // The current push could be a quick fix after a merge. - } else if (!wrongCommitCandidate && message.indexOf('Merge pull request') == -1) { - wrongCommitCandidate = { - message: message, - issue: issue, - index: i, - }; - } - } else if (wrongCommitCandidate) { - // We've found a commit with the branch name after a commit with a different branch. Probably wrong commit. - if (!wrongCommitCandidate.issue) { - console.log('The issue number could not be found in one of the commit messages.'); - console.log(`Commit: ${wrongCommitCandidate.message}`); - } else { - console.log('The issue number in a certain commit does not match the branch being pushed to.'); - console.log(`Branch: ${branchData.issue} vs. commit: ${wrongCommitCandidate.issue}`); - console.log(`Commit message: ${wrongCommitCandidate.message}`); - } - - return this.askConfirmContinue(); - } - } - - return true; - } -} - -module.exports = PushTask; diff --git a/gulp/url.js b/gulp/url.js deleted file mode 100644 index 8b46409f6..000000000 --- a/gulp/url.js +++ /dev/null @@ -1,79 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/** - * Class with helper functions for urls. - */ -class Url { - - /** - * Add params to a URL. - * - * @param url URL to add the params to. - * @param params Object with the params to add. - * @return URL with params. - */ - static addParamsToUrl(url, params) { - let separator = url.indexOf('?') != -1 ? '&' : '?'; - - for (const key in params) { - let value = params[key]; - - // Ignore objects. - if (typeof value != 'object') { - url += separator + key + '=' + value; - separator = '&'; - } - } - - return url; - } - - /** - * Parse parts of a url, using an implicit protocol if it is missing from the url. - * - * @param url Url. - * @return Url parts. - */ - static parse(url) { - // Parse url with regular expression taken from RFC 3986: https://tools.ietf.org/html/rfc3986#appendix-B. - const match = url.trim().match(/^(([^:/?#]+):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/); - - if (!match) { - return null; - } - - const host = match[4] || ''; - - // Get the credentials and the port from the host. - const [domainAndPort, credentials] = host.split('@').reverse(); - const [domain, port] = domainAndPort.split(':'); - const [username, password] = credentials ? credentials.split(':') : []; - - // Prepare parts replacing empty strings with undefined. - return { - protocol: match[2] || undefined, - domain: domain || undefined, - port: port || undefined, - credentials: credentials || undefined, - username: username || undefined, - password: password || undefined, - path: match[5] || undefined, - query: match[7] || undefined, - fragment: match[9] || undefined, - }; - } -} - -module.exports = Url; diff --git a/gulp/utils.js b/gulp/utils.js deleted file mode 100644 index 1e8783337..000000000 --- a/gulp/utils.js +++ /dev/null @@ -1,120 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -const DevConfig = require('./dev-config'); -const DEFAULT_ISSUE_REGEX = '^(MOBILE)[-_]([0-9]+)'; - -/** - * Class with some utility functions. - */ -class Utils { - /** - * Concatenate several paths, adding a slash between them if needed. - * - * @param paths List of paths. - * @return Concatenated path. - */ - static concatenatePaths(paths) { - if (!paths.length) { - return ''; - } - - // Remove all slashes between paths. - for (let i = 0; i < paths.length; i++) { - if (!paths[i]) { - continue; - } - - if (i === 0) { - paths[i] = String(paths[i]).replace(/\/+$/g, ''); - } else if (i === paths.length - 1) { - paths[i] = String(paths[i]).replace(/^\/+/g, ''); - } else { - paths[i] = String(paths[i]).replace(/^\/+|\/+$/g, ''); - } - } - - // Remove empty paths. - paths = paths.filter(path => !!path); - - return paths.join('/'); - } - - /** - * Get command line arguments. - * - * @return Object with command line arguments. - */ - static getCommandLineArguments() { - - let args = {}; - let curOpt; - - for (const argument of process.argv) { - const thisOpt = argument.trim(); - const option = thisOpt.replace(/^\-+/, ''); - - if (option === thisOpt) { - // argument value - if (curOpt) { - args[curOpt] = option; - } - curOpt = null; - } - else { - // Argument name. - curOpt = option; - args[curOpt] = true; - } - } - - return args; - } - - /** - * Given a commit message, return the issue name (e.g. MOBILE-1234). - * - * @param commit Commit message. - * @return Issue name. - */ - static getIssueFromCommitMessage(commit) { - const regex = new RegExp(DevConfig.get('wording.branchRegex', DEFAULT_ISSUE_REGEX), 'i'); - const matches = commit.match(regex); - - return matches && matches[0]; - } - - /** - * Parse a branch name to extract some data. - * - * @param branch Branch name to parse. - * @return Data. - */ - static parseBranch(branch) { - const regex = new RegExp(DevConfig.get('wording.branchRegex', DEFAULT_ISSUE_REGEX), 'i'); - - const matches = branch.match(regex); - if (!matches || matches.length < 3) { - throw new Error(`Error parsing branch ${branch}`); - } - - return { - issue: matches[0], - project: matches[1], - issueNumber: matches[2], - }; - } -} - -module.exports = Utils; diff --git a/gulpfile.js b/gulpfile.js index 75ec00a39..ec4a3f831 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -16,9 +16,7 @@ const BuildLangTask = require('./gulp/task-build-lang'); const BuildBehatPluginTask = require('./gulp/task-build-behat-plugin'); const BuildEnvTask = require('./gulp/task-build-env'); const BuildIconsJsonTask = require('./gulp/task-build-icons-json'); -const PushTask = require('./gulp/task-push'); const OverrideLangTask = require('./gulp/task-override-lang'); -const Utils = require('./gulp/utils'); const gulp = require('gulp'); const paths = { @@ -31,8 +29,6 @@ const paths = { ], }; -const args = Utils.getCommandLineArguments(); - // Build the language files into a single file per language. gulp.task('lang', (done) => { new BuildLangTask().run(paths.lang, done); @@ -59,10 +55,6 @@ if (BuildBehatPluginTask.isBehatConfigured()) { }); } -gulp.task('push', (done) => { - new PushTask().run(args, done); -}); - gulp.task( 'default', gulp.parallel([ diff --git a/package-lock.json b/package-lock.json index 841cc1fb7..ead02a7ee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -141,7 +141,6 @@ "jest": "^29.7.0", "jest-preset-angular": "^13.1.4", "jsonc-parser": "^2.3.1", - "keytar": "^7.2.0", "minimatch": "^9.0.3", "native-run": "^2.0.0", "patch-package": "^6.5.0", @@ -11688,21 +11687,6 @@ "node": ">=0.10" } }, - "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "dev": true, - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/dedent": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", @@ -12029,15 +12013,6 @@ "node": ">=8" } }, - "node_modules/detect-libc": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", - "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -13338,15 +13313,6 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, - "node_modules/expand-template": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/expand-tilde": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", @@ -14500,12 +14466,6 @@ "assert-plus": "^1.0.0" } }, - "node_modules/github-from-package": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", - "dev": true - }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -19897,17 +19857,6 @@ "resolved": "https://registry.npmjs.org/keycode/-/keycode-2.2.1.tgz", "integrity": "sha512-Rdgz9Hl9Iv4QKi8b0OlCRQEzp4AgVxyCtz5S/+VIHezDmrDhkp2N2TqBWOLz0/gbeREXOOiI9/4b8BY9uw2vFg==" }, - "node_modules/keytar": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/keytar/-/keytar-7.9.0.tgz", - "integrity": "sha512-VPD8mtVtm5JNtA2AErl6Chp06JBfy7diFQ7TQQhdpWOl6MrCRB+eRbvAZUsbGQS9kiMq0coJsy0W0vHpDCkWsQ==", - "dev": true, - "hasInstallScript": true, - "dependencies": { - "node-addon-api": "^4.3.0", - "prebuild-install": "^7.0.1" - } - }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -21178,18 +21127,6 @@ "node": ">=8" } }, - "node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/min-document": { "version": "2.19.0", "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", @@ -21472,12 +21409,6 @@ "node": ">=10" } }, - "node_modules/mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "dev": true - }, "node_modules/moment": { "version": "2.29.4", "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", @@ -21652,12 +21583,6 @@ "node": ">=0.10.0" } }, - "node_modules/napi-build-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", - "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", - "dev": true - }, "node_modules/native-run": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/native-run/-/native-run-2.0.0.tgz", @@ -21845,24 +21770,6 @@ "lower-case": "^1.1.1" } }, - "node_modules/node-abi": { - "version": "3.51.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.51.0.tgz", - "integrity": "sha512-SQkEP4hmNWjlniS5zdnfIXTk1x7Ome85RDzHlTbBtzE97Gfwz/Ipw4v/Ryk20DWIy3yCNVLVlGKApCnmvYoJbA==", - "dev": true, - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-addon-api": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", - "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==", - "dev": true - }, "node_modules/node-forge": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", @@ -24054,32 +23961,6 @@ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, - "node_modules/prebuild-install": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", - "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==", - "dev": true, - "dependencies": { - "detect-libc": "^2.0.0", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^1.0.1", - "node-abi": "^3.3.0", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^4.0.0", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0" - }, - "bin": { - "prebuild-install": "bin.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -25938,51 +25819,6 @@ "tail": "^0.4.0" } }, - "node_modules/simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/simple-get": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", - "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "decompress-response": "^6.0.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, "node_modules/simple-plist": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/simple-plist/-/simple-plist-1.3.1.tgz", @@ -26948,24 +26784,6 @@ "node": ">=10" } }, - "node_modules/tar-fs": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", - "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", - "dev": true, - "dependencies": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" - } - }, - "node_modules/tar-fs/node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "dev": true - }, "node_modules/tar-stream": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", diff --git a/package.json b/package.json index 7e739e549..f63a48dc4 100644 --- a/package.json +++ b/package.json @@ -176,7 +176,6 @@ "jest": "^29.7.0", "jest-preset-angular": "^13.1.4", "jsonc-parser": "^2.3.1", - "keytar": "^7.2.0", "minimatch": "^9.0.3", "native-run": "^2.0.0", "patch-package": "^6.5.0",