diff --git a/.github/actions/javascript/createOrUpdateStagingDeploy/index.js b/.github/actions/javascript/createOrUpdateStagingDeploy/index.js index cc3b6228416b..58c529bca0b5 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,15 +11698,20 @@ 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.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 = VersionUpdater.getPreviousVersion(previousVersion, level); + previousVersion = (0, versionUpdater_1.getPreviousVersion)(previousVersion, level); } return previousVersion; } @@ -11775,8 +11757,8 @@ 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.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); console.log('Getting pull requests merged between the following tags:', fromTag, toTag); @@ -11821,7 +11803,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..937f83f08975 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,15 +11739,20 @@ 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.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 = VersionUpdater.getPreviousVersion(previousVersion, level); + previousVersion = (0, versionUpdater_1.getPreviousVersion)(previousVersion, level); } return previousVersion; } @@ -11816,8 +11798,8 @@ 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.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); console.log('Getting pull requests merged between the following tags:', fromTag, toTag); @@ -11862,7 +11844,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..e4c852b625e0 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,15 +2863,20 @@ 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.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 = VersionUpdater.getPreviousVersion(previousVersion, level); + previousVersion = (0, versionUpdater_1.getPreviousVersion)(previousVersion, level); } return previousVersion; } @@ -2940,8 +2922,8 @@ 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.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); console.log('Getting pull requests merged between the following tags:', fromTag, toTag); @@ -2986,7 +2968,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 89950613bc2e..58d6c1544a54 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,15 +55,20 @@ 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.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 = VersionUpdater.getPreviousVersion(previousVersion, level); + previousVersion = getPreviousVersion(previousVersion, level); } return previousVersion; } @@ -112,8 +117,8 @@ 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, VersionUpdater.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); @@ -164,7 +169,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 50471b76318a..be6a10f2466c 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, removing it now...`); fs.rmSync(GIT_REMOTE, {recursive: true}); } fs.mkdirSync(GIT_REMOTE, {recursive: true}); @@ -76,8 +77,18 @@ function initGitServer() { exec('git add -A'); exec('git commit -m "Initial commit"'); exec('git switch -c staging'); + 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 branch -D staging production'); + exec('git switch -c staging'); + exec('git switch -c production'); exec(`git tag ${getVersion()}`); - exec('git branch production'); + exec(`git switch staging`); exec('git config --local receive.denyCurrentBranch ignore'); Log.success(`Initialized git server in ${GIT_REMOTE}`); } @@ -96,7 +107,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'); @@ -104,7 +115,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`); } @@ -139,7 +152,8 @@ function updateProductionFromStaging() { } catch (e) {} exec('git switch -c production'); - exec('git push --force origin production'); + exec(`git tag ${getVersion()}`); + exec('git push --force --tags origin production'); Log.success('Recreated production from staging!'); } @@ -170,9 +184,8 @@ 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(); bumpVersion(VersionUpdater.SEMANTIC_VERSION_LEVELS.BUILD); const versionBumpCommit = execSync('git rev-parse HEAD', {encoding: 'utf-8'}).trim(); @@ -181,6 +194,8 @@ function cherryPickPR(num: number, resolveVersionBumpConflicts: () => void = () 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'); @@ -210,6 +225,56 @@ function cherryPickPR(num: number, resolveVersionBumpConflicts: () => void = () 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...`); + 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(); + checkoutRepo(); + setupGitAsOSBotify(); + + mockGetInput.mockReturnValue(VersionUpdater.SEMANTIC_VERSION_LEVELS.MINOR); + 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 -Xtheirs ${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(); + + checkoutRepo(); + 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'); + tagStaging(); + Log.success(`Pushed to staging after CP to production`); + + Log.success(`Successfully cherry-picked PR #${num} to production!`); +} + function tagStaging() { Log.info('Tagging new version from the staging branch...'); checkoutRepo(); @@ -220,6 +285,22 @@ function tagStaging() { exec('git fetch origin staging --depth=1'); } exec('git switch staging'); + exec(`git tag ${getVersion()}-staging`); + exec('git push --tags'); + Log.success(`Created new tag ${getVersion()}`); +} + +function tagProduction() { + Log.info('Tagging new version from the production branch...'); + Log.info(`Version is: ${getVersion()}`); + 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()}`); @@ -290,70 +371,86 @@ 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-staging', [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('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 () => { createBasicPR(3); - cherryPickPR(3); + mergePR(3); + cherryPickPRToStaging(3); + + // Verify output for checklist + 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-staging', '2.0.0-2-staging', [3]); + }); + + test('Merge a pull request with the checklist locked and CP it to production', async () => { + createBasicPR(5); + mergePR(5); + cherryPickPRToProduction(5); // Verify output for checklist - await assertPRsMergedBetween('1.0.0-0', '1.0.0-2', [1, 3]); + await assertPRsMergedBetween('2.0.0-0', '2.0.1-1-staging', [1, 3]); // Verify output for deploy comment - await assertPRsMergedBetween('1.0.0-1', '1.0.0-2', [3]); + await assertPRsMergedBetween('2.0.0-0', '2.0.1-0', [5]); }); - 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('1.0.0-0', '1.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('1.0.0-2', '1.0.1-0', [2]); + 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('1.0.0-2', '1.0.1-1', [2, 5]); + await assertPRsMergedBetween('2.0.0-2-staging', '2.0.2-1-staging', [2, 5, 6]); // Verify output for deploy comment - await assertPRsMergedBetween('1.0.1-0', '1.0.1-1', [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('1.0.0-2', '1.0.1-2', [2, 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('1.0.1-1', '1.0.1-2', [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} @@ -362,34 +459,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('1.0.0-2', '1.0.1-3', [2, 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('1.0.1-2', '1.0.1-3', [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"'); - cherryPickPR(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); @@ -398,65 +496,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('1.0.0-2', '1.0.1-4', [2, 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('1.0.1-4', '1.0.2-0', [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('1.0.1-4', '1.0.2-1', [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('1.0.2-0', '1.0.2-1', [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('1.0.1-4', '1.0.2-1', [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('1.0.2-1', '1.0.3-0', [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(); @@ -464,27 +562,28 @@ 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.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'}); - cherryPickPR( - 14, + mergePR(15); + cherryPickPRToStaging( + 15, () => { 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'); @@ -492,9 +591,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', [15]); // Verify PRs for the deploy checklist - await assertPRsMergedBetween('1.0.3-0', '7.0.0-0', [13, 14]); + await assertPRsMergedBetween('2.0.4-0-staging', '8.0.0-0-staging', [14, 15]); }); });