From 5737caaca88f7c4b74bd8e482e73d8975d7507e7 Mon Sep 17 00:00:00 2001 From: Andrew Gable Date: Thu, 3 Apr 2025 14:35:50 -0600 Subject: [PATCH 01/22] Rename to cherryPickPRToStaging --- tests/unit/CIGitLogicTest.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/unit/CIGitLogicTest.ts b/tests/unit/CIGitLogicTest.ts index 50471b76318a..285816971767 100644 --- a/tests/unit/CIGitLogicTest.ts +++ b/tests/unit/CIGitLogicTest.ts @@ -170,7 +170,7 @@ function mergePR(num: number) { Log.success(`Merged PR #${num} to main`); } -function cherryPickPR(num: number, resolveVersionBumpConflicts: () => void = () => {}, resolveMergeCommitConflicts: () => void = () => {}) { +function cherryPickPRToStaging(num: number, resolveVersionBumpConflicts: () => void = () => {}, resolveMergeCommitConflicts: () => void = () => {}) { Log.info(`Cherry-picking PR ${num} to staging...`); mergePR(num); const prMergeCommit = execSync('git rev-parse HEAD', {encoding: 'utf-8'}).trim(); @@ -300,7 +300,7 @@ describe('CIGitLogic', () => { test('Merge a pull request with the checklist locked and CP it to staging', async () => { createBasicPR(3); - cherryPickPR(3); + cherryPickPRToStaging(3); // Verify output for checklist await assertPRsMergedBetween('1.0.0-0', '1.0.0-2', [1, 3]); @@ -389,7 +389,7 @@ Appended content console.log('RORY_DEBUG AFTER:', fs.readFileSync('myFile.txt', {encoding: 'utf8'})); exec('git add myFile.txt'); exec('git commit -m "Revert append and prepend"'); - cherryPickPR(9); + cherryPickPRToStaging(9); Log.info('Verifying that the revert is present on staging, but the unrelated change is not'); expect(fs.readFileSync('myFile.txt', {encoding: 'utf8'})).toBe(initialFileContent); @@ -479,12 +479,12 @@ Appended content Log.success('Created manual version bump in PR #14 in branch pr-14'); const packageJSONBefore = fs.readFileSync('package.json', {encoding: 'utf-8'}); - cherryPickPR( + cherryPickPRToStaging( 14, () => { fs.writeFileSync('package.json', packageJSONBefore); exec('git add package.json'); - exec('git cherry-pick --continue'); + exec('git cherry-pick --no-edit --continue'); }, () => { exec('git commit --no-edit --allow-empty'); From 32724addc1767f7c4c47a625b073e66a4fa07c6c Mon Sep 17 00:00:00 2001 From: Andrew Gable Date: Fri, 4 Apr 2025 10:11:17 -0600 Subject: [PATCH 02/22] Add V1 for CP to prod tests --- .github/libs/GitUtils.ts | 2 +- tests/unit/CIGitLogicTest.ts | 69 ++++++++++++++++++++++++++++++++++-- 2 files changed, 67 insertions(+), 4 deletions(-) diff --git a/.github/libs/GitUtils.ts b/.github/libs/GitUtils.ts index 89950613bc2e..40e49eecf167 100644 --- a/.github/libs/GitUtils.ts +++ b/.github/libs/GitUtils.ts @@ -164,7 +164,7 @@ function getValidMergedPRs(commits: CommitType[]): number[] { return; } - const match = commit.subject.match(/Merge pull request #(\d+) from (?!Expensify\/.*-cherry-pick-staging)/); + const match = commit.subject.match(/Merge pull request #(\d+) from (?!Expensify\/.*-cherry-pick-(staging|production))/); if (!Array.isArray(match) || match.length < 2) { return; } diff --git a/tests/unit/CIGitLogicTest.ts b/tests/unit/CIGitLogicTest.ts index 285816971767..58eda6613018 100644 --- a/tests/unit/CIGitLogicTest.ts +++ b/tests/unit/CIGitLogicTest.ts @@ -77,7 +77,7 @@ function initGitServer() { exec('git commit -m "Initial commit"'); exec('git switch -c staging'); exec(`git tag ${getVersion()}`); - exec('git branch production'); + exec('git switch -c production'); exec('git config --local receive.denyCurrentBranch ignore'); Log.success(`Initialized git server in ${GIT_REMOTE}`); } @@ -210,6 +210,46 @@ function cherryPickPRToStaging(num: number, resolveVersionBumpConflicts: () => v Log.success(`Successfully cherry-picked PR #${num} to staging!`); } +function cherryPickPRToProduction(num: number, resolveVersionBumpConflicts: () => void = () => {}, resolveMergeCommitConflicts: () => void = () => {}) { + Log.info(`Cherry-picking PR ${num} to production...`); + mergePR(num); + const prMergeCommit = execSync('git rev-parse HEAD', {encoding: 'utf-8'}).trim(); + bumpVersion(VersionUpdater.SEMANTIC_VERSION_LEVELS.BUILD); + const versionBumpCommit = execSync('git rev-parse HEAD', {encoding: 'utf-8'}).trim(); + checkoutRepo(); + setupGitAsOSBotify(); + + mockGetInput.mockReturnValue(VersionUpdater.SEMANTIC_VERSION_LEVELS.PATCH); + const previousPatchVersion = getPreviousVersion(); + exec(`git fetch origin main production --no-tags --shallow-exclude="${previousPatchVersion}"`); + + exec('git switch production'); + exec('git switch -c cherry-pick-production'); + + try { + exec(`git cherry-pick -x --mainline 1 ${versionBumpCommit}`); + } catch (e) { + resolveVersionBumpConflicts(); + } + + setupGitAsHuman(); + + try { + exec(`git cherry-pick -x --mainline 1 --strategy=recursive -Xtheirs ${prMergeCommit}`); + } catch (e) { + resolveMergeCommitConflicts(); + } + + setupGitAsOSBotify(); + exec('git switch production'); + exec(`git merge cherry-pick-production --no-ff -m "Merge pull request #${num + 1} from Expensify/cherry-pick-production"`); + exec('git branch -d cherry-pick-production'); + exec('git push origin production'); + Log.info(`Merged PR #${num + 1} into production`); + tagProduction(); + Log.success(`Successfully cherry-picked PR #${num} to production!`); +} + function tagStaging() { Log.info('Tagging new version from the staging branch...'); checkoutRepo(); @@ -225,6 +265,22 @@ function tagStaging() { Log.success(`Created new tag ${getVersion()}`); } +function tagProduction() { + Log.info('Tagging new version from the production branch...'); + checkoutRepo(); + setupGitAsOSBotify(); + try { + execSync('git rev-parse --verify production', {stdio: 'ignore'}); + } catch (e) { + exec('git fetch origin production --depth=1'); + } + exec('git switch production'); + exec(`git tag ${getVersion()}`); + exec('git push --tags'); + Log.success(`Created new tag ${getVersion()}`); +} + + function deployStaging() { Log.info('Deploying staging...'); checkoutRepo(); @@ -270,6 +326,12 @@ describe('CIGitLogic', () => { beforeAll(() => { Log.info('Starting setup'); startingDir = process.cwd(); + + if (fs.existsSync(startingDir)) { + Log.warn(`Found existing directory at ${startingDir}, deleting it to simulate a fresh test instance...`); + fs.rmSync(startingDir, {recursive: true}); + } + initGitServer(); checkoutRepo(); Log.success('Setup complete!'); @@ -300,7 +362,7 @@ describe('CIGitLogic', () => { test('Merge a pull request with the checklist locked and CP it to staging', async () => { createBasicPR(3); - cherryPickPRToStaging(3); + cherryPickPRToProduction(3); // Verify output for checklist await assertPRsMergedBetween('1.0.0-0', '1.0.0-2', [1, 3]); @@ -308,7 +370,7 @@ describe('CIGitLogic', () => { // Verify output for deploy comment await assertPRsMergedBetween('1.0.0-1', '1.0.0-2', [3]); }); - +/** test('Close the checklist', async () => { deployProduction(); @@ -497,4 +559,5 @@ Appended content // Verify PRs for the deploy checklist await assertPRsMergedBetween('1.0.3-0', '7.0.0-0', [13, 14]); }); + */ }); From b76fc1e1369c8774869dd1704ab5b72f8b08ddc3 Mon Sep 17 00:00:00 2001 From: Andrew Gable Date: Fri, 4 Apr 2025 10:11:34 -0600 Subject: [PATCH 03/22] Remove bad code --- tests/unit/CIGitLogicTest.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/unit/CIGitLogicTest.ts b/tests/unit/CIGitLogicTest.ts index 58eda6613018..5c4979cf2d5a 100644 --- a/tests/unit/CIGitLogicTest.ts +++ b/tests/unit/CIGitLogicTest.ts @@ -326,12 +326,6 @@ describe('CIGitLogic', () => { beforeAll(() => { Log.info('Starting setup'); startingDir = process.cwd(); - - if (fs.existsSync(startingDir)) { - Log.warn(`Found existing directory at ${startingDir}, deleting it to simulate a fresh test instance...`); - fs.rmSync(startingDir, {recursive: true}); - } - initGitServer(); checkoutRepo(); Log.success('Setup complete!'); From fc99fd559d373301e9ce2fcc591a194c529be1a3 Mon Sep 17 00:00:00 2001 From: Andrew Gable Date: Fri, 4 Apr 2025 11:38:56 -0600 Subject: [PATCH 04/22] Fix tests, remove some others --- tests/unit/CIGitLogicTest.ts | 196 +---------------------------------- 1 file changed, 4 insertions(+), 192 deletions(-) diff --git a/tests/unit/CIGitLogicTest.ts b/tests/unit/CIGitLogicTest.ts index 5c4979cf2d5a..1b0fa7060de1 100644 --- a/tests/unit/CIGitLogicTest.ts +++ b/tests/unit/CIGitLogicTest.ts @@ -64,6 +64,7 @@ function getVersion(): string { function initGitServer() { Log.info('Initializing git server...'); if (fs.existsSync(GIT_REMOTE)) { + Log.info(`${GIT_REMOTE} exists, remove it now...`); fs.rmSync(GIT_REMOTE, {recursive: true}); } fs.mkdirSync(GIT_REMOTE, {recursive: true}); @@ -77,7 +78,7 @@ function initGitServer() { exec('git commit -m "Initial commit"'); exec('git switch -c staging'); exec(`git tag ${getVersion()}`); - exec('git switch -c production'); + exec('git branch production'); exec('git config --local receive.denyCurrentBranch ignore'); Log.success(`Initialized git server in ${GIT_REMOTE}`); } @@ -354,7 +355,8 @@ describe('CIGitLogic', () => { mergePR(2); }); - test('Merge a pull request with the checklist locked and CP it to staging', async () => { + test('Merge a pull request with the checklist locked and CP it', async () => { + updateProductionFromStaging(); createBasicPR(3); cherryPickPRToProduction(3); @@ -364,194 +366,4 @@ describe('CIGitLogic', () => { // Verify output for deploy comment await assertPRsMergedBetween('1.0.0-1', '1.0.0-2', [3]); }); -/** - test('Close the checklist', async () => { - deployProduction(); - - // Verify output for release body and production deploy comments - await assertPRsMergedBetween('1.0.0-0', '1.0.0-2', [1, 3]); - - // Verify output for new checklist and staging deploy comments - await assertPRsMergedBetween('1.0.0-2', '1.0.1-0', [2]); - }); - - test('Merging another pull request when the checklist is unlocked', async () => { - createBasicPR(5); - mergePR(5); - deployStaging(); - - // Verify output for checklist - await assertPRsMergedBetween('1.0.0-2', '1.0.1-1', [2, 5]); - - // Verify output for deploy comment - await assertPRsMergedBetween('1.0.1-0', '1.0.1-1', [5]); - }); - - test('Deploying a PR, then CPing a revert, then adding the same code back again before the next production deploy results in the correct code on staging and production', async () => { - Log.info('Creating myFile.txt in PR #6'); - setupGitAsHuman(); - exec('git switch main'); - exec('git switch -c pr-6'); - const initialFileContent = 'Changes from PR #6'; - fs.appendFileSync('myFile.txt', 'Changes from PR #6'); - exec('git add myFile.txt'); - exec('git commit -m "Add myFile.txt in PR #6"'); - - mergePR(6); - deployStaging(); - - // Verify output for checklist - await assertPRsMergedBetween('1.0.0-2', '1.0.1-2', [2, 5, 6]); - - // Verify output for deploy comment - await assertPRsMergedBetween('1.0.1-1', '1.0.1-2', [6]); - - Log.info('Appending and prepending content to myFile.txt in PR #7'); - setupGitAsHuman(); - exec('git switch main'); - exec('git switch -c pr-7'); - const newFileContent = ` -Prepended content -${initialFileContent} -Appended content -`; - fs.writeFileSync('myFile.txt', newFileContent, {encoding: 'utf-8'}); - exec('git add myFile.txt'); - exec('git commit -m "Append and prepend content in myFile.txt"'); - mergePR(7); - deployStaging(); - - // Verify output for checklist - await assertPRsMergedBetween('1.0.0-2', '1.0.1-3', [2, 5, 6, 7]); - - // Verify output for deploy comment - await assertPRsMergedBetween('1.0.1-2', '1.0.1-3', [7]); - - Log.info('Making an unrelated change in PR #8'); - setupGitAsHuman(); - exec('git switch main'); - exec('git switch -c pr-8'); - fs.appendFileSync('anotherFile.txt', 'some content'); - exec('git add anotherFile.txt'); - exec('git commit -m "Create another file"'); - mergePR(8); - - Log.info('Reverting the append + prepend on main in PR #9'); - setupGitAsHuman(); - exec('git switch main'); - exec('git switch -c pr-9'); - console.log('RORY_DEBUG BEFORE:', fs.readFileSync('myFile.txt', {encoding: 'utf8'})); - fs.writeFileSync('myFile.txt', initialFileContent); - console.log('RORY_DEBUG AFTER:', fs.readFileSync('myFile.txt', {encoding: 'utf8'})); - exec('git add myFile.txt'); - exec('git commit -m "Revert append and prepend"'); - cherryPickPRToStaging(9); - - Log.info('Verifying that the revert is present on staging, but the unrelated change is not'); - expect(fs.readFileSync('myFile.txt', {encoding: 'utf8'})).toBe(initialFileContent); - expect(fs.existsSync('anotherFile.txt')).toBe(false); - - Log.info('Repeating previously reverted append + prepend on main in PR #10'); - setupGitAsHuman(); - exec('git switch main'); - exec('git switch -c pr-10'); - fs.writeFileSync('myFile.txt', newFileContent, {encoding: 'utf-8'}); - exec('git add myFile.txt'); - exec('git commit -m "Append and prepend content in myFile.txt"'); - - mergePR(10); - deployProduction(); - - // Verify production release list - await assertPRsMergedBetween('1.0.0-2', '1.0.1-4', [2, 5, 6, 7, 9]); - - // Verify PR list for the new checklist - await assertPRsMergedBetween('1.0.1-4', '1.0.2-0', [8, 10]); - }); - - test('Force-pushing to a branch after rebasing older commits', async () => { - createBasicPR(11); - exec('git push origin pr-11'); - createBasicPR(12); - mergePR(12); - deployStaging(); - - // Verify PRs for checklist - await assertPRsMergedBetween('1.0.1-4', '1.0.2-1', [8, 10, 12]); - - // Verify PRs for deploy comments - await assertPRsMergedBetween('1.0.2-0', '1.0.2-1', [12]); - - checkoutRepo(); - setupGitAsHuman(); - exec('git fetch origin pr-11'); - exec('git switch pr-11'); - exec('git rebase main -Xours'); - exec('git push --force origin pr-11'); - mergePR(11); - - deployProduction(); - - // Verify PRs for deploy comments / release - await assertPRsMergedBetween('1.0.1-4', '1.0.2-1', [8, 10, 12]); - - // Verify PRs for new checklist - await assertPRsMergedBetween('1.0.2-1', '1.0.3-0', [11]); - }); - - test('Manual version bump', async () => { - Log.info('Creating manual version bump in PR #13'); - checkoutRepo(); - setupGitAsHuman(); - exec('git pull'); - exec('git switch -c "pr-13"'); - for (let i = 0; i < 3; i++) { - exec(`npm --no-git-tag-version version ${VersionUpdater.incrementVersion(getVersion(), VersionUpdater.SEMANTIC_VERSION_LEVELS.MAJOR)}`); - } - exec('git add package.json'); - exec(`git commit -m "Manually bump version to ${getVersion()} in PR #13"`); - Log.success('Created manual version bump in PR #13 in branch pr-13'); - - mergePR(13); - Log.info('Deploying staging...'); - checkoutRepo(); - updateStagingFromMain(); - tagStaging(); - Log.success(`Deployed v${getVersion()} to staging!`); - - // Verify PRs for deploy comments / release and new checklist - await assertPRsMergedBetween('1.0.3-0', '4.0.0-0', [13]); - - Log.info('Creating manual version bump in PR #14'); - checkoutRepo(); - setupGitAsHuman(); - exec('git pull'); - exec('git switch -c "pr-14"'); - for (let i = 0; i < 3; i++) { - exec(`npm --no-git-tag-version version ${VersionUpdater.incrementVersion(getVersion(), VersionUpdater.SEMANTIC_VERSION_LEVELS.MAJOR)}`); - } - exec('git add package.json'); - exec(`git commit -m "Manually bump version to ${getVersion()} in PR #14"`); - Log.success('Created manual version bump in PR #14 in branch pr-14'); - - const packageJSONBefore = fs.readFileSync('package.json', {encoding: 'utf-8'}); - cherryPickPRToStaging( - 14, - () => { - fs.writeFileSync('package.json', packageJSONBefore); - exec('git add package.json'); - exec('git cherry-pick --no-edit --continue'); - }, - () => { - exec('git commit --no-edit --allow-empty'); - }, - ); - - // Verify PRs for deploy comments - await assertPRsMergedBetween('4.0.0-0', '7.0.0-0', [14]); - - // Verify PRs for the deploy checklist - await assertPRsMergedBetween('1.0.3-0', '7.0.0-0', [13, 14]); - }); - */ }); From 9bc4729bf3cf111f0f8fb7de570ca61763189239 Mon Sep 17 00:00:00 2001 From: Andrew Gable Date: Fri, 4 Apr 2025 12:00:26 -0600 Subject: [PATCH 05/22] Add back the tests --- tests/unit/CIGitLogicTest.ts | 206 ++++++++++++++++++++++++++++++++++- 1 file changed, 203 insertions(+), 3 deletions(-) diff --git a/tests/unit/CIGitLogicTest.ts b/tests/unit/CIGitLogicTest.ts index 1b0fa7060de1..a977719b5e32 100644 --- a/tests/unit/CIGitLogicTest.ts +++ b/tests/unit/CIGitLogicTest.ts @@ -355,10 +355,9 @@ describe('CIGitLogic', () => { mergePR(2); }); - test('Merge a pull request with the checklist locked and CP it', async () => { - updateProductionFromStaging(); + test('Merge a pull request with the checklist locked and CP it to staging', async () => { createBasicPR(3); - cherryPickPRToProduction(3); + cherryPickPRToStaging(3); // Verify output for checklist await assertPRsMergedBetween('1.0.0-0', '1.0.0-2', [1, 3]); @@ -366,4 +365,205 @@ describe('CIGitLogic', () => { // Verify output for deploy comment await assertPRsMergedBetween('1.0.0-1', '1.0.0-2', [3]); }); + + test('Merge a pull request with the checklist locked and CP it to production', async () => { + updateProductionFromStaging(); + createBasicPR(4); + cherryPickPRToProduction(4); + + // Verify output for checklist + await assertPRsMergedBetween('1.0.0-0', '1.0.0-3', [1, 3, 4]); + + // Verify output for deploy comment + await assertPRsMergedBetween('1.0.0-1', '1.0.0-3', [4]); + }); + + test('Close the checklist', async () => { + deployProduction(); + + // Verify output for release body and production deploy comments + await assertPRsMergedBetween('1.0.0-0', '1.0.0-2', [1, 3]); + + // Verify output for new checklist and staging deploy comments + await assertPRsMergedBetween('1.0.0-2', '1.0.1-0', [2]); + }); + + test('Merging another pull request when the checklist is unlocked', async () => { + createBasicPR(5); + mergePR(5); + deployStaging(); + + // Verify output for checklist + await assertPRsMergedBetween('1.0.0-2', '1.0.1-1', [2, 5]); + + // Verify output for deploy comment + await assertPRsMergedBetween('1.0.1-0', '1.0.1-1', [5]); + }); + + test('Deploying a PR, then CPing a revert, then adding the same code back again before the next production deploy results in the correct code on staging and production', async () => { + Log.info('Creating myFile.txt in PR #6'); + setupGitAsHuman(); + exec('git switch main'); + exec('git switch -c pr-6'); + const initialFileContent = 'Changes from PR #6'; + fs.appendFileSync('myFile.txt', 'Changes from PR #6'); + exec('git add myFile.txt'); + exec('git commit -m "Add myFile.txt in PR #6"'); + + mergePR(6); + deployStaging(); + + // Verify output for checklist + await assertPRsMergedBetween('1.0.0-2', '1.0.1-2', [2, 5, 6]); + + // Verify output for deploy comment + await assertPRsMergedBetween('1.0.1-1', '1.0.1-2', [6]); + + Log.info('Appending and prepending content to myFile.txt in PR #7'); + setupGitAsHuman(); + exec('git switch main'); + exec('git switch -c pr-7'); + const newFileContent = ` +Prepended content +${initialFileContent} +Appended content +`; + fs.writeFileSync('myFile.txt', newFileContent, {encoding: 'utf-8'}); + exec('git add myFile.txt'); + exec('git commit -m "Append and prepend content in myFile.txt"'); + mergePR(7); + deployStaging(); + + // Verify output for checklist + await assertPRsMergedBetween('1.0.0-2', '1.0.1-3', [2, 5, 6, 7]); + + // Verify output for deploy comment + await assertPRsMergedBetween('1.0.1-2', '1.0.1-3', [7]); + + Log.info('Making an unrelated change in PR #8'); + setupGitAsHuman(); + exec('git switch main'); + exec('git switch -c pr-8'); + fs.appendFileSync('anotherFile.txt', 'some content'); + exec('git add anotherFile.txt'); + exec('git commit -m "Create another file"'); + mergePR(8); + + Log.info('Reverting the append + prepend on main in PR #9'); + setupGitAsHuman(); + exec('git switch main'); + exec('git switch -c pr-9'); + console.log('RORY_DEBUG BEFORE:', fs.readFileSync('myFile.txt', {encoding: 'utf8'})); + fs.writeFileSync('myFile.txt', initialFileContent); + console.log('RORY_DEBUG AFTER:', fs.readFileSync('myFile.txt', {encoding: 'utf8'})); + exec('git add myFile.txt'); + exec('git commit -m "Revert append and prepend"'); + cherryPickPRToStaging(9); + + Log.info('Verifying that the revert is present on staging, but the unrelated change is not'); + expect(fs.readFileSync('myFile.txt', {encoding: 'utf8'})).toBe(initialFileContent); + expect(fs.existsSync('anotherFile.txt')).toBe(false); + + Log.info('Repeating previously reverted append + prepend on main in PR #10'); + setupGitAsHuman(); + exec('git switch main'); + exec('git switch -c pr-10'); + fs.writeFileSync('myFile.txt', newFileContent, {encoding: 'utf-8'}); + exec('git add myFile.txt'); + exec('git commit -m "Append and prepend content in myFile.txt"'); + + mergePR(10); + deployProduction(); + + // Verify production release list + await assertPRsMergedBetween('1.0.0-2', '1.0.1-4', [2, 5, 6, 7, 9]); + + // Verify PR list for the new checklist + await assertPRsMergedBetween('1.0.1-4', '1.0.2-0', [8, 10]); + }); + + test('Force-pushing to a branch after rebasing older commits', async () => { + createBasicPR(11); + exec('git push origin pr-11'); + createBasicPR(12); + mergePR(12); + deployStaging(); + + // Verify PRs for checklist + await assertPRsMergedBetween('1.0.1-4', '1.0.2-1', [8, 10, 12]); + + // Verify PRs for deploy comments + await assertPRsMergedBetween('1.0.2-0', '1.0.2-1', [12]); + + checkoutRepo(); + setupGitAsHuman(); + exec('git fetch origin pr-11'); + exec('git switch pr-11'); + exec('git rebase main -Xours'); + exec('git push --force origin pr-11'); + mergePR(11); + + deployProduction(); + + // Verify PRs for deploy comments / release + await assertPRsMergedBetween('1.0.1-4', '1.0.2-1', [8, 10, 12]); + + // Verify PRs for new checklist + await assertPRsMergedBetween('1.0.2-1', '1.0.3-0', [11]); + }); + + test('Manual version bump', async () => { + Log.info('Creating manual version bump in PR #13'); + checkoutRepo(); + setupGitAsHuman(); + exec('git pull'); + exec('git switch -c "pr-13"'); + for (let i = 0; i < 3; i++) { + exec(`npm --no-git-tag-version version ${VersionUpdater.incrementVersion(getVersion(), VersionUpdater.SEMANTIC_VERSION_LEVELS.MAJOR)}`); + } + exec('git add package.json'); + exec(`git commit -m "Manually bump version to ${getVersion()} in PR #13"`); + Log.success('Created manual version bump in PR #13 in branch pr-13'); + + mergePR(13); + Log.info('Deploying staging...'); + checkoutRepo(); + updateStagingFromMain(); + tagStaging(); + Log.success(`Deployed v${getVersion()} to staging!`); + + // Verify PRs for deploy comments / release and new checklist + await assertPRsMergedBetween('1.0.3-0', '4.0.0-0', [13]); + + Log.info('Creating manual version bump in PR #14'); + checkoutRepo(); + setupGitAsHuman(); + exec('git pull'); + exec('git switch -c "pr-14"'); + for (let i = 0; i < 3; i++) { + exec(`npm --no-git-tag-version version ${VersionUpdater.incrementVersion(getVersion(), VersionUpdater.SEMANTIC_VERSION_LEVELS.MAJOR)}`); + } + exec('git add package.json'); + exec(`git commit -m "Manually bump version to ${getVersion()} in PR #14"`); + Log.success('Created manual version bump in PR #14 in branch pr-14'); + + const packageJSONBefore = fs.readFileSync('package.json', {encoding: 'utf-8'}); + cherryPickPRToStaging( + 14, + () => { + fs.writeFileSync('package.json', packageJSONBefore); + exec('git add package.json'); + exec('git cherry-pick --no-edit --continue'); + }, + () => { + exec('git commit --no-edit --allow-empty'); + }, + ); + + // Verify PRs for deploy comments + await assertPRsMergedBetween('4.0.0-0', '7.0.0-0', [14]); + + // Verify PRs for the deploy checklist + await assertPRsMergedBetween('1.0.3-0', '7.0.0-0', [13, 14]); + }); }); From fe1856a6b78706b7e226cd73b062657bf7f422a3 Mon Sep 17 00:00:00 2001 From: Andrew Gable Date: Fri, 4 Apr 2025 12:54:10 -0600 Subject: [PATCH 06/22] Fix tests --- tests/unit/CIGitLogicTest.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/unit/CIGitLogicTest.ts b/tests/unit/CIGitLogicTest.ts index a977719b5e32..1a645e8dd34e 100644 --- a/tests/unit/CIGitLogicTest.ts +++ b/tests/unit/CIGitLogicTest.ts @@ -375,7 +375,7 @@ describe('CIGitLogic', () => { await assertPRsMergedBetween('1.0.0-0', '1.0.0-3', [1, 3, 4]); // Verify output for deploy comment - await assertPRsMergedBetween('1.0.0-1', '1.0.0-3', [4]); + await assertPRsMergedBetween('1.0.0-2', '1.0.0-3', [4]); }); test('Close the checklist', async () => { @@ -385,7 +385,7 @@ describe('CIGitLogic', () => { await assertPRsMergedBetween('1.0.0-0', '1.0.0-2', [1, 3]); // Verify output for new checklist and staging deploy comments - await assertPRsMergedBetween('1.0.0-2', '1.0.1-0', [2]); + await assertPRsMergedBetween('1.0.0-2', '1.0.1-0', [2, 4]); }); test('Merging another pull request when the checklist is unlocked', async () => { @@ -394,7 +394,7 @@ describe('CIGitLogic', () => { deployStaging(); // Verify output for checklist - await assertPRsMergedBetween('1.0.0-2', '1.0.1-1', [2, 5]); + await assertPRsMergedBetween('1.0.0-2', '1.0.1-1', [2, 4, 5]); // Verify output for deploy comment await assertPRsMergedBetween('1.0.1-0', '1.0.1-1', [5]); @@ -414,7 +414,7 @@ describe('CIGitLogic', () => { deployStaging(); // Verify output for checklist - await assertPRsMergedBetween('1.0.0-2', '1.0.1-2', [2, 5, 6]); + await assertPRsMergedBetween('1.0.0-2', '1.0.1-2', [2, 4, 5, 6]); // Verify output for deploy comment await assertPRsMergedBetween('1.0.1-1', '1.0.1-2', [6]); @@ -435,7 +435,7 @@ Appended content deployStaging(); // Verify output for checklist - await assertPRsMergedBetween('1.0.0-2', '1.0.1-3', [2, 5, 6, 7]); + await assertPRsMergedBetween('1.0.0-2', '1.0.1-3', [2, 4, 5, 6, 7]); // Verify output for deploy comment await assertPRsMergedBetween('1.0.1-2', '1.0.1-3', [7]); From 2fd213774eb952bfbee0cc02174f56398372c512 Mon Sep 17 00:00:00 2001 From: Andrew Gable Date: Fri, 4 Apr 2025 14:02:03 -0600 Subject: [PATCH 07/22] Progress on the CP to production test --- tests/unit/CIGitLogicTest.ts | 48 +++++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/tests/unit/CIGitLogicTest.ts b/tests/unit/CIGitLogicTest.ts index 1a645e8dd34e..7cc131d1c15b 100644 --- a/tests/unit/CIGitLogicTest.ts +++ b/tests/unit/CIGitLogicTest.ts @@ -78,7 +78,8 @@ function initGitServer() { exec('git commit -m "Initial commit"'); exec('git switch -c staging'); exec(`git tag ${getVersion()}`); - exec('git branch production'); + exec('git switch -c production'); + exec(`git switch staging`); exec('git config --local receive.denyCurrentBranch ignore'); Log.success(`Initialized git server in ${GIT_REMOTE}`); } @@ -173,6 +174,7 @@ function mergePR(num: number) { function cherryPickPRToStaging(num: number, resolveVersionBumpConflicts: () => void = () => {}, resolveMergeCommitConflicts: () => void = () => {}) { Log.info(`Cherry-picking PR ${num} to staging...`); + // TODO: Move mergePR into the test itself mergePR(num); const prMergeCommit = execSync('git rev-parse HEAD', {encoding: 'utf-8'}).trim(); bumpVersion(VersionUpdater.SEMANTIC_VERSION_LEVELS.BUILD); @@ -182,6 +184,8 @@ function cherryPickPRToStaging(num: number, resolveVersionBumpConflicts: () => v mockGetInput.mockReturnValue(VersionUpdater.SEMANTIC_VERSION_LEVELS.PATCH); const previousPatchVersion = getPreviousVersion(); + + // --shallow-exclude is used to speed up the fetch exec(`git fetch origin main staging --no-tags --shallow-exclude="${previousPatchVersion}"`); exec('git switch staging'); @@ -193,11 +197,17 @@ function cherryPickPRToStaging(num: number, resolveVersionBumpConflicts: () => v resolveVersionBumpConflicts(); } + // TODO: This assumes that we have a conflict, we should not assume that setupGitAsHuman(); try { exec(`git cherry-pick -x --mainline 1 --strategy=recursive -Xtheirs ${prMergeCommit}`); } catch (e) { + // 1. Abort cherry-pick + // 2. Create the cherry-pick-staging branch + // 3. Run setupGitAsHuman() + // 4. Re-run the cherry pick git command (it will have conflicts again) + // 5. Catch the conflicts exception, run resolveMergeCommitConflicts() resolveMergeCommitConflicts(); } @@ -215,12 +225,12 @@ function cherryPickPRToProduction(num: number, resolveVersionBumpConflicts: () = Log.info(`Cherry-picking PR ${num} to production...`); mergePR(num); const prMergeCommit = execSync('git rev-parse HEAD', {encoding: 'utf-8'}).trim(); - bumpVersion(VersionUpdater.SEMANTIC_VERSION_LEVELS.BUILD); - const versionBumpCommit = execSync('git rev-parse HEAD', {encoding: 'utf-8'}).trim(); + bumpVersion(VersionUpdater.SEMANTIC_VERSION_LEVELS.PATCH); + let versionBumpCommit = execSync('git rev-parse HEAD', {encoding: 'utf-8'}).trim(); checkoutRepo(); setupGitAsOSBotify(); - mockGetInput.mockReturnValue(VersionUpdater.SEMANTIC_VERSION_LEVELS.PATCH); + mockGetInput.mockReturnValue(VersionUpdater.SEMANTIC_VERSION_LEVELS.MINOR); const previousPatchVersion = getPreviousVersion(); exec(`git fetch origin main production --no-tags --shallow-exclude="${previousPatchVersion}"`); @@ -228,7 +238,7 @@ function cherryPickPRToProduction(num: number, resolveVersionBumpConflicts: () = exec('git switch -c cherry-pick-production'); try { - exec(`git cherry-pick -x --mainline 1 ${versionBumpCommit}`); + exec(`git cherry-pick -x --mainline 1 -Xtheirs ${versionBumpCommit}`); } catch (e) { resolveVersionBumpConflicts(); } @@ -248,6 +258,15 @@ function cherryPickPRToProduction(num: number, resolveVersionBumpConflicts: () = exec('git push origin production'); Log.info(`Merged PR #${num + 1} into production`); tagProduction(); + + bumpVersion(VersionUpdater.SEMANTIC_VERSION_LEVELS.BUILD); + versionBumpCommit = execSync('git rev-parse HEAD', {encoding: 'utf-8'}).trim(); + exec(`git fetch origin staging --depth=1`) + exec(`git switch staging`) + exec(`git cherry-pick -x --mainline 1 -Xtheirs ${versionBumpCommit}`) + exec('git push origin staging'); + Log.success(`Pushed to staging after CP to production.`); + Log.success(`Successfully cherry-picked PR #${num} to production!`); } @@ -350,9 +369,12 @@ describe('CIGitLogic', () => { await assertPRsMergedBetween('1.0.0-0', '1.0.0-1', [1]); }); - test("Merge a pull request with the checklist locked, but don't CP it", () => { + test("Merge a pull request with the checklist locked, but don't CP it", async () => { createBasicPR(2); mergePR(2); + + // Verify output for checklist and deploy comment, and make sure PR #2 is not on staging + await assertPRsMergedBetween('1.0.0-0', '1.0.0-1', [1]); }); test('Merge a pull request with the checklist locked and CP it to staging', async () => { @@ -362,22 +384,25 @@ describe('CIGitLogic', () => { // Verify output for checklist await assertPRsMergedBetween('1.0.0-0', '1.0.0-2', [1, 3]); - // Verify output for deploy comment + // Verify output for deploy comment, and make sure PR #2 is not on staging await assertPRsMergedBetween('1.0.0-1', '1.0.0-2', [3]); }); test('Merge a pull request with the checklist locked and CP it to production', async () => { - updateProductionFromStaging(); + // updateProductionFromStaging(); createBasicPR(4); cherryPickPRToProduction(4); + // Figure out how to adjust to not have this hacky fetch + exec(`git fetch`); + // Verify output for checklist - await assertPRsMergedBetween('1.0.0-0', '1.0.0-3', [1, 3, 4]); + await assertPRsMergedBetween('1.0.0-0', '1.0.1-1', [1, 3, 4]); // Verify output for deploy comment - await assertPRsMergedBetween('1.0.0-2', '1.0.0-3', [4]); + await assertPRsMergedBetween('1.0.0-0', '1.0.1-0', [4]); }); - +/* test('Close the checklist', async () => { deployProduction(); @@ -566,4 +591,5 @@ Appended content // Verify PRs for the deploy checklist await assertPRsMergedBetween('1.0.3-0', '7.0.0-0', [13, 14]); }); + */ }); From 129ea92f91720430f48365e282ce38b9912db591 Mon Sep 17 00:00:00 2001 From: Andrew Gable Date: Fri, 4 Apr 2025 15:06:41 -0600 Subject: [PATCH 08/22] Get closer to having tests working totally --- .github/libs/GitUtils.ts | 15 ++++++-- tests/unit/CIGitLogicTest.ts | 73 +++++++++++++++++++++--------------- 2 files changed, 54 insertions(+), 34 deletions(-) diff --git a/.github/libs/GitUtils.ts b/.github/libs/GitUtils.ts index 40e49eecf167..f31712b54710 100644 --- a/.github/libs/GitUtils.ts +++ b/.github/libs/GitUtils.ts @@ -113,9 +113,18 @@ function fetchTag(tag: string, shallowExcludeTag = '') { */ function getCommitHistoryAsJSON(fromTag: string, toTag: string): Promise { // Fetch tags, excluding commits reachable from the previous patch version (i.e: previous checklist), so that we don't have to fetch the full history - const previousPatchVersion = getPreviousExistingTag(fromTag, VersionUpdater.SEMANTIC_VERSION_LEVELS.PATCH); - fetchTag(fromTag, previousPatchVersion); - fetchTag(toTag, previousPatchVersion); + // TODO: Add check for if tag is off by at least a minor, fetch the minor + + // const previousPatchVersion = getPreviousExistingTag(fromTag, VersionUpdater.SEMANTIC_VERSION_LEVELS.MINOR); + // fetchTag(fromTag, `${fromTag}~1`); + // fetchTag(toTag, `${fromTag}~1`); + + // execSync(`git fetch origin tag --no-tags ${fromTag}`); + // execSync(`git fetch origin tag --no-tags ${toTag}`); + // const hashForFromTag = execSync(`git rev-parse ${fromTag}`).toString().trim(); + + // TODO: Make this fast 🚀 + execSync(`git fetch --tags`); console.log('Getting pull requests merged between the following tags:', fromTag, toTag); return new Promise((resolve, reject) => { diff --git a/tests/unit/CIGitLogicTest.ts b/tests/unit/CIGitLogicTest.ts index 7cc131d1c15b..c4f0eabb8522 100644 --- a/tests/unit/CIGitLogicTest.ts +++ b/tests/unit/CIGitLogicTest.ts @@ -14,7 +14,7 @@ import getPreviousVersion from '@github/actions/javascript/getPreviousVersion/ge import CONST from '@github/libs/CONST'; import GitUtils from '@github/libs/GitUtils'; import * as VersionUpdater from '@github/libs/versionUpdater'; -import type {SemverLevel} from '@github/libs/versionUpdater'; +import {SEMANTIC_VERSION_LEVELS, SemverLevel} from '@github/libs/versionUpdater'; import asMutable from '@src/types/utils/asMutable'; import * as Log from '../../scripts/utils/Logger'; @@ -77,8 +77,14 @@ function initGitServer() { exec('git add -A'); exec('git commit -m "Initial commit"'); exec('git switch -c staging'); - exec(`git tag ${getVersion()}`); exec('git switch -c production'); + + // Tag the production branch with 1.0.0.0 + exec(`git tag ${getVersion()}`); + + // Bump version to 2.0.0.0 + bumpVersion(VersionUpdater.SEMANTIC_VERSION_LEVELS.MAJOR, true) + exec(`git tag ${getVersion()}`) exec(`git switch staging`); exec('git config --local receive.denyCurrentBranch ignore'); Log.success(`Initialized git server in ${GIT_REMOTE}`); @@ -98,7 +104,7 @@ function checkoutRepo() { Log.success('Checked out repo at $DUMMY_DIR!'); } -function bumpVersion(level: SemverLevel) { +function bumpVersion(level: SemverLevel, isRemote = false) { Log.info('Bumping version...'); setupGitAsOSBotify(); exec('git switch main'); @@ -106,7 +112,9 @@ function bumpVersion(level: SemverLevel) { exec(`npm --no-git-tag-version version ${nextVersion}`); exec('git add package.json'); exec(`git commit -m "Update version to ${nextVersion}"`); - exec('git push origin main'); + if (!isRemote) { + exec('git push origin main'); + } Log.success(`Version bumped to ${nextVersion} on main`); } @@ -141,7 +149,11 @@ function updateProductionFromStaging() { } catch (e) {} exec('git switch -c production'); - exec('git push --force origin production'); + + exec(`git push origin --delete ${getVersion()}`); + exec(`git tag ${getVersion()}`); + + exec('git push --force --tags origin production'); Log.success('Recreated production from staging!'); } @@ -265,7 +277,8 @@ function cherryPickPRToProduction(num: number, resolveVersionBumpConflicts: () = exec(`git switch staging`) exec(`git cherry-pick -x --mainline 1 -Xtheirs ${versionBumpCommit}`) exec('git push origin staging'); - Log.success(`Pushed to staging after CP to production.`); + tagStaging(); + Log.success(`Pushed to staging after CP to production`); Log.success(`Successfully cherry-picked PR #${num} to production!`); } @@ -287,6 +300,7 @@ function tagStaging() { function tagProduction() { Log.info('Tagging new version from the production branch...'); + Log.info(`Version is: ${getVersion()}`); checkoutRepo(); setupGitAsOSBotify(); try { @@ -366,7 +380,7 @@ describe('CIGitLogic', () => { deployStaging(); // Verify output for checklist and deploy comment - await assertPRsMergedBetween('1.0.0-0', '1.0.0-1', [1]); + await assertPRsMergedBetween('2.0.0-0', '2.0.0-1', [1]); }); test("Merge a pull request with the checklist locked, but don't CP it", async () => { @@ -374,7 +388,7 @@ describe('CIGitLogic', () => { mergePR(2); // Verify output for checklist and deploy comment, and make sure PR #2 is not on staging - await assertPRsMergedBetween('1.0.0-0', '1.0.0-1', [1]); + await assertPRsMergedBetween('2.0.0-0', '2.0.0-1', [1]); }); test('Merge a pull request with the checklist locked and CP it to staging', async () => { @@ -382,10 +396,10 @@ describe('CIGitLogic', () => { cherryPickPRToStaging(3); // Verify output for checklist - await assertPRsMergedBetween('1.0.0-0', '1.0.0-2', [1, 3]); + await assertPRsMergedBetween('2.0.0-0', '2.0.0-2', [1, 3]); // Verify output for deploy comment, and make sure PR #2 is not on staging - await assertPRsMergedBetween('1.0.0-1', '1.0.0-2', [3]); + await assertPRsMergedBetween('2.0.0-1', '2.0.0-2', [3]); }); test('Merge a pull request with the checklist locked and CP it to production', async () => { @@ -393,24 +407,21 @@ describe('CIGitLogic', () => { createBasicPR(4); cherryPickPRToProduction(4); - // Figure out how to adjust to not have this hacky fetch - exec(`git fetch`); - // Verify output for checklist - await assertPRsMergedBetween('1.0.0-0', '1.0.1-1', [1, 3, 4]); + await assertPRsMergedBetween('2.0.0-0', '2.0.1-1', [1, 3]); // Verify output for deploy comment - await assertPRsMergedBetween('1.0.0-0', '1.0.1-0', [4]); + await assertPRsMergedBetween('2.0.0-0', '2.0.1-0', [4]); }); /* test('Close the checklist', async () => { deployProduction(); // Verify output for release body and production deploy comments - await assertPRsMergedBetween('1.0.0-0', '1.0.0-2', [1, 3]); + await assertPRsMergedBetween('2.0.0-0', '2.0.0-2', [1, 3]); // Verify output for new checklist and staging deploy comments - await assertPRsMergedBetween('1.0.0-2', '1.0.1-0', [2, 4]); + await assertPRsMergedBetween('2.0.0-2', '2.0.1-0', [2, 4]); }); test('Merging another pull request when the checklist is unlocked', async () => { @@ -419,10 +430,10 @@ describe('CIGitLogic', () => { deployStaging(); // Verify output for checklist - await assertPRsMergedBetween('1.0.0-2', '1.0.1-1', [2, 4, 5]); + await assertPRsMergedBetween('2.0.0-2', '2.0.1-1', [2, 4, 5]); // Verify output for deploy comment - await assertPRsMergedBetween('1.0.1-0', '1.0.1-1', [5]); + await assertPRsMergedBetween('2.0.1-0', '2.0.1-1', [5]); }); test('Deploying a PR, then CPing a revert, then adding the same code back again before the next production deploy results in the correct code on staging and production', async () => { @@ -439,10 +450,10 @@ describe('CIGitLogic', () => { deployStaging(); // Verify output for checklist - await assertPRsMergedBetween('1.0.0-2', '1.0.1-2', [2, 4, 5, 6]); + await assertPRsMergedBetween('2.0.0-2', '2.0.1-2', [2, 4, 5, 6]); // Verify output for deploy comment - await assertPRsMergedBetween('1.0.1-1', '1.0.1-2', [6]); + await assertPRsMergedBetween('2.0.1-1', '2.0.1-2', [6]); Log.info('Appending and prepending content to myFile.txt in PR #7'); setupGitAsHuman(); @@ -460,10 +471,10 @@ Appended content deployStaging(); // Verify output for checklist - await assertPRsMergedBetween('1.0.0-2', '1.0.1-3', [2, 4, 5, 6, 7]); + await assertPRsMergedBetween('2.0.0-2', '2.0.1-3', [2, 4, 5, 6, 7]); // Verify output for deploy comment - await assertPRsMergedBetween('1.0.1-2', '1.0.1-3', [7]); + await assertPRsMergedBetween('2.0.1-2', '2.0.1-3', [7]); Log.info('Making an unrelated change in PR #8'); setupGitAsHuman(); @@ -501,10 +512,10 @@ Appended content deployProduction(); // Verify production release list - await assertPRsMergedBetween('1.0.0-2', '1.0.1-4', [2, 5, 6, 7, 9]); + await assertPRsMergedBetween('2.0.0-2', '2.0.1-4', [2, 5, 6, 7, 9]); // Verify PR list for the new checklist - await assertPRsMergedBetween('1.0.1-4', '1.0.2-0', [8, 10]); + await assertPRsMergedBetween('2.0.1-4', '2.0.2-0', [8, 10]); }); test('Force-pushing to a branch after rebasing older commits', async () => { @@ -515,10 +526,10 @@ Appended content deployStaging(); // Verify PRs for checklist - await assertPRsMergedBetween('1.0.1-4', '1.0.2-1', [8, 10, 12]); + await assertPRsMergedBetween('2.0.1-4', '2.0.2-1', [8, 10, 12]); // Verify PRs for deploy comments - await assertPRsMergedBetween('1.0.2-0', '1.0.2-1', [12]); + await assertPRsMergedBetween('2.0.2-0', '2.0.2-1', [12]); checkoutRepo(); setupGitAsHuman(); @@ -531,10 +542,10 @@ Appended content deployProduction(); // Verify PRs for deploy comments / release - await assertPRsMergedBetween('1.0.1-4', '1.0.2-1', [8, 10, 12]); + await assertPRsMergedBetween('2.0.1-4', '2.0.2-1', [8, 10, 12]); // Verify PRs for new checklist - await assertPRsMergedBetween('1.0.2-1', '1.0.3-0', [11]); + await assertPRsMergedBetween('2.0.2-1', '2.0.3-0', [11]); }); test('Manual version bump', async () => { @@ -558,7 +569,7 @@ Appended content Log.success(`Deployed v${getVersion()} to staging!`); // Verify PRs for deploy comments / release and new checklist - await assertPRsMergedBetween('1.0.3-0', '4.0.0-0', [13]); + await assertPRsMergedBetween('2.0.3-0', '4.0.0-0', [13]); Log.info('Creating manual version bump in PR #14'); checkoutRepo(); @@ -589,7 +600,7 @@ Appended content await assertPRsMergedBetween('4.0.0-0', '7.0.0-0', [14]); // Verify PRs for the deploy checklist - await assertPRsMergedBetween('1.0.3-0', '7.0.0-0', [13, 14]); + await assertPRsMergedBetween('2.0.3-0', '7.0.0-0', [13, 14]); }); */ }); From cb08c84490cdd2b2814bf8cef5739744efca9cb3 Mon Sep 17 00:00:00 2001 From: Andrew Gable Date: Mon, 7 Apr 2025 13:43:37 -0600 Subject: [PATCH 09/22] Update tests to use `-version` postfix --- .github/libs/GitUtils.ts | 2 +- tests/unit/CIGitLogicTest.ts | 25 +++++++++++-------------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/.github/libs/GitUtils.ts b/.github/libs/GitUtils.ts index f31712b54710..daf4eba844fd 100644 --- a/.github/libs/GitUtils.ts +++ b/.github/libs/GitUtils.ts @@ -124,7 +124,7 @@ function getCommitHistoryAsJSON(fromTag: string, toTag: string): Promise((resolve, reject) => { diff --git a/tests/unit/CIGitLogicTest.ts b/tests/unit/CIGitLogicTest.ts index c4f0eabb8522..f1d785933df2 100644 --- a/tests/unit/CIGitLogicTest.ts +++ b/tests/unit/CIGitLogicTest.ts @@ -149,10 +149,7 @@ function updateProductionFromStaging() { } catch (e) {} exec('git switch -c production'); - - exec(`git push origin --delete ${getVersion()}`); exec(`git tag ${getVersion()}`); - exec('git push --force --tags origin production'); Log.success('Recreated production from staging!'); } @@ -293,7 +290,7 @@ function tagStaging() { exec('git fetch origin staging --depth=1'); } exec('git switch staging'); - exec(`git tag ${getVersion()}`); + exec(`git tag ${getVersion()}-staging`); exec('git push --tags'); Log.success(`Created new tag ${getVersion()}`); } @@ -380,7 +377,7 @@ describe('CIGitLogic', () => { deployStaging(); // Verify output for checklist and deploy comment - await assertPRsMergedBetween('2.0.0-0', '2.0.0-1', [1]); + await assertPRsMergedBetween('2.0.0-0', '2.0.0-1-staging', [1]); }); test("Merge a pull request with the checklist locked, but don't CP it", async () => { @@ -388,7 +385,7 @@ describe('CIGitLogic', () => { mergePR(2); // Verify output for checklist and deploy comment, and make sure PR #2 is not on staging - await assertPRsMergedBetween('2.0.0-0', '2.0.0-1', [1]); + await assertPRsMergedBetween('2.0.0-0', '2.0.0-1-staging', [1]); }); test('Merge a pull request with the checklist locked and CP it to staging', async () => { @@ -396,10 +393,10 @@ describe('CIGitLogic', () => { cherryPickPRToStaging(3); // Verify output for checklist - await assertPRsMergedBetween('2.0.0-0', '2.0.0-2', [1, 3]); + await assertPRsMergedBetween('2.0.0-0', '2.0.0-2-staging', [1, 3]); // Verify output for deploy comment, and make sure PR #2 is not on staging - await assertPRsMergedBetween('2.0.0-1', '2.0.0-2', [3]); + await assertPRsMergedBetween('2.0.0-1-staging', '2.0.0-2-staging', [3]); }); test('Merge a pull request with the checklist locked and CP it to production', async () => { @@ -408,22 +405,22 @@ describe('CIGitLogic', () => { cherryPickPRToProduction(4); // Verify output for checklist - await assertPRsMergedBetween('2.0.0-0', '2.0.1-1', [1, 3]); + await assertPRsMergedBetween('2.0.0-0', '2.0.1-1-staging', [1, 3]); // Verify output for deploy comment await assertPRsMergedBetween('2.0.0-0', '2.0.1-0', [4]); }); -/* - test('Close the checklist', async () => { + + test('Close the checklist, deploy production and staging', async () => { deployProduction(); // Verify output for release body and production deploy comments - await assertPRsMergedBetween('2.0.0-0', '2.0.0-2', [1, 3]); + await assertPRsMergedBetween('2.0.0-0', '2.0.1-1', [1, 3]); // Verify output for new checklist and staging deploy comments - await assertPRsMergedBetween('2.0.0-2', '2.0.1-0', [2, 4]); + await assertPRsMergedBetween('2.0.0-2-staging', '2.0.2-0-staging', [2, 4]); }); - +/* test('Merging another pull request when the checklist is unlocked', async () => { createBasicPR(5); mergePR(5); From 8cbb83cc6d3b43b6b3c85ff4c0067c0fd3641823 Mon Sep 17 00:00:00 2001 From: Andrew Gable Date: Mon, 7 Apr 2025 13:54:25 -0600 Subject: [PATCH 10/22] Fix another test --- tests/unit/CIGitLogicTest.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/unit/CIGitLogicTest.ts b/tests/unit/CIGitLogicTest.ts index f1d785933df2..b7d19ff00d56 100644 --- a/tests/unit/CIGitLogicTest.ts +++ b/tests/unit/CIGitLogicTest.ts @@ -420,19 +420,19 @@ describe('CIGitLogic', () => { // Verify output for new checklist and staging deploy comments await assertPRsMergedBetween('2.0.0-2-staging', '2.0.2-0-staging', [2, 4]); }); -/* + test('Merging another pull request when the checklist is unlocked', async () => { createBasicPR(5); mergePR(5); deployStaging(); // Verify output for checklist - await assertPRsMergedBetween('2.0.0-2', '2.0.1-1', [2, 4, 5]); + await assertPRsMergedBetween('2.0.0-2-staging', '2.0.2-1-staging', [2, 4, 5]); // Verify output for deploy comment - await assertPRsMergedBetween('2.0.1-0', '2.0.1-1', [5]); + await assertPRsMergedBetween('2.0.2-0-staging', '2.0.2-1-staging', [5]); }); - +/* test('Deploying a PR, then CPing a revert, then adding the same code back again before the next production deploy results in the correct code on staging and production', async () => { Log.info('Creating myFile.txt in PR #6'); setupGitAsHuman(); From fa6268d15b0bc32d3161f986634e820bf7b772e7 Mon Sep 17 00:00:00 2001 From: Andrew Gable Date: Mon, 7 Apr 2025 14:44:25 -0600 Subject: [PATCH 11/22] Update a few more test cases --- tests/unit/CIGitLogicTest.ts | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/tests/unit/CIGitLogicTest.ts b/tests/unit/CIGitLogicTest.ts index b7d19ff00d56..373e58140e56 100644 --- a/tests/unit/CIGitLogicTest.ts +++ b/tests/unit/CIGitLogicTest.ts @@ -400,7 +400,6 @@ describe('CIGitLogic', () => { }); test('Merge a pull request with the checklist locked and CP it to production', async () => { - // updateProductionFromStaging(); createBasicPR(4); cherryPickPRToProduction(4); @@ -432,7 +431,7 @@ describe('CIGitLogic', () => { // Verify output for deploy comment await assertPRsMergedBetween('2.0.2-0-staging', '2.0.2-1-staging', [5]); }); -/* + test('Deploying a PR, then CPing a revert, then adding the same code back again before the next production deploy results in the correct code on staging and production', async () => { Log.info('Creating myFile.txt in PR #6'); setupGitAsHuman(); @@ -447,10 +446,10 @@ describe('CIGitLogic', () => { deployStaging(); // Verify output for checklist - await assertPRsMergedBetween('2.0.0-2', '2.0.1-2', [2, 4, 5, 6]); + await assertPRsMergedBetween('2.0.0-2-staging', '2.0.2-2-staging', [2, 4, 5, 6]); // Verify output for deploy comment - await assertPRsMergedBetween('2.0.1-1', '2.0.1-2', [6]); + await assertPRsMergedBetween('2.0.2-1-staging', '2.0.2-2-staging', [6]); Log.info('Appending and prepending content to myFile.txt in PR #7'); setupGitAsHuman(); @@ -468,10 +467,10 @@ Appended content deployStaging(); // Verify output for checklist - await assertPRsMergedBetween('2.0.0-2', '2.0.1-3', [2, 4, 5, 6, 7]); + await assertPRsMergedBetween('2.0.0-2-staging', '2.0.2-3-staging', [2, 4, 5, 6, 7]); // Verify output for deploy comment - await assertPRsMergedBetween('2.0.1-2', '2.0.1-3', [7]); + await assertPRsMergedBetween('2.0.2-2-staging', '2.0.2-3-staging', [7]); Log.info('Making an unrelated change in PR #8'); setupGitAsHuman(); @@ -509,10 +508,11 @@ Appended content deployProduction(); // Verify production release list - await assertPRsMergedBetween('2.0.0-2', '2.0.1-4', [2, 5, 6, 7, 9]); + // TODO: Fix this case + // await assertPRsMergedBetween('2.0.1-0', '2.0.2-4', [2, 4, 5, 6, 7, 9]); // Verify PR list for the new checklist - await assertPRsMergedBetween('2.0.1-4', '2.0.2-0', [8, 10]); + await assertPRsMergedBetween('2.0.2-4-staging', '2.0.3-0-staging', [8, 10]); }); test('Force-pushing to a branch after rebasing older commits', async () => { @@ -523,10 +523,10 @@ Appended content deployStaging(); // Verify PRs for checklist - await assertPRsMergedBetween('2.0.1-4', '2.0.2-1', [8, 10, 12]); + await assertPRsMergedBetween('2.0.2-4-staging', '2.0.3-1-staging', [8, 10, 12]); // Verify PRs for deploy comments - await assertPRsMergedBetween('2.0.2-0', '2.0.2-1', [12]); + await assertPRsMergedBetween('2.0.3-0-staging', '2.0.3-1-staging', [12]); checkoutRepo(); setupGitAsHuman(); @@ -539,10 +539,10 @@ Appended content deployProduction(); // Verify PRs for deploy comments / release - await assertPRsMergedBetween('2.0.1-4', '2.0.2-1', [8, 10, 12]); + await assertPRsMergedBetween('2.0.2-4-staging', '2.0.3-1-staging', [8, 10, 12]); // Verify PRs for new checklist - await assertPRsMergedBetween('2.0.2-1', '2.0.3-0', [11]); + await assertPRsMergedBetween('2.0.3-1-staging', '2.0.4-0-staging', [11]); }); test('Manual version bump', async () => { @@ -566,7 +566,7 @@ Appended content Log.success(`Deployed v${getVersion()} to staging!`); // Verify PRs for deploy comments / release and new checklist - await assertPRsMergedBetween('2.0.3-0', '4.0.0-0', [13]); + await assertPRsMergedBetween('2.0.4-0-staging', '5.0.0-0-staging', [13]); Log.info('Creating manual version bump in PR #14'); checkoutRepo(); @@ -594,10 +594,9 @@ Appended content ); // Verify PRs for deploy comments - await assertPRsMergedBetween('4.0.0-0', '7.0.0-0', [14]); + await assertPRsMergedBetween('5.0.0-0-staging', '8.0.0-0-staging', [14]); // Verify PRs for the deploy checklist - await assertPRsMergedBetween('2.0.3-0', '7.0.0-0', [13, 14]); - }); - */ + await assertPRsMergedBetween('2.0.4-0-staging', '8.0.0-0-staging', [13, 14]); + }) }); From ad38ff320ac91b310285c6d3117625377a2bde27 Mon Sep 17 00:00:00 2001 From: Andrew Gable Date: Mon, 7 Apr 2025 15:02:15 -0600 Subject: [PATCH 12/22] Fix prettier --- tests/unit/CIGitLogicTest.ts | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/tests/unit/CIGitLogicTest.ts b/tests/unit/CIGitLogicTest.ts index 373e58140e56..4deb1c82496c 100644 --- a/tests/unit/CIGitLogicTest.ts +++ b/tests/unit/CIGitLogicTest.ts @@ -14,7 +14,7 @@ import getPreviousVersion from '@github/actions/javascript/getPreviousVersion/ge import CONST from '@github/libs/CONST'; import GitUtils from '@github/libs/GitUtils'; import * as VersionUpdater from '@github/libs/versionUpdater'; -import {SEMANTIC_VERSION_LEVELS, SemverLevel} from '@github/libs/versionUpdater'; +import {SemverLevel} from '@github/libs/versionUpdater'; import asMutable from '@src/types/utils/asMutable'; import * as Log from '../../scripts/utils/Logger'; @@ -83,8 +83,8 @@ function initGitServer() { exec(`git tag ${getVersion()}`); // Bump version to 2.0.0.0 - bumpVersion(VersionUpdater.SEMANTIC_VERSION_LEVELS.MAJOR, true) - exec(`git tag ${getVersion()}`) + bumpVersion(VersionUpdater.SEMANTIC_VERSION_LEVELS.MAJOR, true); + exec(`git tag ${getVersion()}`); exec(`git switch staging`); exec('git config --local receive.denyCurrentBranch ignore'); Log.success(`Initialized git server in ${GIT_REMOTE}`); @@ -270,9 +270,9 @@ function cherryPickPRToProduction(num: number, resolveVersionBumpConflicts: () = bumpVersion(VersionUpdater.SEMANTIC_VERSION_LEVELS.BUILD); versionBumpCommit = execSync('git rev-parse HEAD', {encoding: 'utf-8'}).trim(); - exec(`git fetch origin staging --depth=1`) - exec(`git switch staging`) - exec(`git cherry-pick -x --mainline 1 -Xtheirs ${versionBumpCommit}`) + exec(`git fetch origin staging --depth=1`); + exec(`git switch staging`); + exec(`git cherry-pick -x --mainline 1 -Xtheirs ${versionBumpCommit}`); exec('git push origin staging'); tagStaging(); Log.success(`Pushed to staging after CP to production`); @@ -311,7 +311,6 @@ function tagProduction() { Log.success(`Created new tag ${getVersion()}`); } - function deployStaging() { Log.info('Deploying staging...'); checkoutRepo(); @@ -598,5 +597,5 @@ Appended content // Verify PRs for the deploy checklist await assertPRsMergedBetween('2.0.4-0-staging', '8.0.0-0-staging', [13, 14]); - }) + }); }); From 5cbe76804912d11f83927a63c5101ca26251f4fe Mon Sep 17 00:00:00 2001 From: Andrew Gable Date: Mon, 7 Apr 2025 15:11:45 -0600 Subject: [PATCH 13/22] Fix last test case --- tests/unit/CIGitLogicTest.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/unit/CIGitLogicTest.ts b/tests/unit/CIGitLogicTest.ts index 4deb1c82496c..e48b9cb5d34a 100644 --- a/tests/unit/CIGitLogicTest.ts +++ b/tests/unit/CIGitLogicTest.ts @@ -507,8 +507,7 @@ Appended content deployProduction(); // Verify production release list - // TODO: Fix this case - // await assertPRsMergedBetween('2.0.1-0', '2.0.2-4', [2, 4, 5, 6, 7, 9]); + await assertPRsMergedBetween('2.0.1-1', '2.0.2-4', [2, 4, 5, 6, 7, 9]); // Verify PR list for the new checklist await assertPRsMergedBetween('2.0.2-4-staging', '2.0.3-0-staging', [8, 10]); From 4ab4779c1f778ee0724993f7986114735276182b Mon Sep 17 00:00:00 2001 From: Andrew Gable Date: Mon, 7 Apr 2025 15:51:51 -0600 Subject: [PATCH 14/22] Debugging and fixing hacks for fetching --- .github/libs/GitUtils.ts | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/.github/libs/GitUtils.ts b/.github/libs/GitUtils.ts index daf4eba844fd..9f867736b384 100644 --- a/.github/libs/GitUtils.ts +++ b/.github/libs/GitUtils.ts @@ -113,17 +113,11 @@ function fetchTag(tag: string, shallowExcludeTag = '') { */ function getCommitHistoryAsJSON(fromTag: string, toTag: string): Promise { // Fetch tags, excluding commits reachable from the previous patch version (i.e: previous checklist), so that we don't have to fetch the full history - // TODO: Add check for if tag is off by at least a minor, fetch the minor + // const previousPatchVersion = getPreviousExistingTag(fromTag, VersionUpdater.SEMANTIC_VERSION_LEVELS.PATCH); + // fetchTag(fromTag, previousPatchVersion); + // fetchTag(toTag, previousPatchVersion); - // const previousPatchVersion = getPreviousExistingTag(fromTag, VersionUpdater.SEMANTIC_VERSION_LEVELS.MINOR); - // fetchTag(fromTag, `${fromTag}~1`); - // fetchTag(toTag, `${fromTag}~1`); - - // execSync(`git fetch origin tag --no-tags ${fromTag}`); - // execSync(`git fetch origin tag --no-tags ${toTag}`); - // const hashForFromTag = execSync(`git rev-parse ${fromTag}`).toString().trim(); - - // TODO: Make this fast 🚀 + // TODO: Make this fast by removing this line and relying on fetchTag above. execSync(`git fetch --tags --unshallow`); console.log('Getting pull requests merged between the following tags:', fromTag, toTag); From af5375e9341d1a608b595f3cb707b3eef7b84b48 Mon Sep 17 00:00:00 2001 From: Andrew Gable Date: Mon, 7 Apr 2025 16:35:01 -0600 Subject: [PATCH 15/22] Fixing lint, tests, and minimal changes --- .../javascript/createOrUpdateStagingDeploy/index.js | 4 +++- .../actions/javascript/getDeployPullRequestList/index.js | 4 +++- .github/actions/javascript/getPreviousVersion/index.js | 4 +++- .github/libs/GitUtils.ts | 8 ++++---- tests/unit/CIGitLogicTest.ts | 2 +- 5 files changed, 14 insertions(+), 8 deletions(-) diff --git a/.github/actions/javascript/createOrUpdateStagingDeploy/index.js b/.github/actions/javascript/createOrUpdateStagingDeploy/index.js index cc3b6228416b..25c852e6000a 100644 --- a/.github/actions/javascript/createOrUpdateStagingDeploy/index.js +++ b/.github/actions/javascript/createOrUpdateStagingDeploy/index.js @@ -11779,6 +11779,8 @@ function getCommitHistoryAsJSON(fromTag, toTag) { const previousPatchVersion = getPreviousExistingTag(fromTag, VersionUpdater.SEMANTIC_VERSION_LEVELS.PATCH); fetchTag(fromTag, previousPatchVersion); fetchTag(toTag, previousPatchVersion); + // TODO: Make this fast by removing this line and relying on fetchTag above. + (0, child_process_1.execSync)(`git repack -d && git fetch --tags --unshallow`); console.log('Getting pull requests merged between the following tags:', fromTag, toTag); return new Promise((resolve, reject) => { let stdout = ''; @@ -11821,7 +11823,7 @@ function getValidMergedPRs(commits) { if (author === CONST_1.default.OS_BOTIFY) { return; } - const match = commit.subject.match(/Merge pull request #(\d+) from (?!Expensify\/.*-cherry-pick-staging)/); + const match = commit.subject.match(/Merge pull request #(\d+) from (?!Expensify\/.*-cherry-pick-(staging|production))/); if (!Array.isArray(match) || match.length < 2) { return; } diff --git a/.github/actions/javascript/getDeployPullRequestList/index.js b/.github/actions/javascript/getDeployPullRequestList/index.js index 4fb109b9e7a5..fa199cd8b2c2 100644 --- a/.github/actions/javascript/getDeployPullRequestList/index.js +++ b/.github/actions/javascript/getDeployPullRequestList/index.js @@ -11820,6 +11820,8 @@ function getCommitHistoryAsJSON(fromTag, toTag) { const previousPatchVersion = getPreviousExistingTag(fromTag, VersionUpdater.SEMANTIC_VERSION_LEVELS.PATCH); fetchTag(fromTag, previousPatchVersion); fetchTag(toTag, previousPatchVersion); + // TODO: Make this fast by removing this line and relying on fetchTag above. + (0, child_process_1.execSync)(`git repack -d && git fetch --tags --unshallow`); console.log('Getting pull requests merged between the following tags:', fromTag, toTag); return new Promise((resolve, reject) => { let stdout = ''; @@ -11862,7 +11864,7 @@ function getValidMergedPRs(commits) { if (author === CONST_1.default.OS_BOTIFY) { return; } - const match = commit.subject.match(/Merge pull request #(\d+) from (?!Expensify\/.*-cherry-pick-staging)/); + const match = commit.subject.match(/Merge pull request #(\d+) from (?!Expensify\/.*-cherry-pick-(staging|production))/); if (!Array.isArray(match) || match.length < 2) { return; } diff --git a/.github/actions/javascript/getPreviousVersion/index.js b/.github/actions/javascript/getPreviousVersion/index.js index e0db5ae76166..456ea63c126e 100644 --- a/.github/actions/javascript/getPreviousVersion/index.js +++ b/.github/actions/javascript/getPreviousVersion/index.js @@ -2944,6 +2944,8 @@ function getCommitHistoryAsJSON(fromTag, toTag) { const previousPatchVersion = getPreviousExistingTag(fromTag, VersionUpdater.SEMANTIC_VERSION_LEVELS.PATCH); fetchTag(fromTag, previousPatchVersion); fetchTag(toTag, previousPatchVersion); + // TODO: Make this fast by removing this line and relying on fetchTag above. + (0, child_process_1.execSync)(`git repack -d && git fetch --tags --unshallow`); console.log('Getting pull requests merged between the following tags:', fromTag, toTag); return new Promise((resolve, reject) => { let stdout = ''; @@ -2986,7 +2988,7 @@ function getValidMergedPRs(commits) { if (author === CONST_1.default.OS_BOTIFY) { return; } - const match = commit.subject.match(/Merge pull request #(\d+) from (?!Expensify\/.*-cherry-pick-staging)/); + const match = commit.subject.match(/Merge pull request #(\d+) from (?!Expensify\/.*-cherry-pick-(staging|production))/); if (!Array.isArray(match) || match.length < 2) { return; } diff --git a/.github/libs/GitUtils.ts b/.github/libs/GitUtils.ts index 9f867736b384..f81adc70f8ed 100644 --- a/.github/libs/GitUtils.ts +++ b/.github/libs/GitUtils.ts @@ -113,12 +113,12 @@ function fetchTag(tag: string, shallowExcludeTag = '') { */ function getCommitHistoryAsJSON(fromTag: string, toTag: string): Promise { // Fetch tags, excluding commits reachable from the previous patch version (i.e: previous checklist), so that we don't have to fetch the full history - // const previousPatchVersion = getPreviousExistingTag(fromTag, VersionUpdater.SEMANTIC_VERSION_LEVELS.PATCH); - // fetchTag(fromTag, previousPatchVersion); - // fetchTag(toTag, previousPatchVersion); + const previousPatchVersion = getPreviousExistingTag(fromTag, VersionUpdater.SEMANTIC_VERSION_LEVELS.PATCH); + fetchTag(fromTag, previousPatchVersion); + fetchTag(toTag, previousPatchVersion); // TODO: Make this fast by removing this line and relying on fetchTag above. - execSync(`git fetch --tags --unshallow`); + execSync(`git repack -d && git fetch --tags --unshallow`); console.log('Getting pull requests merged between the following tags:', fromTag, toTag); return new Promise((resolve, reject) => { diff --git a/tests/unit/CIGitLogicTest.ts b/tests/unit/CIGitLogicTest.ts index e48b9cb5d34a..7f3819de88ec 100644 --- a/tests/unit/CIGitLogicTest.ts +++ b/tests/unit/CIGitLogicTest.ts @@ -14,7 +14,7 @@ import getPreviousVersion from '@github/actions/javascript/getPreviousVersion/ge import CONST from '@github/libs/CONST'; import GitUtils from '@github/libs/GitUtils'; import * as VersionUpdater from '@github/libs/versionUpdater'; -import {SemverLevel} from '@github/libs/versionUpdater'; +import type {SemverLevel} from '@github/libs/versionUpdater'; import asMutable from '@src/types/utils/asMutable'; import * as Log from '../../scripts/utils/Logger'; From 7cb6e54f1bc88c6855b70f0d9bc52a6120af4d11 Mon Sep 17 00:00:00 2001 From: Andrew Gable Date: Mon, 7 Apr 2025 16:45:45 -0600 Subject: [PATCH 16/22] Fix lint --- .github/libs/GitUtils.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/libs/GitUtils.ts b/.github/libs/GitUtils.ts index f81adc70f8ed..84b5cdf12ee2 100644 --- a/.github/libs/GitUtils.ts +++ b/.github/libs/GitUtils.ts @@ -1,7 +1,7 @@ import {execSync, spawn} from 'child_process'; import CONST from './CONST'; import sanitizeStringForJSONParse from './sanitizeStringForJSONParse'; -import * as VersionUpdater from './versionUpdater'; +import {getPreviousVersion, SEMANTIC_VERSION_LEVELS} from './versionUpdater'; import type {SemverLevel} from './versionUpdater'; type CommitType = { @@ -55,7 +55,7 @@ function tagExists(tag: string) { * @param level the Semver level to step backward by */ function getPreviousExistingTag(tag: string, level: SemverLevel) { - let previousVersion = VersionUpdater.getPreviousVersion(tag, level); + let previousVersion = getPreviousVersion(tag, level); let tagExistsForPreviousVersion = false; while (!tagExistsForPreviousVersion) { if (tagExists(previousVersion)) { @@ -63,7 +63,7 @@ function getPreviousExistingTag(tag: string, level: SemverLevel) { break; } console.log(`Tag for previous version ${previousVersion} does not exist. Checking for an older version...`); - previousVersion = VersionUpdater.getPreviousVersion(previousVersion, level); + previousVersion = getPreviousVersion(previousVersion, level); } return previousVersion; } @@ -113,7 +113,7 @@ function fetchTag(tag: string, shallowExcludeTag = '') { */ function getCommitHistoryAsJSON(fromTag: string, toTag: string): Promise { // Fetch tags, excluding commits reachable from the previous patch version (i.e: previous checklist), so that we don't have to fetch the full history - const previousPatchVersion = getPreviousExistingTag(fromTag, VersionUpdater.SEMANTIC_VERSION_LEVELS.PATCH); + const previousPatchVersion = getPreviousExistingTag(fromTag, SEMANTIC_VERSION_LEVELS.PATCH); fetchTag(fromTag, previousPatchVersion); fetchTag(toTag, previousPatchVersion); From 8986aacc5ad951e331847884b7af3b71d6387aeb Mon Sep 17 00:00:00 2001 From: Andrew Gable Date: Mon, 7 Apr 2025 16:49:22 -0600 Subject: [PATCH 17/22] Rebuild tests after eslint fixes --- .../createOrUpdateStagingDeploy/index.js | 31 +++---------------- .../getDeployPullRequestList/index.js | 31 +++---------------- .../javascript/getPreviousVersion/index.js | 31 +++---------------- 3 files changed, 12 insertions(+), 81 deletions(-) diff --git a/.github/actions/javascript/createOrUpdateStagingDeploy/index.js b/.github/actions/javascript/createOrUpdateStagingDeploy/index.js index 25c852e6000a..491a76a733d6 100644 --- a/.github/actions/javascript/createOrUpdateStagingDeploy/index.js +++ b/.github/actions/javascript/createOrUpdateStagingDeploy/index.js @@ -11643,29 +11643,6 @@ exports["default"] = CONST; "use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; @@ -11673,7 +11650,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); const child_process_1 = __nccwpck_require__(2081); const CONST_1 = __importDefault(__nccwpck_require__(9873)); const sanitizeStringForJSONParse_1 = __importDefault(__nccwpck_require__(3902)); -const VersionUpdater = __importStar(__nccwpck_require__(8982)); +const versionUpdater_1 = __nccwpck_require__(8982); /** * Check if a tag exists locally or in the remote. */ @@ -11721,7 +11698,7 @@ function tagExists(tag) { * @param level the Semver level to step backward by */ function getPreviousExistingTag(tag, level) { - let previousVersion = VersionUpdater.getPreviousVersion(tag, level); + let previousVersion = (0, versionUpdater_1.getPreviousVersion)(tag, level); let tagExistsForPreviousVersion = false; while (!tagExistsForPreviousVersion) { if (tagExists(previousVersion)) { @@ -11729,7 +11706,7 @@ function getPreviousExistingTag(tag, level) { break; } console.log(`Tag for previous version ${previousVersion} does not exist. Checking for an older version...`); - previousVersion = VersionUpdater.getPreviousVersion(previousVersion, level); + previousVersion = (0, versionUpdater_1.getPreviousVersion)(previousVersion, level); } return previousVersion; } @@ -11776,7 +11753,7 @@ function fetchTag(tag, shallowExcludeTag = '') { */ function getCommitHistoryAsJSON(fromTag, toTag) { // Fetch tags, excluding commits reachable from the previous patch version (i.e: previous checklist), so that we don't have to fetch the full history - const previousPatchVersion = getPreviousExistingTag(fromTag, VersionUpdater.SEMANTIC_VERSION_LEVELS.PATCH); + const previousPatchVersion = getPreviousExistingTag(fromTag, versionUpdater_1.SEMANTIC_VERSION_LEVELS.PATCH); fetchTag(fromTag, previousPatchVersion); fetchTag(toTag, previousPatchVersion); // TODO: Make this fast by removing this line and relying on fetchTag above. diff --git a/.github/actions/javascript/getDeployPullRequestList/index.js b/.github/actions/javascript/getDeployPullRequestList/index.js index fa199cd8b2c2..6c3b0c5196c2 100644 --- a/.github/actions/javascript/getDeployPullRequestList/index.js +++ b/.github/actions/javascript/getDeployPullRequestList/index.js @@ -11684,29 +11684,6 @@ exports["default"] = CONST; "use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; @@ -11714,7 +11691,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); const child_process_1 = __nccwpck_require__(2081); const CONST_1 = __importDefault(__nccwpck_require__(9873)); const sanitizeStringForJSONParse_1 = __importDefault(__nccwpck_require__(3902)); -const VersionUpdater = __importStar(__nccwpck_require__(8982)); +const versionUpdater_1 = __nccwpck_require__(8982); /** * Check if a tag exists locally or in the remote. */ @@ -11762,7 +11739,7 @@ function tagExists(tag) { * @param level the Semver level to step backward by */ function getPreviousExistingTag(tag, level) { - let previousVersion = VersionUpdater.getPreviousVersion(tag, level); + let previousVersion = (0, versionUpdater_1.getPreviousVersion)(tag, level); let tagExistsForPreviousVersion = false; while (!tagExistsForPreviousVersion) { if (tagExists(previousVersion)) { @@ -11770,7 +11747,7 @@ function getPreviousExistingTag(tag, level) { break; } console.log(`Tag for previous version ${previousVersion} does not exist. Checking for an older version...`); - previousVersion = VersionUpdater.getPreviousVersion(previousVersion, level); + previousVersion = (0, versionUpdater_1.getPreviousVersion)(previousVersion, level); } return previousVersion; } @@ -11817,7 +11794,7 @@ function fetchTag(tag, shallowExcludeTag = '') { */ function getCommitHistoryAsJSON(fromTag, toTag) { // Fetch tags, excluding commits reachable from the previous patch version (i.e: previous checklist), so that we don't have to fetch the full history - const previousPatchVersion = getPreviousExistingTag(fromTag, VersionUpdater.SEMANTIC_VERSION_LEVELS.PATCH); + const previousPatchVersion = getPreviousExistingTag(fromTag, versionUpdater_1.SEMANTIC_VERSION_LEVELS.PATCH); fetchTag(fromTag, previousPatchVersion); fetchTag(toTag, previousPatchVersion); // TODO: Make this fast by removing this line and relying on fetchTag above. diff --git a/.github/actions/javascript/getPreviousVersion/index.js b/.github/actions/javascript/getPreviousVersion/index.js index 456ea63c126e..b1088ed37ef9 100644 --- a/.github/actions/javascript/getPreviousVersion/index.js +++ b/.github/actions/javascript/getPreviousVersion/index.js @@ -2808,29 +2808,6 @@ exports["default"] = CONST; "use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; @@ -2838,7 +2815,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); const child_process_1 = __nccwpck_require__(81); const CONST_1 = __importDefault(__nccwpck_require__(873)); const sanitizeStringForJSONParse_1 = __importDefault(__nccwpck_require__(902)); -const VersionUpdater = __importStar(__nccwpck_require__(982)); +const versionUpdater_1 = __nccwpck_require__(982); /** * Check if a tag exists locally or in the remote. */ @@ -2886,7 +2863,7 @@ function tagExists(tag) { * @param level the Semver level to step backward by */ function getPreviousExistingTag(tag, level) { - let previousVersion = VersionUpdater.getPreviousVersion(tag, level); + let previousVersion = (0, versionUpdater_1.getPreviousVersion)(tag, level); let tagExistsForPreviousVersion = false; while (!tagExistsForPreviousVersion) { if (tagExists(previousVersion)) { @@ -2894,7 +2871,7 @@ function getPreviousExistingTag(tag, level) { break; } console.log(`Tag for previous version ${previousVersion} does not exist. Checking for an older version...`); - previousVersion = VersionUpdater.getPreviousVersion(previousVersion, level); + previousVersion = (0, versionUpdater_1.getPreviousVersion)(previousVersion, level); } return previousVersion; } @@ -2941,7 +2918,7 @@ function fetchTag(tag, shallowExcludeTag = '') { */ function getCommitHistoryAsJSON(fromTag, toTag) { // Fetch tags, excluding commits reachable from the previous patch version (i.e: previous checklist), so that we don't have to fetch the full history - const previousPatchVersion = getPreviousExistingTag(fromTag, VersionUpdater.SEMANTIC_VERSION_LEVELS.PATCH); + const previousPatchVersion = getPreviousExistingTag(fromTag, versionUpdater_1.SEMANTIC_VERSION_LEVELS.PATCH); fetchTag(fromTag, previousPatchVersion); fetchTag(toTag, previousPatchVersion); // TODO: Make this fast by removing this line and relying on fetchTag above. From e6508c5ec8c8de3b34ae72b320809f3a35385b72 Mon Sep 17 00:00:00 2001 From: Andrew Gable Date: Tue, 8 Apr 2025 12:04:57 -0600 Subject: [PATCH 18/22] Fix todos --- tests/unit/CIGitLogicTest.ts | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/tests/unit/CIGitLogicTest.ts b/tests/unit/CIGitLogicTest.ts index 7f3819de88ec..5d291f637c57 100644 --- a/tests/unit/CIGitLogicTest.ts +++ b/tests/unit/CIGitLogicTest.ts @@ -183,8 +183,6 @@ function mergePR(num: number) { function cherryPickPRToStaging(num: number, resolveVersionBumpConflicts: () => void = () => {}, resolveMergeCommitConflicts: () => void = () => {}) { Log.info(`Cherry-picking PR ${num} to staging...`); - // TODO: Move mergePR into the test itself - mergePR(num); const prMergeCommit = execSync('git rev-parse HEAD', {encoding: 'utf-8'}).trim(); bumpVersion(VersionUpdater.SEMANTIC_VERSION_LEVELS.BUILD); const versionBumpCommit = execSync('git rev-parse HEAD', {encoding: 'utf-8'}).trim(); @@ -206,17 +204,11 @@ function cherryPickPRToStaging(num: number, resolveVersionBumpConflicts: () => v resolveVersionBumpConflicts(); } - // TODO: This assumes that we have a conflict, we should not assume that setupGitAsHuman(); try { exec(`git cherry-pick -x --mainline 1 --strategy=recursive -Xtheirs ${prMergeCommit}`); } catch (e) { - // 1. Abort cherry-pick - // 2. Create the cherry-pick-staging branch - // 3. Run setupGitAsHuman() - // 4. Re-run the cherry pick git command (it will have conflicts again) - // 5. Catch the conflicts exception, run resolveMergeCommitConflicts() resolveMergeCommitConflicts(); } @@ -232,7 +224,6 @@ function cherryPickPRToStaging(num: number, resolveVersionBumpConflicts: () => v function cherryPickPRToProduction(num: number, resolveVersionBumpConflicts: () => void = () => {}, resolveMergeCommitConflicts: () => void = () => {}) { Log.info(`Cherry-picking PR ${num} to production...`); - mergePR(num); const prMergeCommit = execSync('git rev-parse HEAD', {encoding: 'utf-8'}).trim(); bumpVersion(VersionUpdater.SEMANTIC_VERSION_LEVELS.PATCH); let versionBumpCommit = execSync('git rev-parse HEAD', {encoding: 'utf-8'}).trim(); @@ -389,6 +380,7 @@ describe('CIGitLogic', () => { test('Merge a pull request with the checklist locked and CP it to staging', async () => { createBasicPR(3); + mergePR(3); cherryPickPRToStaging(3); // Verify output for checklist @@ -400,6 +392,7 @@ describe('CIGitLogic', () => { test('Merge a pull request with the checklist locked and CP it to production', async () => { createBasicPR(4); + mergePR(4); cherryPickPRToProduction(4); // Verify output for checklist @@ -489,6 +482,7 @@ Appended content console.log('RORY_DEBUG AFTER:', fs.readFileSync('myFile.txt', {encoding: 'utf8'})); exec('git add myFile.txt'); exec('git commit -m "Revert append and prepend"'); + mergePR(9); cherryPickPRToStaging(9); Log.info('Verifying that the revert is present on staging, but the unrelated change is not'); @@ -579,6 +573,7 @@ Appended content Log.success('Created manual version bump in PR #14 in branch pr-14'); const packageJSONBefore = fs.readFileSync('package.json', {encoding: 'utf-8'}); + mergePR(14); cherryPickPRToStaging( 14, () => { From edbe72b0e91672f8bd690b2e964ca28d65623e4d Mon Sep 17 00:00:00 2001 From: rory Date: Tue, 8 Apr 2025 12:42:57 -0700 Subject: [PATCH 19/22] Fix tests --- .github/libs/GitUtils.ts | 14 +++-- tests/unit/CIGitLogicTest.ts | 119 ++++++++++++++++++----------------- 2 files changed, 69 insertions(+), 64 deletions(-) diff --git a/.github/libs/GitUtils.ts b/.github/libs/GitUtils.ts index 84b5cdf12ee2..58d6c1544a54 100644 --- a/.github/libs/GitUtils.ts +++ b/.github/libs/GitUtils.ts @@ -55,13 +55,18 @@ function tagExists(tag: string) { * @param level the Semver level to step backward by */ function getPreviousExistingTag(tag: string, level: SemverLevel) { - let previousVersion = getPreviousVersion(tag, level); + let previousVersion = getPreviousVersion(tag.replace('-staging', ''), level); let tagExistsForPreviousVersion = false; while (!tagExistsForPreviousVersion) { if (tagExists(previousVersion)) { tagExistsForPreviousVersion = true; break; } + if (tagExists(`${previousVersion}-staging`)) { + tagExistsForPreviousVersion = true; + previousVersion = `${previousVersion}-staging`; + break; + } console.log(`Tag for previous version ${previousVersion} does not exist. Checking for an older version...`); previousVersion = getPreviousVersion(previousVersion, level); } @@ -112,14 +117,11 @@ function fetchTag(tag: string, shallowExcludeTag = '') { * Get merge logs between two tags (inclusive) as a JavaScript object. */ function getCommitHistoryAsJSON(fromTag: string, toTag: string): Promise { - // Fetch tags, excluding commits reachable from the previous patch version (i.e: previous checklist), so that we don't have to fetch the full history - const previousPatchVersion = getPreviousExistingTag(fromTag, SEMANTIC_VERSION_LEVELS.PATCH); + // Fetch tags, excluding commits reachable from the previous patch version (or minor for prod) (i.e: previous checklist), so that we don't have to fetch the full history + const previousPatchVersion = getPreviousExistingTag(fromTag.replace('-staging', ''), fromTag.endsWith('-staging') ? SEMANTIC_VERSION_LEVELS.PATCH : SEMANTIC_VERSION_LEVELS.MINOR); fetchTag(fromTag, previousPatchVersion); fetchTag(toTag, previousPatchVersion); - // TODO: Make this fast by removing this line and relying on fetchTag above. - execSync(`git repack -d && git fetch --tags --unshallow`); - console.log('Getting pull requests merged between the following tags:', fromTag, toTag); return new Promise((resolve, reject) => { let stdout = ''; diff --git a/tests/unit/CIGitLogicTest.ts b/tests/unit/CIGitLogicTest.ts index 5d291f637c57..42400f556239 100644 --- a/tests/unit/CIGitLogicTest.ts +++ b/tests/unit/CIGitLogicTest.ts @@ -84,6 +84,9 @@ function initGitServer() { // Bump version to 2.0.0.0 bumpVersion(VersionUpdater.SEMANTIC_VERSION_LEVELS.MAJOR, true); + exec('git branch -D staging production'); + exec('git switch -c staging'); + exec('git switch -c production'); exec(`git tag ${getVersion()}`); exec(`git switch staging`); exec('git config --local receive.denyCurrentBranch ignore'); @@ -391,15 +394,15 @@ describe('CIGitLogic', () => { }); test('Merge a pull request with the checklist locked and CP it to production', async () => { - createBasicPR(4); - mergePR(4); - cherryPickPRToProduction(4); + createBasicPR(5); + mergePR(5); + cherryPickPRToProduction(5); // Verify output for checklist await assertPRsMergedBetween('2.0.0-0', '2.0.1-1-staging', [1, 3]); // Verify output for deploy comment - await assertPRsMergedBetween('2.0.0-0', '2.0.1-0', [4]); + await assertPRsMergedBetween('2.0.0-0', '2.0.1-0', [5]); }); test('Close the checklist, deploy production and staging', async () => { @@ -409,44 +412,44 @@ describe('CIGitLogic', () => { await assertPRsMergedBetween('2.0.0-0', '2.0.1-1', [1, 3]); // Verify output for new checklist and staging deploy comments - await assertPRsMergedBetween('2.0.0-2-staging', '2.0.2-0-staging', [2, 4]); + await assertPRsMergedBetween('2.0.0-2-staging', '2.0.2-0-staging', [2, 5]); }); test('Merging another pull request when the checklist is unlocked', async () => { - createBasicPR(5); - mergePR(5); + createBasicPR(6); + mergePR(6); deployStaging(); // Verify output for checklist - await assertPRsMergedBetween('2.0.0-2-staging', '2.0.2-1-staging', [2, 4, 5]); + await assertPRsMergedBetween('2.0.0-2-staging', '2.0.2-1-staging', [2, 5, 6]); // Verify output for deploy comment - await assertPRsMergedBetween('2.0.2-0-staging', '2.0.2-1-staging', [5]); + await assertPRsMergedBetween('2.0.2-0-staging', '2.0.2-1-staging', [6]); }); test('Deploying a PR, then CPing a revert, then adding the same code back again before the next production deploy results in the correct code on staging and production', async () => { - Log.info('Creating myFile.txt in PR #6'); + Log.info('Creating myFile.txt in PR #7'); setupGitAsHuman(); exec('git switch main'); - exec('git switch -c pr-6'); - const initialFileContent = 'Changes from PR #6'; - fs.appendFileSync('myFile.txt', 'Changes from PR #6'); + exec('git switch -c pr-7'); + const initialFileContent = 'Changes from PR #7'; + fs.appendFileSync('myFile.txt', 'Changes from PR #7'); exec('git add myFile.txt'); - exec('git commit -m "Add myFile.txt in PR #6"'); + exec('git commit -m "Add myFile.txt in PR #7"'); - mergePR(6); + mergePR(7); deployStaging(); // Verify output for checklist - await assertPRsMergedBetween('2.0.0-2-staging', '2.0.2-2-staging', [2, 4, 5, 6]); + await assertPRsMergedBetween('2.0.0-2-staging', '2.0.2-2-staging', [2, 5, 6, 7]); // Verify output for deploy comment - await assertPRsMergedBetween('2.0.2-1-staging', '2.0.2-2-staging', [6]); + await assertPRsMergedBetween('2.0.2-1-staging', '2.0.2-2-staging', [7]); - Log.info('Appending and prepending content to myFile.txt in PR #7'); + Log.info('Appending and prepending content to myFile.txt in PR #8'); setupGitAsHuman(); exec('git switch main'); - exec('git switch -c pr-7'); + exec('git switch -c pr-8'); const newFileContent = ` Prepended content ${initialFileContent} @@ -455,35 +458,35 @@ Appended content fs.writeFileSync('myFile.txt', newFileContent, {encoding: 'utf-8'}); exec('git add myFile.txt'); exec('git commit -m "Append and prepend content in myFile.txt"'); - mergePR(7); + mergePR(8); deployStaging(); // Verify output for checklist - await assertPRsMergedBetween('2.0.0-2-staging', '2.0.2-3-staging', [2, 4, 5, 6, 7]); + await assertPRsMergedBetween('2.0.0-2-staging', '2.0.2-3-staging', [2, 5, 6, 7, 8]); // Verify output for deploy comment - await assertPRsMergedBetween('2.0.2-2-staging', '2.0.2-3-staging', [7]); + await assertPRsMergedBetween('2.0.2-2-staging', '2.0.2-3-staging', [8]); - Log.info('Making an unrelated change in PR #8'); + Log.info('Making an unrelated change in PR #9'); setupGitAsHuman(); exec('git switch main'); - exec('git switch -c pr-8'); + exec('git switch -c pr-9'); fs.appendFileSync('anotherFile.txt', 'some content'); exec('git add anotherFile.txt'); exec('git commit -m "Create another file"'); - mergePR(8); + mergePR(9); - Log.info('Reverting the append + prepend on main in PR #9'); + Log.info('Reverting the append + prepend on main in PR #10'); setupGitAsHuman(); exec('git switch main'); - exec('git switch -c pr-9'); + exec('git switch -c pr-10'); console.log('RORY_DEBUG BEFORE:', fs.readFileSync('myFile.txt', {encoding: 'utf8'})); fs.writeFileSync('myFile.txt', initialFileContent); console.log('RORY_DEBUG AFTER:', fs.readFileSync('myFile.txt', {encoding: 'utf8'})); exec('git add myFile.txt'); exec('git commit -m "Revert append and prepend"'); - mergePR(9); - cherryPickPRToStaging(9); + mergePR(10); + cherryPickPRToStaging(10); Log.info('Verifying that the revert is present on staging, but the unrelated change is not'); expect(fs.readFileSync('myFile.txt', {encoding: 'utf8'})).toBe(initialFileContent); @@ -492,65 +495,65 @@ Appended content Log.info('Repeating previously reverted append + prepend on main in PR #10'); setupGitAsHuman(); exec('git switch main'); - exec('git switch -c pr-10'); + exec('git switch -c pr-11'); fs.writeFileSync('myFile.txt', newFileContent, {encoding: 'utf-8'}); exec('git add myFile.txt'); exec('git commit -m "Append and prepend content in myFile.txt"'); - mergePR(10); + mergePR(11); deployProduction(); // Verify production release list - await assertPRsMergedBetween('2.0.1-1', '2.0.2-4', [2, 4, 5, 6, 7, 9]); + await assertPRsMergedBetween('2.0.1-1', '2.0.2-4', [2, 5, 6, 7, 8, 10]); // Verify PR list for the new checklist - await assertPRsMergedBetween('2.0.2-4-staging', '2.0.3-0-staging', [8, 10]); + await assertPRsMergedBetween('2.0.2-4-staging', '2.0.3-0-staging', [9, 11]); }); test('Force-pushing to a branch after rebasing older commits', async () => { - createBasicPR(11); - exec('git push origin pr-11'); createBasicPR(12); - mergePR(12); + exec('git push origin pr-12'); + createBasicPR(13); + mergePR(13); deployStaging(); // Verify PRs for checklist - await assertPRsMergedBetween('2.0.2-4-staging', '2.0.3-1-staging', [8, 10, 12]); + await assertPRsMergedBetween('2.0.2-4-staging', '2.0.3-1-staging', [9, 11, 13]); // Verify PRs for deploy comments - await assertPRsMergedBetween('2.0.3-0-staging', '2.0.3-1-staging', [12]); + await assertPRsMergedBetween('2.0.3-0-staging', '2.0.3-1-staging', [13]); checkoutRepo(); setupGitAsHuman(); - exec('git fetch origin pr-11'); - exec('git switch pr-11'); + exec('git fetch origin pr-12'); + exec('git switch pr-12'); exec('git rebase main -Xours'); - exec('git push --force origin pr-11'); - mergePR(11); + exec('git push --force origin pr-12'); + mergePR(12); deployProduction(); // Verify PRs for deploy comments / release - await assertPRsMergedBetween('2.0.2-4-staging', '2.0.3-1-staging', [8, 10, 12]); + await assertPRsMergedBetween('2.0.2-4-staging', '2.0.3-1-staging', [9, 11, 13]); // Verify PRs for new checklist - await assertPRsMergedBetween('2.0.3-1-staging', '2.0.4-0-staging', [11]); + await assertPRsMergedBetween('2.0.3-1-staging', '2.0.4-0-staging', [12]); }); test('Manual version bump', async () => { - Log.info('Creating manual version bump in PR #13'); + Log.info('Creating manual version bump in PR #14'); checkoutRepo(); setupGitAsHuman(); exec('git pull'); - exec('git switch -c "pr-13"'); + exec('git switch -c "pr-14"'); for (let i = 0; i < 3; i++) { exec(`npm --no-git-tag-version version ${VersionUpdater.incrementVersion(getVersion(), VersionUpdater.SEMANTIC_VERSION_LEVELS.MAJOR)}`); } exec('git add package.json'); - exec(`git commit -m "Manually bump version to ${getVersion()} in PR #13"`); - Log.success('Created manual version bump in PR #13 in branch pr-13'); + exec(`git commit -m "Manually bump version to ${getVersion()} in PR #14"`); + Log.success('Created manual version bump in PR #13 in branch pr-14'); - mergePR(13); + mergePR(14); Log.info('Deploying staging...'); checkoutRepo(); updateStagingFromMain(); @@ -558,24 +561,24 @@ Appended content Log.success(`Deployed v${getVersion()} to staging!`); // Verify PRs for deploy comments / release and new checklist - await assertPRsMergedBetween('2.0.4-0-staging', '5.0.0-0-staging', [13]); + await assertPRsMergedBetween('2.0.4-0-staging', '5.0.0-0-staging', [14]); - Log.info('Creating manual version bump in PR #14'); + Log.info('Creating manual version bump in PR #15'); checkoutRepo(); setupGitAsHuman(); exec('git pull'); - exec('git switch -c "pr-14"'); + exec('git switch -c "pr-15"'); for (let i = 0; i < 3; i++) { exec(`npm --no-git-tag-version version ${VersionUpdater.incrementVersion(getVersion(), VersionUpdater.SEMANTIC_VERSION_LEVELS.MAJOR)}`); } exec('git add package.json'); - exec(`git commit -m "Manually bump version to ${getVersion()} in PR #14"`); - Log.success('Created manual version bump in PR #14 in branch pr-14'); + exec(`git commit -m "Manually bump version to ${getVersion()} in PR #15"`); + Log.success('Created manual version bump in PR #15 in branch pr-15'); const packageJSONBefore = fs.readFileSync('package.json', {encoding: 'utf-8'}); - mergePR(14); + mergePR(15); cherryPickPRToStaging( - 14, + 15, () => { fs.writeFileSync('package.json', packageJSONBefore); exec('git add package.json'); @@ -587,9 +590,9 @@ Appended content ); // Verify PRs for deploy comments - await assertPRsMergedBetween('5.0.0-0-staging', '8.0.0-0-staging', [14]); + await assertPRsMergedBetween('5.0.0-0-staging', '8.0.0-0-staging', [15]); // Verify PRs for the deploy checklist - await assertPRsMergedBetween('2.0.4-0-staging', '8.0.0-0-staging', [13, 14]); + await assertPRsMergedBetween('2.0.4-0-staging', '8.0.0-0-staging', [14, 15]); }); }); From b9b7a1ebb5a620e11d2d53b6cd314990d38dd9fb Mon Sep 17 00:00:00 2001 From: Andrew Gable Date: Tue, 8 Apr 2025 14:31:19 -0600 Subject: [PATCH 20/22] Rebuild actions --- .../javascript/createOrUpdateStagingDeploy/index.js | 13 ++++++++----- .../javascript/getDeployPullRequestList/index.js | 13 ++++++++----- .../actions/javascript/getPreviousVersion/index.js | 13 ++++++++----- 3 files changed, 24 insertions(+), 15 deletions(-) diff --git a/.github/actions/javascript/createOrUpdateStagingDeploy/index.js b/.github/actions/javascript/createOrUpdateStagingDeploy/index.js index 491a76a733d6..58c529bca0b5 100644 --- a/.github/actions/javascript/createOrUpdateStagingDeploy/index.js +++ b/.github/actions/javascript/createOrUpdateStagingDeploy/index.js @@ -11698,13 +11698,18 @@ function tagExists(tag) { * @param level the Semver level to step backward by */ function getPreviousExistingTag(tag, level) { - let previousVersion = (0, versionUpdater_1.getPreviousVersion)(tag, level); + let previousVersion = (0, versionUpdater_1.getPreviousVersion)(tag.replace('-staging', ''), level); let tagExistsForPreviousVersion = false; while (!tagExistsForPreviousVersion) { if (tagExists(previousVersion)) { tagExistsForPreviousVersion = true; break; } + if (tagExists(`${previousVersion}-staging`)) { + tagExistsForPreviousVersion = true; + previousVersion = `${previousVersion}-staging`; + break; + } console.log(`Tag for previous version ${previousVersion} does not exist. Checking for an older version...`); previousVersion = (0, versionUpdater_1.getPreviousVersion)(previousVersion, level); } @@ -11752,12 +11757,10 @@ function fetchTag(tag, shallowExcludeTag = '') { * Get merge logs between two tags (inclusive) as a JavaScript object. */ function getCommitHistoryAsJSON(fromTag, toTag) { - // Fetch tags, excluding commits reachable from the previous patch version (i.e: previous checklist), so that we don't have to fetch the full history - const previousPatchVersion = getPreviousExistingTag(fromTag, versionUpdater_1.SEMANTIC_VERSION_LEVELS.PATCH); + // Fetch tags, excluding commits reachable from the previous patch version (or minor for prod) (i.e: previous checklist), so that we don't have to fetch the full history + const previousPatchVersion = getPreviousExistingTag(fromTag.replace('-staging', ''), fromTag.endsWith('-staging') ? versionUpdater_1.SEMANTIC_VERSION_LEVELS.PATCH : versionUpdater_1.SEMANTIC_VERSION_LEVELS.MINOR); fetchTag(fromTag, previousPatchVersion); fetchTag(toTag, previousPatchVersion); - // TODO: Make this fast by removing this line and relying on fetchTag above. - (0, child_process_1.execSync)(`git repack -d && git fetch --tags --unshallow`); console.log('Getting pull requests merged between the following tags:', fromTag, toTag); return new Promise((resolve, reject) => { let stdout = ''; diff --git a/.github/actions/javascript/getDeployPullRequestList/index.js b/.github/actions/javascript/getDeployPullRequestList/index.js index 6c3b0c5196c2..937f83f08975 100644 --- a/.github/actions/javascript/getDeployPullRequestList/index.js +++ b/.github/actions/javascript/getDeployPullRequestList/index.js @@ -11739,13 +11739,18 @@ function tagExists(tag) { * @param level the Semver level to step backward by */ function getPreviousExistingTag(tag, level) { - let previousVersion = (0, versionUpdater_1.getPreviousVersion)(tag, level); + let previousVersion = (0, versionUpdater_1.getPreviousVersion)(tag.replace('-staging', ''), level); let tagExistsForPreviousVersion = false; while (!tagExistsForPreviousVersion) { if (tagExists(previousVersion)) { tagExistsForPreviousVersion = true; break; } + if (tagExists(`${previousVersion}-staging`)) { + tagExistsForPreviousVersion = true; + previousVersion = `${previousVersion}-staging`; + break; + } console.log(`Tag for previous version ${previousVersion} does not exist. Checking for an older version...`); previousVersion = (0, versionUpdater_1.getPreviousVersion)(previousVersion, level); } @@ -11793,12 +11798,10 @@ function fetchTag(tag, shallowExcludeTag = '') { * Get merge logs between two tags (inclusive) as a JavaScript object. */ function getCommitHistoryAsJSON(fromTag, toTag) { - // Fetch tags, excluding commits reachable from the previous patch version (i.e: previous checklist), so that we don't have to fetch the full history - const previousPatchVersion = getPreviousExistingTag(fromTag, versionUpdater_1.SEMANTIC_VERSION_LEVELS.PATCH); + // Fetch tags, excluding commits reachable from the previous patch version (or minor for prod) (i.e: previous checklist), so that we don't have to fetch the full history + const previousPatchVersion = getPreviousExistingTag(fromTag.replace('-staging', ''), fromTag.endsWith('-staging') ? versionUpdater_1.SEMANTIC_VERSION_LEVELS.PATCH : versionUpdater_1.SEMANTIC_VERSION_LEVELS.MINOR); fetchTag(fromTag, previousPatchVersion); fetchTag(toTag, previousPatchVersion); - // TODO: Make this fast by removing this line and relying on fetchTag above. - (0, child_process_1.execSync)(`git repack -d && git fetch --tags --unshallow`); console.log('Getting pull requests merged between the following tags:', fromTag, toTag); return new Promise((resolve, reject) => { let stdout = ''; diff --git a/.github/actions/javascript/getPreviousVersion/index.js b/.github/actions/javascript/getPreviousVersion/index.js index b1088ed37ef9..e4c852b625e0 100644 --- a/.github/actions/javascript/getPreviousVersion/index.js +++ b/.github/actions/javascript/getPreviousVersion/index.js @@ -2863,13 +2863,18 @@ function tagExists(tag) { * @param level the Semver level to step backward by */ function getPreviousExistingTag(tag, level) { - let previousVersion = (0, versionUpdater_1.getPreviousVersion)(tag, level); + let previousVersion = (0, versionUpdater_1.getPreviousVersion)(tag.replace('-staging', ''), level); let tagExistsForPreviousVersion = false; while (!tagExistsForPreviousVersion) { if (tagExists(previousVersion)) { tagExistsForPreviousVersion = true; break; } + if (tagExists(`${previousVersion}-staging`)) { + tagExistsForPreviousVersion = true; + previousVersion = `${previousVersion}-staging`; + break; + } console.log(`Tag for previous version ${previousVersion} does not exist. Checking for an older version...`); previousVersion = (0, versionUpdater_1.getPreviousVersion)(previousVersion, level); } @@ -2917,12 +2922,10 @@ function fetchTag(tag, shallowExcludeTag = '') { * Get merge logs between two tags (inclusive) as a JavaScript object. */ function getCommitHistoryAsJSON(fromTag, toTag) { - // Fetch tags, excluding commits reachable from the previous patch version (i.e: previous checklist), so that we don't have to fetch the full history - const previousPatchVersion = getPreviousExistingTag(fromTag, versionUpdater_1.SEMANTIC_VERSION_LEVELS.PATCH); + // Fetch tags, excluding commits reachable from the previous patch version (or minor for prod) (i.e: previous checklist), so that we don't have to fetch the full history + const previousPatchVersion = getPreviousExistingTag(fromTag.replace('-staging', ''), fromTag.endsWith('-staging') ? versionUpdater_1.SEMANTIC_VERSION_LEVELS.PATCH : versionUpdater_1.SEMANTIC_VERSION_LEVELS.MINOR); fetchTag(fromTag, previousPatchVersion); fetchTag(toTag, previousPatchVersion); - // TODO: Make this fast by removing this line and relying on fetchTag above. - (0, child_process_1.execSync)(`git repack -d && git fetch --tags --unshallow`); console.log('Getting pull requests merged between the following tags:', fromTag, toTag); return new Promise((resolve, reject) => { let stdout = ''; From 7df37b92a3477b5352f250489dc07b1c4bb19c04 Mon Sep 17 00:00:00 2001 From: Andrew Gable Date: Tue, 8 Apr 2025 15:07:01 -0600 Subject: [PATCH 21/22] Update tests/unit/CIGitLogicTest.ts Co-authored-by: Rory Abraham <47436092+roryabraham@users.noreply.github.com> --- tests/unit/CIGitLogicTest.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/CIGitLogicTest.ts b/tests/unit/CIGitLogicTest.ts index 42400f556239..bc05eb8991fe 100644 --- a/tests/unit/CIGitLogicTest.ts +++ b/tests/unit/CIGitLogicTest.ts @@ -64,7 +64,7 @@ function getVersion(): string { function initGitServer() { Log.info('Initializing git server...'); if (fs.existsSync(GIT_REMOTE)) { - Log.info(`${GIT_REMOTE} exists, remove it now...`); + Log.info(`${GIT_REMOTE} exists, removing it now...`); fs.rmSync(GIT_REMOTE, {recursive: true}); } fs.mkdirSync(GIT_REMOTE, {recursive: true}); From 2ea414f7950d5e55003bbe788e5bfee99bc3c3e4 Mon Sep 17 00:00:00 2001 From: Andrew Gable Date: Tue, 8 Apr 2025 15:10:29 -0600 Subject: [PATCH 22/22] Add `checkoutRepo()` --- tests/unit/CIGitLogicTest.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/unit/CIGitLogicTest.ts b/tests/unit/CIGitLogicTest.ts index bc05eb8991fe..be6a10f2466c 100644 --- a/tests/unit/CIGitLogicTest.ts +++ b/tests/unit/CIGitLogicTest.ts @@ -262,6 +262,7 @@ function cherryPickPRToProduction(num: number, resolveVersionBumpConflicts: () = Log.info(`Merged PR #${num + 1} into production`); tagProduction(); + checkoutRepo(); bumpVersion(VersionUpdater.SEMANTIC_VERSION_LEVELS.BUILD); versionBumpCommit = execSync('git rev-parse HEAD', {encoding: 'utf-8'}).trim(); exec(`git fetch origin staging --depth=1`);