diff --git a/.github/actions/javascript/createOrUpdateStagingDeploy/createOrUpdateStagingDeploy.js b/.github/actions/javascript/createOrUpdateStagingDeploy/createOrUpdateStagingDeploy.js index 854349998902..27db22f3ca71 100644 --- a/.github/actions/javascript/createOrUpdateStagingDeploy/createOrUpdateStagingDeploy.js +++ b/.github/actions/javascript/createOrUpdateStagingDeploy/createOrUpdateStagingDeploy.js @@ -9,7 +9,10 @@ const run = function () { console.log('New version found from action input:', newVersion); let shouldCreateNewStagingDeployCash = false; - let currentStagingDeployCashIssueNumber = null; + let newTag = newVersion; + let newDeployBlockers = []; + let previousStagingDeployCashData = {}; + let currentStagingDeployCashData = null; // Start by fetching the list of recent StagingDeployCash issues, along with the list of open deploy blockers return Promise.all([ @@ -37,6 +40,12 @@ const run = function () { console.log('Failed fetching DeployBlockerCash issues from Github, continuing...'); } + newDeployBlockers = _.map(deployBlockerResponse.data, ({html_url}) => ({ + url: html_url, + number: GithubUtils.getIssueOrPullRequestNumberFromURL(html_url), + isResolved: false, + })); + // Look at the state of the most recent StagingDeployCash, // if it is open then we'll update the existing one, otherwise, we'll create a new one. shouldCreateNewStagingDeployCash = Boolean(stagingDeployResponse.data[0].state !== 'open'); @@ -52,46 +61,37 @@ const run = function () { // Parse the data from the previous StagingDeployCash // (newest if there are none open, otherwise second-newest) - const previousStagingDeployCashData = shouldCreateNewStagingDeployCash + previousStagingDeployCashData = shouldCreateNewStagingDeployCash ? GithubUtils.getStagingDeployCashData(stagingDeployResponse.data[0]) : GithubUtils.getStagingDeployCashData(stagingDeployResponse.data[1]); console.log('Found tag of previous StagingDeployCash:', previousStagingDeployCashData.tag); + // Find the list of PRs merged between the last StagingDeployCash and the new version if (shouldCreateNewStagingDeployCash) { - // Find the list of PRs merged between the last StagingDeployCash and the new version - const mergedPRs = GitUtils.getPullRequestsMergedBetween(previousStagingDeployCashData.tag, newVersion); - // eslint-disable-next-line max-len - console.log(`The following PRs have been merged between the previous StagingDeployCash (${previousStagingDeployCashData.tag}) and new version (${newVersion}):`, mergedPRs); + return GitUtils.getPullRequestsMergedBetween(previousStagingDeployCashData.tag, newTag); + } + + currentStagingDeployCashData = GithubUtils.getStagingDeployCashData(stagingDeployResponse.data[0]); + console.log('Parsed the following data from the current StagingDeployCash:', currentStagingDeployCashData); + + // If we aren't sent a tag, then use the existing tag + newTag = newTag || currentStagingDeployCashData.tag; + + return GitUtils.getPullRequestsMergedBetween(previousStagingDeployCashData.tag, newTag); + }) + .then((mergedPRs) => { + console.log(`The following PRs have been merged between the previous StagingDeployCash (${previousStagingDeployCashData.tag}) and new version (${newVersion}):`, mergedPRs); - // TODO: if there are open DeployBlockers and we are opening a new checklist, - // then we should close / remove the DeployBlockerCash label from those + if (shouldCreateNewStagingDeployCash) { return GithubUtils.generateStagingDeployCashBody( - newVersion, + newTag, _.map(mergedPRs, GithubUtils.getPullRequestURLFromNumber), ); } - // There is an open StagingDeployCash, so we'll be updating it, not creating a new one - const currentStagingDeployCashData = GithubUtils.getStagingDeployCashData(stagingDeployResponse.data[0]); - console.log('Parsed the following data from the current StagingDeployCash:', currentStagingDeployCashData); - currentStagingDeployCashIssueNumber = currentStagingDeployCashData.number; - - const newDeployBlockers = _.map(deployBlockerResponse.data, ({html_url}) => ({ - url: html_url, - number: GithubUtils.getIssueOrPullRequestNumberFromURL(html_url), - isResolved: false, - })); - - // If we aren't sent a tag, then use the existing tag - const tag = newVersion || currentStagingDeployCashData.tag; const didVersionChange = newVersion ? newVersion !== currentStagingDeployCashData.tag : false; - // Find the list of PRs merged between the last StagingDeployCash and the new version - const mergedPRs = GitUtils.getPullRequestsMergedBetween(previousStagingDeployCashData.tag, tag); - // eslint-disable-next-line max-len - console.log(`The following PRs have been merged between the previous StagingDeployCash (${previousStagingDeployCashData.tag}) and new version (${tag}):`, mergedPRs); - // Generate the PR list, preserving the previous state of `isVerified` for existing PRs const PRList = _.sortBy( _.unique( @@ -123,7 +123,7 @@ const run = function () { ); return GithubUtils.generateStagingDeployCashBody( - tag, + newTag, _.pluck(PRList, 'url'), _.pluck(_.where(PRList, {isVerified: true}), 'url'), _.pluck(_.where(PRList, {isAccessible: true}), 'url'), @@ -151,7 +151,7 @@ const run = function () { return GithubUtils.octokit.issues.update({ ...defaultPayload, - issue_number: currentStagingDeployCashIssueNumber, + issue_number: currentStagingDeployCashData.number, }); }) .then(({data}) => { diff --git a/.github/actions/javascript/createOrUpdateStagingDeploy/index.js b/.github/actions/javascript/createOrUpdateStagingDeploy/index.js index 155d321bac76..97ea937ef3e9 100644 --- a/.github/actions/javascript/createOrUpdateStagingDeploy/index.js +++ b/.github/actions/javascript/createOrUpdateStagingDeploy/index.js @@ -19,7 +19,10 @@ const run = function () { console.log('New version found from action input:', newVersion); let shouldCreateNewStagingDeployCash = false; - let currentStagingDeployCashIssueNumber = null; + let newTag = newVersion; + let newDeployBlockers = []; + let previousStagingDeployCashData = {}; + let currentStagingDeployCashData = null; // Start by fetching the list of recent StagingDeployCash issues, along with the list of open deploy blockers return Promise.all([ @@ -47,6 +50,12 @@ const run = function () { console.log('Failed fetching DeployBlockerCash issues from Github, continuing...'); } + newDeployBlockers = _.map(deployBlockerResponse.data, ({html_url}) => ({ + url: html_url, + number: GithubUtils.getIssueOrPullRequestNumberFromURL(html_url), + isResolved: false, + })); + // Look at the state of the most recent StagingDeployCash, // if it is open then we'll update the existing one, otherwise, we'll create a new one. shouldCreateNewStagingDeployCash = Boolean(stagingDeployResponse.data[0].state !== 'open'); @@ -62,46 +71,37 @@ const run = function () { // Parse the data from the previous StagingDeployCash // (newest if there are none open, otherwise second-newest) - const previousStagingDeployCashData = shouldCreateNewStagingDeployCash + previousStagingDeployCashData = shouldCreateNewStagingDeployCash ? GithubUtils.getStagingDeployCashData(stagingDeployResponse.data[0]) : GithubUtils.getStagingDeployCashData(stagingDeployResponse.data[1]); console.log('Found tag of previous StagingDeployCash:', previousStagingDeployCashData.tag); + // Find the list of PRs merged between the last StagingDeployCash and the new version if (shouldCreateNewStagingDeployCash) { - // Find the list of PRs merged between the last StagingDeployCash and the new version - const mergedPRs = GitUtils.getPullRequestsMergedBetween(previousStagingDeployCashData.tag, newVersion); - // eslint-disable-next-line max-len - console.log(`The following PRs have been merged between the previous StagingDeployCash (${previousStagingDeployCashData.tag}) and new version (${newVersion}):`, mergedPRs); + return GitUtils.getPullRequestsMergedBetween(previousStagingDeployCashData.tag, newTag); + } + + currentStagingDeployCashData = GithubUtils.getStagingDeployCashData(stagingDeployResponse.data[0]); + console.log('Parsed the following data from the current StagingDeployCash:', currentStagingDeployCashData); + + // If we aren't sent a tag, then use the existing tag + newTag = newTag || currentStagingDeployCashData.tag; + + return GitUtils.getPullRequestsMergedBetween(previousStagingDeployCashData.tag, newTag); + }) + .then((mergedPRs) => { + console.log(`The following PRs have been merged between the previous StagingDeployCash (${previousStagingDeployCashData.tag}) and new version (${newVersion}):`, mergedPRs); - // TODO: if there are open DeployBlockers and we are opening a new checklist, - // then we should close / remove the DeployBlockerCash label from those + if (shouldCreateNewStagingDeployCash) { return GithubUtils.generateStagingDeployCashBody( - newVersion, + newTag, _.map(mergedPRs, GithubUtils.getPullRequestURLFromNumber), ); } - // There is an open StagingDeployCash, so we'll be updating it, not creating a new one - const currentStagingDeployCashData = GithubUtils.getStagingDeployCashData(stagingDeployResponse.data[0]); - console.log('Parsed the following data from the current StagingDeployCash:', currentStagingDeployCashData); - currentStagingDeployCashIssueNumber = currentStagingDeployCashData.number; - - const newDeployBlockers = _.map(deployBlockerResponse.data, ({html_url}) => ({ - url: html_url, - number: GithubUtils.getIssueOrPullRequestNumberFromURL(html_url), - isResolved: false, - })); - - // If we aren't sent a tag, then use the existing tag - const tag = newVersion || currentStagingDeployCashData.tag; const didVersionChange = newVersion ? newVersion !== currentStagingDeployCashData.tag : false; - // Find the list of PRs merged between the last StagingDeployCash and the new version - const mergedPRs = GitUtils.getPullRequestsMergedBetween(previousStagingDeployCashData.tag, tag); - // eslint-disable-next-line max-len - console.log(`The following PRs have been merged between the previous StagingDeployCash (${previousStagingDeployCashData.tag}) and new version (${tag}):`, mergedPRs); - // Generate the PR list, preserving the previous state of `isVerified` for existing PRs const PRList = _.sortBy( _.unique( @@ -133,7 +133,7 @@ const run = function () { ); return GithubUtils.generateStagingDeployCashBody( - tag, + newTag, _.pluck(PRList, 'url'), _.pluck(_.where(PRList, {isVerified: true}), 'url'), _.pluck(_.where(PRList, {isAccessible: true}), 'url'), @@ -161,7 +161,7 @@ const run = function () { return GithubUtils.octokit.issues.update({ ...defaultPayload, - issue_number: currentStagingDeployCashIssueNumber, + issue_number: currentStagingDeployCashData.number, }); }) .then(({data}) => { @@ -188,28 +188,52 @@ module.exports = run; /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { const _ = __nccwpck_require__(3571); -const {execSync} = __nccwpck_require__(3129); +const {spawn} = __nccwpck_require__(3129); /** * Get merge logs between two refs (inclusive) as a JavaScript object. * * @param {String} fromRef * @param {String} toRef - * @returns {Object<{commit: String, subject: String}>} + * @returns {Promise>} */ function getMergeLogsAsJSON(fromRef, toRef) { const command = `git log --format='{"commit": "%H", "subject": "%s"},' ${fromRef}...${toRef}`; console.log('Getting pull requests merged between the following refs:', fromRef, toRef); console.log('Running command: ', command); - const result = execSync(command).toString().trim(); + return new Promise((resolve, reject) => { + let stdout = ''; + let stderr = ''; + const spawnedProcess = spawn('git', ['log', '--format={"commit": "%H", "subject": "%s"},', `${fromRef}...${toRef}`]); + spawnedProcess.on('message', console.log); + spawnedProcess.stdout.on('data', (chunk) => { + console.log(chunk.toString()); + stdout += chunk.toString(); + }); + spawnedProcess.stderr.on('data', (chunk) => { + console.error(chunk.toString()); + stderr += chunk.toString(); + }); + spawnedProcess.on('close', (code) => { + if (code !== 0) { + return reject(new Error(`${stderr}`)); + } - // Remove any double-quotes from commit subjects - const sanitizedOutput = result - .replace(/(?<="subject": ").*(?="})/g, subject => subject.replace(/"/g, "'")); + resolve(stdout); + }); + spawnedProcess.on('error', err => reject(err)); + }) + .then((stdout) => { + // Remove any double-quotes from commit subjects + let sanitizedOutput = stdout.replace(/(?<="subject": ").*(?="})/g, subject => subject.replace(/"/g, "'")); - // Then format as JSON and convert to a proper JS object - const json = `[${sanitizedOutput}]`.replace('},]', '}]'); - return JSON.parse(json); + // Also remove any newlines + sanitizedOutput = sanitizedOutput.replace(/(\r\n|\n|\r)/gm, ''); + + // Then format as JSON and convert to a proper JS object + const json = `[${sanitizedOutput}]`.replace('},]', '}]'); + return JSON.parse(json); + }); } /** @@ -238,33 +262,38 @@ function getValidMergedPRs(commitMessages) { * * @param {String} fromRef * @param {String} toRef - * @returns {Array} – Pull request numbers + * @returns {Promise>} – Pull request numbers */ function getPullRequestsMergedBetween(fromRef, toRef) { - const targetMergeList = getMergeLogsAsJSON(fromRef, toRef); - console.log(`Commits made between ${fromRef} and ${toRef}:`, targetMergeList); - - // Get the full history on this branch, inclusive of the oldest commit from our target comparison - const oldestCommit = _.last(targetMergeList).commit; - const fullMergeList = getMergeLogsAsJSON(oldestCommit, 'HEAD'); - - // Remove from the final merge list any commits whose message appears in the full merge list more than once. - // This indicates that the PR should not be included in our list because it is a duplicate, and thus has already been processed by our CI - // See https://github.com/Expensify/App/issues/4977 for details - const duplicateMergeList = _.chain(fullMergeList) - .groupBy('subject') - .values() - .filter(i => i.length > 1) - .flatten() - .pluck('commit') - .value(); - const finalMergeList = _.filter(targetMergeList, i => !_.contains(duplicateMergeList, i.commit)); - console.log('Filtered out the following commits which were duplicated in the full git log:', _.difference(targetMergeList, finalMergeList)); - - // Find which commit messages correspond to merged PR's - const pullRequestNumbers = getValidMergedPRs(_.pluck(finalMergeList, 'subject')); - console.log(`List of pull requests merged between ${fromRef} and ${toRef}`, pullRequestNumbers); - return pullRequestNumbers; + let targetMergeList; + return getMergeLogsAsJSON(fromRef, toRef) + .then((mergeList) => { + console.log(`Commits made between ${fromRef} and ${toRef}:`, mergeList); + targetMergeList = mergeList; + + // Get the full history on this branch, inclusive of the oldest commit from our target comparison + const oldestCommit = _.last(mergeList).commit; + return getMergeLogsAsJSON(oldestCommit, 'HEAD'); + }) + .then((fullMergeList) => { + // Remove from the final merge list any commits whose message appears in the full merge list more than once. + // This indicates that the PR should not be included in our list because it is a duplicate, and thus has already been processed by our CI + // See https://github.com/Expensify/App/issues/4977 for details + const duplicateMergeList = _.chain(fullMergeList) + .groupBy('subject') + .values() + .filter(i => i.length > 1) + .flatten() + .pluck('commit') + .value(); + const finalMergeList = _.filter(targetMergeList, i => !_.contains(duplicateMergeList, i.commit)); + console.log('Filtered out the following commits which were duplicated in the full git log:', _.difference(targetMergeList, finalMergeList)); + + // Find which commit messages correspond to merged PR's + const pullRequestNumbers = getValidMergedPRs(_.pluck(finalMergeList, 'subject')); + console.log(`List of pull requests merged between ${fromRef} and ${toRef}`, pullRequestNumbers); + return pullRequestNumbers; + }); } module.exports = { diff --git a/.github/actions/javascript/getDeployPullRequestList/getDeployPullRequestList.js b/.github/actions/javascript/getDeployPullRequestList/getDeployPullRequestList.js index 32f0e494c7ad..753e3b98d43e 100644 --- a/.github/actions/javascript/getDeployPullRequestList/getDeployPullRequestList.js +++ b/.github/actions/javascript/getDeployPullRequestList/getDeployPullRequestList.js @@ -55,7 +55,9 @@ getTagsOrReleases(isProductionDeploy) console.log(`Given ${itemToFetch}: ${inputTag}`); console.log(`Prior ${itemToFetch}: ${priorTag}`); - const pullRequestList = GitUtils.getPullRequestsMergedBetween(priorTag, inputTag); + return GitUtils.getPullRequestsMergedBetween(priorTag, inputTag); + }) + .then((pullRequestList) => { console.log(`Found the pull request list: ${pullRequestList}`); return core.setOutput('PR_LIST', pullRequestList); }) diff --git a/.github/actions/javascript/getDeployPullRequestList/index.js b/.github/actions/javascript/getDeployPullRequestList/index.js index d1e1163657a3..3efbe9136d02 100644 --- a/.github/actions/javascript/getDeployPullRequestList/index.js +++ b/.github/actions/javascript/getDeployPullRequestList/index.js @@ -65,7 +65,9 @@ getTagsOrReleases(isProductionDeploy) console.log(`Given ${itemToFetch}: ${inputTag}`); console.log(`Prior ${itemToFetch}: ${priorTag}`); - const pullRequestList = GitUtils.getPullRequestsMergedBetween(priorTag, inputTag); + return GitUtils.getPullRequestsMergedBetween(priorTag, inputTag); + }) + .then((pullRequestList) => { console.log(`Found the pull request list: ${pullRequestList}`); return core.setOutput('PR_LIST', pullRequestList); }) @@ -124,28 +126,52 @@ module.exports = { /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { const _ = __nccwpck_require__(3571); -const {execSync} = __nccwpck_require__(3129); +const {spawn} = __nccwpck_require__(3129); /** * Get merge logs between two refs (inclusive) as a JavaScript object. * * @param {String} fromRef * @param {String} toRef - * @returns {Object<{commit: String, subject: String}>} + * @returns {Promise>} */ function getMergeLogsAsJSON(fromRef, toRef) { const command = `git log --format='{"commit": "%H", "subject": "%s"},' ${fromRef}...${toRef}`; console.log('Getting pull requests merged between the following refs:', fromRef, toRef); console.log('Running command: ', command); - const result = execSync(command).toString().trim(); + return new Promise((resolve, reject) => { + let stdout = ''; + let stderr = ''; + const spawnedProcess = spawn('git', ['log', '--format={"commit": "%H", "subject": "%s"},', `${fromRef}...${toRef}`]); + spawnedProcess.on('message', console.log); + spawnedProcess.stdout.on('data', (chunk) => { + console.log(chunk.toString()); + stdout += chunk.toString(); + }); + spawnedProcess.stderr.on('data', (chunk) => { + console.error(chunk.toString()); + stderr += chunk.toString(); + }); + spawnedProcess.on('close', (code) => { + if (code !== 0) { + return reject(new Error(`${stderr}`)); + } + + resolve(stdout); + }); + spawnedProcess.on('error', err => reject(err)); + }) + .then((stdout) => { + // Remove any double-quotes from commit subjects + let sanitizedOutput = stdout.replace(/(?<="subject": ").*(?="})/g, subject => subject.replace(/"/g, "'")); - // Remove any double-quotes from commit subjects - const sanitizedOutput = result - .replace(/(?<="subject": ").*(?="})/g, subject => subject.replace(/"/g, "'")); + // Also remove any newlines + sanitizedOutput = sanitizedOutput.replace(/(\r\n|\n|\r)/gm, ''); - // Then format as JSON and convert to a proper JS object - const json = `[${sanitizedOutput}]`.replace('},]', '}]'); - return JSON.parse(json); + // Then format as JSON and convert to a proper JS object + const json = `[${sanitizedOutput}]`.replace('},]', '}]'); + return JSON.parse(json); + }); } /** @@ -174,33 +200,38 @@ function getValidMergedPRs(commitMessages) { * * @param {String} fromRef * @param {String} toRef - * @returns {Array} – Pull request numbers + * @returns {Promise>} – Pull request numbers */ function getPullRequestsMergedBetween(fromRef, toRef) { - const targetMergeList = getMergeLogsAsJSON(fromRef, toRef); - console.log(`Commits made between ${fromRef} and ${toRef}:`, targetMergeList); - - // Get the full history on this branch, inclusive of the oldest commit from our target comparison - const oldestCommit = _.last(targetMergeList).commit; - const fullMergeList = getMergeLogsAsJSON(oldestCommit, 'HEAD'); - - // Remove from the final merge list any commits whose message appears in the full merge list more than once. - // This indicates that the PR should not be included in our list because it is a duplicate, and thus has already been processed by our CI - // See https://github.com/Expensify/App/issues/4977 for details - const duplicateMergeList = _.chain(fullMergeList) - .groupBy('subject') - .values() - .filter(i => i.length > 1) - .flatten() - .pluck('commit') - .value(); - const finalMergeList = _.filter(targetMergeList, i => !_.contains(duplicateMergeList, i.commit)); - console.log('Filtered out the following commits which were duplicated in the full git log:', _.difference(targetMergeList, finalMergeList)); - - // Find which commit messages correspond to merged PR's - const pullRequestNumbers = getValidMergedPRs(_.pluck(finalMergeList, 'subject')); - console.log(`List of pull requests merged between ${fromRef} and ${toRef}`, pullRequestNumbers); - return pullRequestNumbers; + let targetMergeList; + return getMergeLogsAsJSON(fromRef, toRef) + .then((mergeList) => { + console.log(`Commits made between ${fromRef} and ${toRef}:`, mergeList); + targetMergeList = mergeList; + + // Get the full history on this branch, inclusive of the oldest commit from our target comparison + const oldestCommit = _.last(mergeList).commit; + return getMergeLogsAsJSON(oldestCommit, 'HEAD'); + }) + .then((fullMergeList) => { + // Remove from the final merge list any commits whose message appears in the full merge list more than once. + // This indicates that the PR should not be included in our list because it is a duplicate, and thus has already been processed by our CI + // See https://github.com/Expensify/App/issues/4977 for details + const duplicateMergeList = _.chain(fullMergeList) + .groupBy('subject') + .values() + .filter(i => i.length > 1) + .flatten() + .pluck('commit') + .value(); + const finalMergeList = _.filter(targetMergeList, i => !_.contains(duplicateMergeList, i.commit)); + console.log('Filtered out the following commits which were duplicated in the full git log:', _.difference(targetMergeList, finalMergeList)); + + // Find which commit messages correspond to merged PR's + const pullRequestNumbers = getValidMergedPRs(_.pluck(finalMergeList, 'subject')); + console.log(`List of pull requests merged between ${fromRef} and ${toRef}`, pullRequestNumbers); + return pullRequestNumbers; + }); } module.exports = { diff --git a/.github/libs/GitUtils.js b/.github/libs/GitUtils.js index 931876037092..327ef6b2b5e4 100644 --- a/.github/libs/GitUtils.js +++ b/.github/libs/GitUtils.js @@ -1,26 +1,50 @@ const _ = require('underscore'); -const {execSync} = require('child_process'); +const {spawn} = require('child_process'); /** * Get merge logs between two refs (inclusive) as a JavaScript object. * * @param {String} fromRef * @param {String} toRef - * @returns {Object<{commit: String, subject: String}>} + * @returns {Promise>} */ function getMergeLogsAsJSON(fromRef, toRef) { const command = `git log --format='{"commit": "%H", "subject": "%s"},' ${fromRef}...${toRef}`; console.log('Getting pull requests merged between the following refs:', fromRef, toRef); console.log('Running command: ', command); - const result = execSync(command).toString().trim(); + return new Promise((resolve, reject) => { + let stdout = ''; + let stderr = ''; + const spawnedProcess = spawn('git', ['log', '--format={"commit": "%H", "subject": "%s"},', `${fromRef}...${toRef}`]); + spawnedProcess.on('message', console.log); + spawnedProcess.stdout.on('data', (chunk) => { + console.log(chunk.toString()); + stdout += chunk.toString(); + }); + spawnedProcess.stderr.on('data', (chunk) => { + console.error(chunk.toString()); + stderr += chunk.toString(); + }); + spawnedProcess.on('close', (code) => { + if (code !== 0) { + return reject(new Error(`${stderr}`)); + } - // Remove any double-quotes from commit subjects - const sanitizedOutput = result - .replace(/(?<="subject": ").*(?="})/g, subject => subject.replace(/"/g, "'")); + resolve(stdout); + }); + spawnedProcess.on('error', err => reject(err)); + }) + .then((stdout) => { + // Remove any double-quotes from commit subjects + let sanitizedOutput = stdout.replace(/(?<="subject": ").*(?="})/g, subject => subject.replace(/"/g, "'")); - // Then format as JSON and convert to a proper JS object - const json = `[${sanitizedOutput}]`.replace('},]', '}]'); - return JSON.parse(json); + // Also remove any newlines + sanitizedOutput = sanitizedOutput.replace(/(\r\n|\n|\r)/gm, ''); + + // Then format as JSON and convert to a proper JS object + const json = `[${sanitizedOutput}]`.replace('},]', '}]'); + return JSON.parse(json); + }); } /** @@ -49,33 +73,38 @@ function getValidMergedPRs(commitMessages) { * * @param {String} fromRef * @param {String} toRef - * @returns {Array} – Pull request numbers + * @returns {Promise>} – Pull request numbers */ function getPullRequestsMergedBetween(fromRef, toRef) { - const targetMergeList = getMergeLogsAsJSON(fromRef, toRef); - console.log(`Commits made between ${fromRef} and ${toRef}:`, targetMergeList); - - // Get the full history on this branch, inclusive of the oldest commit from our target comparison - const oldestCommit = _.last(targetMergeList).commit; - const fullMergeList = getMergeLogsAsJSON(oldestCommit, 'HEAD'); + let targetMergeList; + return getMergeLogsAsJSON(fromRef, toRef) + .then((mergeList) => { + console.log(`Commits made between ${fromRef} and ${toRef}:`, mergeList); + targetMergeList = mergeList; - // Remove from the final merge list any commits whose message appears in the full merge list more than once. - // This indicates that the PR should not be included in our list because it is a duplicate, and thus has already been processed by our CI - // See https://github.com/Expensify/App/issues/4977 for details - const duplicateMergeList = _.chain(fullMergeList) - .groupBy('subject') - .values() - .filter(i => i.length > 1) - .flatten() - .pluck('commit') - .value(); - const finalMergeList = _.filter(targetMergeList, i => !_.contains(duplicateMergeList, i.commit)); - console.log('Filtered out the following commits which were duplicated in the full git log:', _.difference(targetMergeList, finalMergeList)); + // Get the full history on this branch, inclusive of the oldest commit from our target comparison + const oldestCommit = _.last(mergeList).commit; + return getMergeLogsAsJSON(oldestCommit, 'HEAD'); + }) + .then((fullMergeList) => { + // Remove from the final merge list any commits whose message appears in the full merge list more than once. + // This indicates that the PR should not be included in our list because it is a duplicate, and thus has already been processed by our CI + // See https://github.com/Expensify/App/issues/4977 for details + const duplicateMergeList = _.chain(fullMergeList) + .groupBy('subject') + .values() + .filter(i => i.length > 1) + .flatten() + .pluck('commit') + .value(); + const finalMergeList = _.filter(targetMergeList, i => !_.contains(duplicateMergeList, i.commit)); + console.log('Filtered out the following commits which were duplicated in the full git log:', _.difference(targetMergeList, finalMergeList)); - // Find which commit messages correspond to merged PR's - const pullRequestNumbers = getValidMergedPRs(_.pluck(finalMergeList, 'subject')); - console.log(`List of pull requests merged between ${fromRef} and ${toRef}`, pullRequestNumbers); - return pullRequestNumbers; + // Find which commit messages correspond to merged PR's + const pullRequestNumbers = getValidMergedPRs(_.pluck(finalMergeList, 'subject')); + console.log(`List of pull requests merged between ${fromRef} and ${toRef}`, pullRequestNumbers); + return pullRequestNumbers; + }); } module.exports = { diff --git a/tests/unit/getPullRequestsMergedBetweenTest.sh b/tests/unit/getPullRequestsMergedBetweenTest.sh index 4611a2c6c825..538de83700b4 100755 --- a/tests/unit/getPullRequestsMergedBetweenTest.sh +++ b/tests/unit/getPullRequestsMergedBetweenTest.sh @@ -6,7 +6,7 @@ set -e TEST_DIR=$(dirname "$(dirname "$(cd "$(dirname "$0")" || exit 1;pwd)/$(basename "$0")")") SCRIPTS_DIR="$TEST_DIR/../scripts" DUMMY_DIR="$HOME/DumDumRepo" -getPullRequestsMergedBetween="$TEST_DIR/utils/getPullRequestsMergedBetween.js" +getPullRequestsMergedBetween="$TEST_DIR/utils/getPullRequestsMergedBetween.mjs" source "$SCRIPTS_DIR/shellUtils.sh" diff --git a/tests/utils/getPullRequestsMergedBetween.js b/tests/utils/getPullRequestsMergedBetween.js deleted file mode 100755 index 6fcb96960fb0..000000000000 --- a/tests/utils/getPullRequestsMergedBetween.js +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env node - -const GitUtils = require('../../.github/libs/GitUtils'); - -/* eslint-disable no-console */ -const realConsoleLog = console.log; -console.log = () => {}; - -const output = GitUtils.getPullRequestsMergedBetween(process.argv[2], process.argv[3]); - -console.log = realConsoleLog; -console.log(output); diff --git a/tests/utils/getPullRequestsMergedBetween.mjs b/tests/utils/getPullRequestsMergedBetween.mjs new file mode 100755 index 000000000000..05dedaa3e4f1 --- /dev/null +++ b/tests/utils/getPullRequestsMergedBetween.mjs @@ -0,0 +1,15 @@ +#!/usr/bin/env node + +import GitUtils from '../../.github/libs/GitUtils.js'; + +const fromRef = process.argv[2]; +const toRef = process.argv[3]; + +/* eslint-disable no-console */ +const realConsoleLog = console.log; +console.log = () => {}; + +const output = await GitUtils.getPullRequestsMergedBetween(fromRef, toRef); + +console.log = realConsoleLog; +console.log(output);