From 64dc4af66966b2e5a839d6f0a77bccfa4652472f Mon Sep 17 00:00:00 2001 From: rory Date: Wed, 23 Apr 2025 11:52:17 -0700 Subject: [PATCH 01/18] Add TARGET input to cherryPick.yml to support CP to Prod --- .github/workflows/cherryPick.yml | 64 ++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 27 deletions(-) diff --git a/.github/workflows/cherryPick.yml b/.github/workflows/cherryPick.yml index f4ae843836bc..65f3d1e2a061 100644 --- a/.github/workflows/cherryPick.yml +++ b/.github/workflows/cherryPick.yml @@ -6,11 +6,21 @@ on: PULL_REQUEST_URL: description: The full URL of the Expensify/App pull request to cherry-pick required: true + TARGET: + description: CP to staging or production? + required: true + type: choice + options: + - staging + - production + default: staging jobs: createNewVersion: uses: ./.github/workflows/createNewVersion.yml secrets: inherit + with: + SEMVER_LEVEL: ${{ inputs.TARGET == 'staging' && 'BUILD' || 'PATCH' }} cherryPick: needs: createNewVersion @@ -31,25 +41,25 @@ jobs: - name: Set conflict branch name id: getBranchName - run: echo "CONFLICT_BRANCH_NAME=cherry-pick-staging-${{ steps.getPRInfo.outputs.PR_NUMBER }}-${{ github.run_id }}-${{ github.run_attempt }}" >> "$GITHUB_OUTPUT" + run: echo "CONFLICT_BRANCH_NAME=cherry-pick-${{ inputs.TARGET }}-${{ steps.getPRInfo.outputs.PR_NUMBER }}-${{ github.run_id }}-${{ github.run_attempt }}" >> "$GITHUB_OUTPUT" - - name: Checkout staging branch with full history if cherry picking Mobile-Expensify + - name: Checkout target branch with full history if cherry picking Mobile-Expensify if: ${{ steps.getPRInfo.outputs.REPO_FULL_NAME == 'Expensify/Mobile-Expensify' }} # v4 uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 with: - ref: staging + ref: ${{ inputs.TARGET }} token: ${{ secrets.OS_BOTIFY_TOKEN }} submodules: true # Only fetch depth 0 for Mobile-Expensify, because it's a submodule and we need more history to cherry pick successfully fetch-depth: 0 - - - name: Checkout staging branch without full history if cherry picking App + + - name: Checkout target branch without full history if cherry picking App if: ${{ steps.getPRInfo.outputs.REPO_FULL_NAME == 'Expensify/App' }} # v4 uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 with: - ref: staging + ref: ${{ inputs.TARGET }} token: ${{ secrets.OS_BOTIFY_TOKEN }} submodules: true @@ -74,23 +84,23 @@ jobs: id: getPreviousVersion uses: ./.github/actions/javascript/getPreviousVersion with: - SEMVER_LEVEL: "PATCH" + SEMVER_LEVEL: ${{ inputs.TARGET == 'staging' && 'PATCH' || 'MINOR' }} - name: Fetch history of relevant refs if cherry picking an App change if: ${{ steps.getPRInfo.outputs.REPO_FULL_NAME == 'Expensify/App' }} run: | # Temporary hack during transition when -staging suffix is being added to tags if git ls-remote origin refs/tags/${{ steps.getPreviousVersion.outputs.PREVIOUS_VERSION }} | grep -q . ; then - git fetch origin main staging --no-recurse-submodules --no-tags --shallow-exclude ${{ steps.getPreviousVersion.outputs.PREVIOUS_VERSION }} + git fetch origin main ${{ inputs.TARGET }} --no-recurse-submodules --no-tags --shallow-exclude ${{ steps.getPreviousVersion.outputs.PREVIOUS_VERSION }} else - git fetch origin main staging --no-recurse-submodules --no-tags --shallow-exclude ${{ steps.getPreviousVersion.outputs.PREVIOUS_VERSION }}-staging + git fetch origin main ${{ inputs.TARGET }} --no-recurse-submodules --no-tags --shallow-exclude ${{ steps.getPreviousVersion.outputs.PREVIOUS_VERSION }}-staging fi cd Mobile-Expensify # Temporary hack during transition when -staging suffix is being added to tags if git ls-remote origin refs/tags/${{ steps.getPreviousVersion.outputs.PREVIOUS_VERSION }} | grep -q . ; then - git fetch origin main staging --no-recurse-submodules --no-tags --shallow-exclude ${{ steps.getPreviousVersion.outputs.PREVIOUS_VERSION }} + git fetch origin main ${{ inputs.TARGET }} --no-recurse-submodules --no-tags --shallow-exclude ${{ steps.getPreviousVersion.outputs.PREVIOUS_VERSION }} else - git fetch origin main staging --no-recurse-submodules --no-tags --shallow-exclude ${{ steps.getPreviousVersion.outputs.PREVIOUS_VERSION }}-staging + git fetch origin main ${{ inputs.TARGET }} --no-recurse-submodules --no-tags --shallow-exclude ${{ steps.getPreviousVersion.outputs.PREVIOUS_VERSION }}-staging fi - name: Get E/App version bump commit @@ -129,19 +139,19 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.OS_BOTIFY_TOKEN }} - - name: Cherry-pick the Mobile-Expensify version bump to Mobile-Expensify staging + - name: Cherry-pick the Mobile-Expensify version bump to Mobile-Expensify target branch working-directory: Mobile-Expensify run: | - git switch staging + git switch ${{ inputs.TARGET }} git cherry-pick -S -x --mainline 1 --strategy=recursive -Xtheirs ${{ steps.getMobileExpensifyVersionBumpCommit.outputs.VERSION_BUMP_SHA }} - git push origin staging + git push origin ${{ inputs.TARGET }} - - name: Cherry-pick the E/App version-bump to staging + - name: Cherry-pick the E/App version-bump to target branch run: | - git switch staging + git switch ${{ inputs.TARGET }} git cherry-pick -S -x --mainline 1 --strategy=recursive -Xtheirs ${{ steps.getVersionBumpCommit.outputs.VERSION_BUMP_SHA }} - - name: Update the Mobile-Expensify submodule on E/App staging + - name: Update the Mobile-Expensify submodule on E/App target branch run: | git add Mobile-Expensify git commit -m "Update Mobile-Expensify submodule version to ${{ needs.createNewVersion.outputs.NEW_VERSION }}" @@ -172,7 +182,7 @@ jobs: if [[ "${{ steps.getPRInfo.outputs.REPO_FULL_NAME }}" == "Expensify/Mobile-Expensify" ]]; then # Push Mobile-Expensify changes first cd Mobile-Expensify - git push origin staging + git push origin ${{ inputs.TARGET }} cd .. # Update and commit the submodule reference in E/App @@ -181,7 +191,7 @@ jobs: fi # Push E/App changes - git push origin staging + git push origin ${{ inputs.TARGET }} fi - name: Create Pull Request to manually finish CP @@ -191,9 +201,9 @@ jobs: AUTHOR_CHECKLIST=$(sed -n '/### PR Author Checklist/,$p' .github/PULL_REQUEST_TEMPLATE.md) PR_DESCRIPTION=$(cat <`, - text: `💥 Failed to CP ${{ github.event.inputs.PULL_REQUEST_URL }} to staging 💥`, + text: `💥 Failed to CP ${{ github.event.inputs.PULL_REQUEST_URL }} to ${{ inputs.TARGET }} 💥`, }] } env: From f5c6ff7cb5da74e339b4292b08b447d0b06191ca Mon Sep 17 00:00:00 2001 From: rory Date: Wed, 23 Apr 2025 12:01:48 -0700 Subject: [PATCH 02/18] Save draft state with subsequent version bump --- .github/workflows/cherryPick.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/.github/workflows/cherryPick.yml b/.github/workflows/cherryPick.yml index 65f3d1e2a061..ea6225cef3bd 100644 --- a/.github/workflows/cherryPick.yml +++ b/.github/workflows/cherryPick.yml @@ -20,6 +20,7 @@ jobs: uses: ./.github/workflows/createNewVersion.yml secrets: inherit with: + # In order to submit a new build for production review, it must have a higher PATCH version than the previously-submitted build SEMVER_LEVEL: ${{ inputs.TARGET == 'staging' && 'BUILD' || 'PATCH' }} cherryPick: @@ -271,3 +272,19 @@ jobs: env: GITHUB_TOKEN: ${{ github.token }} SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }} + + # FIXME: This needs to happen after the CP-to-prod build finishes + # Why is this necessary for CP-to-prod? Consider this scenario: + # 1. You close a checklist and we create a new staging version `9.0.34-0` and a new checklist + # 2. You then CP a PR to production, and in the process create `9.0.35-0` + # 3. You close the new checklist, and we try to ship `9.0.34-0` to production. This won't work, because we already submitted a higher version `9.0-35-0` + # + # To address this, we'll: + # 1. Bump the version on main again + # 2. CP that version bump to staging + createAnotherNewVersion: + if: ${{ inputs.TARGET == 'production' }} + uses: ./.github/workflows/createNewVersion.yml + secrets: inherit + with: + SEMVER_LEVEL: BUILD From d05eb6df7280f4f684ad9b633361f705e8500a86 Mon Sep 17 00:00:00 2001 From: rory Date: Wed, 23 Apr 2025 13:16:23 -0700 Subject: [PATCH 03/18] Add updated cherry suffix to CP commits --- .github/workflows/cherryPick.yml | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/.github/workflows/cherryPick.yml b/.github/workflows/cherryPick.yml index ea6225cef3bd..ff85952762c0 100644 --- a/.github/workflows/cherryPick.yml +++ b/.github/workflows/cherryPick.yml @@ -145,17 +145,19 @@ jobs: run: | git switch ${{ inputs.TARGET }} git cherry-pick -S -x --mainline 1 --strategy=recursive -Xtheirs ${{ steps.getMobileExpensifyVersionBumpCommit.outputs.VERSION_BUMP_SHA }} + git commit --amend -m "$(git log -1 --pretty=%B)" -m "(cherry-picked to ${{ inputs.TARGET }} by ${{ github.actor }})" git push origin ${{ inputs.TARGET }} + - name: Update the Mobile-Expensify submodule on E/App target branch~ + run: | + git add Mobile-Expensify + git commit -m "Update Mobile-Expensify submodule version to ${{ needs.createNewVersion.outputs.NEW_VERSION }}" + - name: Cherry-pick the E/App version-bump to target branch run: | git switch ${{ inputs.TARGET }} git cherry-pick -S -x --mainline 1 --strategy=recursive -Xtheirs ${{ steps.getVersionBumpCommit.outputs.VERSION_BUMP_SHA }} - - - name: Update the Mobile-Expensify submodule on E/App target branch - run: | - git add Mobile-Expensify - git commit -m "Update Mobile-Expensify submodule version to ${{ needs.createNewVersion.outputs.NEW_VERSION }}" + git commit --amend -m "$(git log -1 --pretty=%B)" -m "(cherry-picked to ${{ inputs.TARGET }} by ${{ github.actor }})" - name: Cherry-pick the merge commit of target PR id: cherryPick @@ -166,7 +168,7 @@ jobs: if git cherry-pick -S -x --mainline 1 ${{ steps.getCPMergeCommit.outputs.MERGE_COMMIT_SHA }}; then echo "🎉 No conflicts! CP was a success, PR can be automerged 🎉" echo "HAS_CONFLICTS=false" >> "$GITHUB_OUTPUT" - git commit --amend -m "$(git log -1 --pretty=%B)" -m "(CP triggered by ${{ github.actor }})" + git commit --amend -m "$(git log -1 --pretty=%B)" -m "(cherry-picked to ${{ inputs.TARGET }} by ${{ github.actor }})" else echo "😞 PR can't be automerged, there are merge conflicts in the following files:" git --no-pager diff --name-only --diff-filter=U @@ -215,10 +217,10 @@ jobs: Then manually resolve conflicts, and commit the change with \`git cherry-pick --continue\`. Lastly, please run: \`\`\`bash - git commit --amend -m "\$(git log -1 --pretty=%B)" -m "(CP triggered by ${{ github.actor }})" + git commit --amend -m "\$(git log -1 --pretty=%B)" -m "(cherry-picked to ${{ inputs.TARGET }} by ${{ github.actor }})" \`\`\` - That will help us keep track of who triggered this CP. Once all that's done, push your changes with \`git push origin ${{ steps.getBranchName.outputs.CONFLICT_BRANCH_NAME }}\`, and then open this PR for review. + This last part is important. It will help us keep track of who triggered this CP, and will ensure that version bumps are tracked correctly. Once all that's done, push your changes with \`git push origin ${{ steps.getBranchName.outputs.CONFLICT_BRANCH_NAME }}\`, and then open this PR for review. Note that you **must** test this PR, and both the author and reviewer checklist should be completed, just as if you were merging the PR to main. From dcbe56ba9358515834ad3e42f5ed27e1f146d056 Mon Sep 17 00:00:00 2001 From: rory Date: Wed, 23 Apr 2025 13:37:04 -0700 Subject: [PATCH 04/18] Implement regexes to test if a deploy was triggered by a cherry pick --- .../markPullRequestsAsDeployed.ts | 2 +- .github/workflows/deploy.yml | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/.github/actions/javascript/markPullRequestsAsDeployed/markPullRequestsAsDeployed.ts b/.github/actions/javascript/markPullRequestsAsDeployed/markPullRequestsAsDeployed.ts index 12e78655d7d7..54a4c6482247 100644 --- a/.github/actions/javascript/markPullRequestsAsDeployed/markPullRequestsAsDeployed.ts +++ b/.github/actions/javascript/markPullRequestsAsDeployed/markPullRequestsAsDeployed.ts @@ -140,7 +140,7 @@ async function run() { }); const prNumForCPMergeCommit = commit.message.match(/Merge pull request #(\d+)[\S\s]*\(cherry picked from commit .*\)/); if (prNumForCPMergeCommit?.at(1) === String(prNumber)) { - const cpActor = commit.message.match(/.*\(CP triggered by (.*)\)/)?.at(1); + const cpActor = commit.message.match(/.*\(cherry-picked to .* by (.*)\)/)?.at(1); if (cpActor) { deployer = cpActor; } diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index b7386da375b4..5ffdd1cf77e9 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -18,6 +18,8 @@ jobs: outputs: APP_VERSION: ${{ steps.getAppVersion.outputs.VERSION }} TAG: ${{ steps.getTagName.outputs.TAG }} + # Is this deploy for a cherry-pick? + IS_CHERRY_PICK: ${{ steps.isCherryPick.outputs.IS_CHERRY_PICK }} steps: - name: Checkout # v4 @@ -57,6 +59,19 @@ jobs: git tag ${{ steps.getTagName.outputs.TAG }} git push origin --tags + # We use JS here instead of bash/jq because inlining potentially large json into a bash command is non-trivial. + # JS is better at handling JSON: https://stackoverflow.com/questions/72953526/github-actions-how-to-pass-tojson-result-to-shell-commands + - name: Check if this deploy was triggered by a cherry-pick + id: isCherryPick + uses: actions/github-script@e7aeb8c663f696059ebb5f9ab1425ed2ef511bdb + with: + script: | + const commitMessages = github.context.payload.commits.map((commit) => commit.message); + core.setOutput( + 'IS_CHERRY_PICK', + commitMessages.some((message) => /.*\(cherry-picked to .* by .*\)$/.test(message)), + ); + # Note: we're updating the checklist before running the deploys and assuming that it will succeed on at least one platform deployChecklist: name: Create or update deploy checklist From 3db98a612e981b49a178d428562cb70c7eaf7788 Mon Sep 17 00:00:00 2001 From: rory Date: Wed, 23 Apr 2025 13:53:29 -0700 Subject: [PATCH 05/18] Bump version again after CP-to-prod in deploy.yml --- .github/workflows/cherryPick.yml | 20 +----- .github/workflows/deploy.yml | 110 +++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+), 18 deletions(-) diff --git a/.github/workflows/cherryPick.yml b/.github/workflows/cherryPick.yml index ff85952762c0..30edb6e624de 100644 --- a/.github/workflows/cherryPick.yml +++ b/.github/workflows/cherryPick.yml @@ -148,14 +148,14 @@ jobs: git commit --amend -m "$(git log -1 --pretty=%B)" -m "(cherry-picked to ${{ inputs.TARGET }} by ${{ github.actor }})" git push origin ${{ inputs.TARGET }} - - name: Update the Mobile-Expensify submodule on E/App target branch~ + - name: Update the Mobile-Expensify submodule on E/App target branch run: | + git switch ${{ inputs.TARGET }} git add Mobile-Expensify git commit -m "Update Mobile-Expensify submodule version to ${{ needs.createNewVersion.outputs.NEW_VERSION }}" - name: Cherry-pick the E/App version-bump to target branch run: | - git switch ${{ inputs.TARGET }} git cherry-pick -S -x --mainline 1 --strategy=recursive -Xtheirs ${{ steps.getVersionBumpCommit.outputs.VERSION_BUMP_SHA }} git commit --amend -m "$(git log -1 --pretty=%B)" -m "(cherry-picked to ${{ inputs.TARGET }} by ${{ github.actor }})" @@ -274,19 +274,3 @@ jobs: env: GITHUB_TOKEN: ${{ github.token }} SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }} - - # FIXME: This needs to happen after the CP-to-prod build finishes - # Why is this necessary for CP-to-prod? Consider this scenario: - # 1. You close a checklist and we create a new staging version `9.0.34-0` and a new checklist - # 2. You then CP a PR to production, and in the process create `9.0.35-0` - # 3. You close the new checklist, and we try to ship `9.0.34-0` to production. This won't work, because we already submitted a higher version `9.0-35-0` - # - # To address this, we'll: - # 1. Bump the version on main again - # 2. CP that version bump to staging - createAnotherNewVersion: - if: ${{ inputs.TARGET == 'production' }} - uses: ./.github/workflows/createNewVersion.yml - secrets: inherit - with: - SEMVER_LEVEL: BUILD diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 5ffdd1cf77e9..2bf2d5284d35 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -685,6 +685,116 @@ jobs: GITHUB_TOKEN: ${{ github.token }} SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }} + # Why is this necessary for CP-to-prod? Consider this scenario: + # 1. You close a checklist and we create a new staging version `9.0.34-0` and a new checklist + # 2. You then CP a PR to production, and in the process create `9.0.35-0` + # 3. You close the new checklist, and we try to ship `9.0.34-0` to production. This won't work, because we already submitted a higher version `9.0-35-0` + # + # To address this, we'll: + # 1. Bump the version on main again + # 2. CP that version bump to staging + createNewVersion: + needs: [prep, checkDeploymentSuccess] + if: ${{ always() && fromJSON(needs.checkDeploymentSuccess.outputs.IS_AT_LEAST_ONE_PLATFORM_DEPLOYED) && github.ref == 'refs/heads/production' && fromJSON(needs.prep.outputs.IS_CHERRY_PICK) }} + uses: ./.github/workflows/createNewVersion.yml + secrets: inherit + with: + SEMVER_LEVEL: BUILD + + cherryPickVersionBump: + needs: [createNewVersion] + runs-on: ubuntu-latest + steps: + # v4.2.2 + - name: Checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + ref: staging + token: ${{ secrets.OS_BOTIFY_TOKEN }} + submodules: true + + # This command is necessary to fetch any branch other than main in the submodule. + # See https://github.com/actions/checkout/issues/1815#issuecomment-2777836442 for further context. + - name: Enable branch-switching in submodules + run: | + git submodule foreach '\ + git config --add remote.origin.fetch "+refs/heads/staging:refs/remotes/origin/staging" && \ + git config --add remote.origin.fetch "+refs/heads/production:refs/remotes/origin/production"' + + - name: Set up git for OSBotify + id: setupGitForOSBotify + uses: Expensify/GitHub-Actions/setupGitForOSBotify@main + with: + OP_VAULT: ${{ vars.OP_VAULT }} + OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }} + OS_BOTIFY_APP_ID: ${{ secrets.OS_BOTIFY_APP_ID }} + OS_BOTIFY_PRIVATE_KEY: ${{ secrets.OS_BOTIFY_PRIVATE_KEY }} + + - name: Get previous app version + id: getPreviousVersion + uses: ./.github/actions/javascript/getPreviousVersion + with: + SEMVER_LEVEL: 'PATCH' + + - name: Fetch history of relevant refs + run: | + git fetch origin main staging --no-recurse-submodules --no-tags --shallow-exclude ${{ steps.getPreviousVersion.outputs.PREVIOUS_VERSION }} + cd Mobile-Expensify + git fetch origin main staging --no-recurse-submodules --no-tags --shallow-exclude ${{ steps.getPreviousVersion.outputs.PREVIOUS_VERSION }} + + - name: Get E/App version bump commit + id: getVersionBumpCommit + run: | + git switch main + VERSION_BUMP_COMMIT="$(git log -1 --format='%H' --author='OSBotify' --grep 'Update version to ${{ needs.createNewVersion.outputs.NEW_VERSION }}')" + if [ -z "$VERSION_BUMP_COMMIT" ]; then + echo "::error::❌ Could not find E/App version bump commit for ${{ needs.createNewVersion.outputs.NEW_VERSION }}" + git log --oneline + else + echo "::notice::👀 Found E/App version bump commit $VERSION_BUMP_COMMIT" + fi + echo "VERSION_BUMP_SHA=$VERSION_BUMP_COMMIT" >> "$GITHUB_OUTPUT" + + - name: Get Mobile-Expensify version bump commit + id: getMobileExpensifyVersionBumpCommit + working-directory: Mobile-Expensify + run: | + git switch main + VERSION_BUMP_COMMIT="$(git log -1 --format='%H' --author='OSBotify' --grep 'Update version to ${{ needs.createNewVersion.outputs.NEW_VERSION }}')" + if [ -z "$VERSION_BUMP_COMMIT" ]; then + echo "::error::❌ Could not find Mobile-Expensify version bump commit for ${{ needs.createNewVersion.outputs.NEW_VERSION }}" + git log --oneline + else + echo "::notice::👀 Found Mobile-Expensify version bump commit $VERSION_BUMP_COMMIT" + fi + echo "VERSION_BUMP_SHA=$VERSION_BUMP_COMMIT" >> "$GITHUB_OUTPUT" + + - name: Cherry-pick the Mobile-Expensify version bump to Mobile-Expensify staging + working-directory: Mobile-Expensify + run: | + git switch staging + git cherry-pick -S -x --mainline 1 --strategy=recursive -Xtheirs ${{ steps.getMobileExpensifyVersionBumpCommit.outputs.VERSION_BUMP_SHA }} + git commit --amend -m "$(git log -1 --pretty=%B)" -m "(cherry-picked to staging by ${{ github.actor }})" + git push origin staging + + - name: Update the Mobile-Expensify submodule on E/App staging + run: | + git switch staging + git add Mobile-Expensify + git commit -m "Update Mobile-Expensify submodule version to ${{ needs.createNewVersion.outputs.NEW_VERSION }}" + + - name: Cherry-pick the E/App version-bump to target branch + run: | + git cherry-pick -S -x --mainline 1 --strategy=recursive -Xtheirs ${{ steps.getVersionBumpCommit.outputs.VERSION_BUMP_SHA }} + git commit --amend -m "$(git log -1 --pretty=%B)" -m "(cherry-picked to ${{ inputs.TARGET }} by ${{ github.actor }})" + git push origin staging + + updateChecklistWithVersionBump: + name: Update deploy checklist with the new version bump + needs: cherryPickVersionBump + uses: ./.github/workflows/createDeployChecklist.yml + secrets: inherit + postSlackMessageOnSuccess: name: Post a Slack message when all platforms deploy successfully runs-on: ubuntu-latest From 82c7190b3fb19d63e335d186638b494e2e847042 Mon Sep 17 00:00:00 2001 From: rory Date: Wed, 23 Apr 2025 14:05:09 -0700 Subject: [PATCH 06/18] Reduce diff --- .github/workflows/cherryPick.yml | 12 ++++++------ .github/workflows/deploy.yml | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/cherryPick.yml b/.github/workflows/cherryPick.yml index 30edb6e624de..0e5d61a37ba7 100644 --- a/.github/workflows/cherryPick.yml +++ b/.github/workflows/cherryPick.yml @@ -148,17 +148,17 @@ jobs: git commit --amend -m "$(git log -1 --pretty=%B)" -m "(cherry-picked to ${{ inputs.TARGET }} by ${{ github.actor }})" git push origin ${{ inputs.TARGET }} - - name: Update the Mobile-Expensify submodule on E/App target branch - run: | - git switch ${{ inputs.TARGET }} - git add Mobile-Expensify - git commit -m "Update Mobile-Expensify submodule version to ${{ needs.createNewVersion.outputs.NEW_VERSION }}" - - name: Cherry-pick the E/App version-bump to target branch run: | + git switch ${{ inputs.TARGET }} git cherry-pick -S -x --mainline 1 --strategy=recursive -Xtheirs ${{ steps.getVersionBumpCommit.outputs.VERSION_BUMP_SHA }} git commit --amend -m "$(git log -1 --pretty=%B)" -m "(cherry-picked to ${{ inputs.TARGET }} by ${{ github.actor }})" + - name: Update the Mobile-Expensify submodule on E/App target branch + run: | + git add Mobile-Expensify + git commit -m "Update Mobile-Expensify submodule version to ${{ needs.createNewVersion.outputs.NEW_VERSION }}" + - name: Cherry-pick the merge commit of target PR id: cherryPick # If cherry picking a Mobile-Expensify change, we need to run the cherry pick in the Mobile-Expensify directory diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 2bf2d5284d35..0b36afbda5b7 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -777,18 +777,18 @@ jobs: git commit --amend -m "$(git log -1 --pretty=%B)" -m "(cherry-picked to staging by ${{ github.actor }})" git push origin staging - - name: Update the Mobile-Expensify submodule on E/App staging - run: | - git switch staging - git add Mobile-Expensify - git commit -m "Update Mobile-Expensify submodule version to ${{ needs.createNewVersion.outputs.NEW_VERSION }}" - - name: Cherry-pick the E/App version-bump to target branch run: | + git switch staging git cherry-pick -S -x --mainline 1 --strategy=recursive -Xtheirs ${{ steps.getVersionBumpCommit.outputs.VERSION_BUMP_SHA }} git commit --amend -m "$(git log -1 --pretty=%B)" -m "(cherry-picked to ${{ inputs.TARGET }} by ${{ github.actor }})" git push origin staging + - name: Update the Mobile-Expensify submodule on E/App staging + run: | + git add Mobile-Expensify + git commit -m "Update Mobile-Expensify submodule version to ${{ needs.createNewVersion.outputs.NEW_VERSION }}" + updateChecklistWithVersionBump: name: Update deploy checklist with the new version bump needs: cherryPickVersionBump From 8b12deb18cb039daffcc1f61d4fd62879dbd39fd Mon Sep 17 00:00:00 2001 From: rory Date: Mon, 28 Apr 2025 17:43:37 -0700 Subject: [PATCH 07/18] Wrap label in quotes --- .github/workflows/cherryPick.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cherryPick.yml b/.github/workflows/cherryPick.yml index c5c8a4ad2bee..9474d581ae3b 100644 --- a/.github/workflows/cherryPick.yml +++ b/.github/workflows/cherryPick.yml @@ -242,7 +242,7 @@ jobs: continue-on-error: true - name: Label original PR with CP label - run: gh pr edit ${{ github.event.inputs.PULL_REQUEST_URL }} --add-label ${{ inputs.TARGET == 'staging' && 'CP Staging' || 'CP Production' }} + run: gh pr edit ${{ github.event.inputs.PULL_REQUEST_URL }} --add-label '${{ inputs.TARGET == 'staging' && 'CP Staging' || 'CP Production' }}' env: GITHUB_TOKEN: ${{ secrets.OS_BOTIFY_TOKEN }} From 9d132ab236b331c261ab2d86158d360995cb82f6 Mon Sep 17 00:00:00 2001 From: rory Date: Mon, 28 Apr 2025 17:51:24 -0700 Subject: [PATCH 08/18] Drop the github. before context --- .github/workflows/deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 0b36afbda5b7..831d9fa97e30 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -66,7 +66,7 @@ jobs: uses: actions/github-script@e7aeb8c663f696059ebb5f9ab1425ed2ef511bdb with: script: | - const commitMessages = github.context.payload.commits.map((commit) => commit.message); + const commitMessages = context.payload.commits.map((commit) => commit.message); core.setOutput( 'IS_CHERRY_PICK', commitMessages.some((message) => /.*\(cherry-picked to .* by .*\)$/.test(message)), From 165b80ab62b563ad89ec9dc80332afbe09832b9b Mon Sep 17 00:00:00 2001 From: rory Date: Mon, 28 Apr 2025 18:05:40 -0700 Subject: [PATCH 09/18] Log isCherryPick --- .github/workflows/deploy.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 831d9fa97e30..eece72bdbaba 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -67,9 +67,11 @@ jobs: with: script: | const commitMessages = context.payload.commits.map((commit) => commit.message); + const isCherryPick = commitMessages.some((message) => /.*\(cherry-picked to .* by .*\)$/.test(message)); + console.log('Is cherry pick?', isCherryPick); core.setOutput( 'IS_CHERRY_PICK', - commitMessages.some((message) => /.*\(cherry-picked to .* by .*\)$/.test(message)), + isCherryPick, ); # Note: we're updating the checklist before running the deploys and assuming that it will succeed on at least one platform From c44f0bf56aa3b02fbb5adf400be1d73ef1bba377 Mon Sep 17 00:00:00 2001 From: rory Date: Mon, 28 Apr 2025 18:46:15 -0700 Subject: [PATCH 10/18] Add build steps for cherryPick prod --- .github/workflows/deploy.yml | 62 +++++++++++++++++------------------- 1 file changed, 29 insertions(+), 33 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index eece72bdbaba..ae14d6a279d0 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -5,7 +5,6 @@ on: branches: [staging, production] env: - SHOULD_DEPLOY_PRODUCTION: ${{ github.ref == 'refs/heads/production' }} IS_APP_REPO: ${{ github.repository == 'Expensify/App' }} concurrency: @@ -49,7 +48,7 @@ jobs: - name: Get tag id: getTagName - run: echo "TAG=${{ fromJSON(env.SHOULD_DEPLOY_PRODUCTION) && steps.getAppVersion.outputs.VERSION || format('{0}-staging', steps.getAppVersion.outputs.VERSION) }}" >> "$GITHUB_OUTPUT" + run: echo "TAG=${{ github.ref == 'refs/heads/production' && steps.getAppVersion.outputs.VERSION || format('{0}-staging', steps.getAppVersion.outputs.VERSION) }}" >> "$GITHUB_OUTPUT" - name: Create and push tag run: | @@ -156,7 +155,7 @@ jobs: run: echo "VERSION_CODE=$(grep -oP 'android:versionCode="\K[0-9]+' Mobile-Expensify/Android/AndroidManifest.xml)" >> "$GITHUB_OUTPUT" - name: Build Android app - if: ${{ !fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }} + if: ${{ github.ref == 'refs/heads/staging' || fromJSON(needs.prep.outputs.IS_CHERRY_PICK) }} run: bundle exec fastlane android build_hybrid env: ANDROID_UPLOAD_KEYSTORE_PASSWORD: ${{ steps.load-credentials.outputs.ANDROID_UPLOAD_KEYSTORE_PASSWORD }} @@ -165,14 +164,14 @@ jobs: ANDROID_BUILD_TYPE: ${{ vars.ANDROID_BUILD_TYPE }} - name: Upload Android app to Google Play - if: ${{ !fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }} + if: ${{ github.ref == 'refs/heads/staging' || fromJSON(needs.prep.outputs.IS_CHERRY_PICK) }} run: bundle exec fastlane android ${{ vars.ANDROID_UPLOAD_COMMAND }} env: VERSION: ${{ steps.getAndroidVersion.outputs.VERSION_CODE }} ANDROID_PACKAGE_NAME: ${{ vars.ANDROID_PACKAGE_NAME }} - name: Get current Android rollout percentage - if: ${{ fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }} + if: ${{ github.ref == 'refs/heads/production' }} id: getAndroidRolloutPercentage uses: ./.github/actions/javascript/getAndroidRolloutPercentage with: @@ -180,7 +179,7 @@ jobs: PACKAGE_NAME: org.me.mobiexpensifyg - name: Submit production build for Google Play review and a slow rollout - if: ${{ fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }} + if: ${{ github.ref == 'refs/heads/production' }} run: | # Complete the previous version rollout if the current rollout percentage is not -1 (no rollout in progress) or 1 (fully rolled out) echo "Current rollout percentage: ${{ steps.getAndroidRolloutPercentage.outputs.CURRENT_ROLLOUT_PERCENTAGE }}" @@ -197,7 +196,7 @@ jobs: VERSION: ${{ steps.getAndroidVersion.outputs.VERSION_CODE }} - name: Upload Android build to Browser Stack - if: ${{ fromJSON(env.IS_APP_REPO) && !fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }} + if: ${{ fromJSON(env.IS_APP_REPO) && (github.ref == 'refs/heads/staging' || fromJSON(needs.prep.outputs.IS_CHERRY_PICK)) }} run: curl -u "$BROWSERSTACK" -X POST "https://api-cloud.browserstack.com/app-live/upload" -F "file=@${{ env.aabPath }}" env: BROWSERSTACK: ${{ secrets.BROWSERSTACK }} @@ -217,7 +216,7 @@ jobs: unzip -p Expensify.apks universal.apk > Expensify.apk - name: Upload Android APK build artifact - if: ${{ !fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }} + if: ${{ github.ref == 'refs/heads/staging' || fromJSON(needs.prep.outputs.IS_CHERRY_PICK) }} # v4 uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 with: @@ -225,7 +224,7 @@ jobs: path: Expensify.apk - name: Upload Android build artifact - if: ${{ !fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }} + if: ${{ github.ref == 'refs/heads/staging' || fromJSON(needs.prep.outputs.IS_CHERRY_PICK) }} # v4 uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 with: @@ -233,7 +232,7 @@ jobs: path: ${{ env.aabPath }} - name: Upload Android sourcemap artifact - if: ${{ !fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }} + if: ${{ github.ref == 'refs/heads/staging' || fromJSON(needs.prep.outputs.IS_CHERRY_PICK) }} # v4 uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 with: @@ -244,7 +243,7 @@ jobs: run: echo "VERSION=$(npm run print-version --silent)" >> "$GITHUB_ENV" - name: Warn deployers if Android production deploy failed - if: ${{ failure() && fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }} + if: ${{ failure() && github.ref == 'refs/heads/production' }} # v3 uses: 8398a7/action-slack@1750b5085f3ec60384090fb7c52965ef822e869e with: @@ -288,7 +287,7 @@ jobs: DESKTOP_CERTIFICATE_PASSWORD: "op://${{ vars.OP_VAULT }}/Desktop Certificates.p12/CSC_KEY_PASSWORD" - name: Build desktop app - run: ${{ env.SHOULD_DEPLOY_PRODUCTION == 'true' && 'npm run desktop-build' || 'npm run desktop-build-staging' }} + run: ${{ github.ref == 'refs/heads/production' && 'npm run desktop-build' || 'npm run desktop-build-staging' }} env: CSC_LINK: ${{ steps.load-credentials.outputs.DESKTOP_CERTIFICATE_BASE64 }} CSC_KEY_PASSWORD: ${{ steps.load-credentials.outputs.DESKTOP_CERTIFICATE_PASSWORD }} @@ -298,7 +297,7 @@ jobs: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} GCP_GEOLOCATION_API_KEY: ${{ secrets.GCP_GEOLOCATION_API_KEY_PRODUCTION }} - S3_BUCKET: ${{ env.SHOULD_DEPLOY_PRODUCTION == 'true' && vars.PRODUCTION_S3_BUCKET || vars.STAGING_S3_BUCKET }} + S3_BUCKET: ${{ github.ref == 'refs/heads/production' && vars.PRODUCTION_S3_BUCKET || vars.STAGING_S3_BUCKET }} - name: Upload desktop sourcemaps artifact # v4 @@ -329,8 +328,7 @@ jobs: token: ${{ secrets.OS_BOTIFY_TOKEN }} - name: Configure MapBox SDK - run: | - ./scripts/setup-mapbox-sdk.sh ${{ secrets.MAPBOX_SDK_DOWNLOAD_TOKEN }} + run: ./scripts/setup-mapbox-sdk.sh ${{ secrets.MAPBOX_SDK_DOWNLOAD_TOKEN }} - name: Setup Node id: setup-node @@ -390,7 +388,7 @@ jobs: run: echo "IOS_VERSION=$(echo '${{ needs.prep.outputs.APP_VERSION }}' | tr '-' '.')" >> "$GITHUB_OUTPUT" - name: Build iOS HybridApp - if: ${{ !fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }} + if: ${{ github.ref == 'refs/heads/staging' || fromJSON(needs.prep.outputs.IS_CHERRY_PICK) }} run: bundle exec fastlane ios build_hybrid env: APPLE_ID: ${{ vars.APPLE_ID }} @@ -402,7 +400,7 @@ jobs: APPLE_NOTIFICATION_PROVISIONING_PROFILE_NAME: ${{ vars.APPLE_NOTIFICATION_PROVISIONING_PROFILE_NAME }} - name: Upload release build to TestFlight - if: ${{ !fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }} + if: ${{ github.ref == 'refs/heads/staging' || fromJSON(needs.prep.outputs.IS_CHERRY_PICK) }} run: bundle exec fastlane ios upload_testflight_hybrid env: APPLE_CONTACT_EMAIL: ${{ secrets.APPLE_CONTACT_EMAIL }} @@ -412,11 +410,11 @@ jobs: APPLE_ID: ${{ vars.APPLE_ID }} - name: Upload DSYMs to Firebase for HybridApp - if: ${{ fromJSON(env.IS_APP_REPO) && !fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }} + if: ${{ fromJSON(env.IS_APP_REPO) && (github.ref == 'refs/heads/staging' || fromJSON(needs.prep.outputs.IS_CHERRY_PICK)) }} run: bundle exec fastlane ios upload_dsyms_hybrid - name: Submit production build for App Store review and a slow rollout - if: ${{ fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }} + if: ${{ github.ref == 'refs/heads/production' }} run: | # Complete the previous version rollout bundle exec fastlane ios complete_hybrid_rollout @@ -428,13 +426,13 @@ jobs: APPLE_ID: ${{ vars.APPLE_ID }} - name: Upload iOS build to Browser Stack - if: ${{ fromJSON(env.IS_APP_REPO) && !fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }} + if: ${{ fromJSON(env.IS_APP_REPO) && (github.ref == 'refs/heads/staging' || fromJSON(needs.prep.outputs.IS_CHERRY_PICK)) }} run: curl -u "$BROWSERSTACK" -X POST "https://api-cloud.browserstack.com/app-live/upload" -F "file=@${{ env.ipaPath }}" env: BROWSERSTACK: ${{ secrets.BROWSERSTACK }} - name: Upload iOS build artifact - if: ${{ !fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }} + if: ${{ github.ref == 'refs/heads/staging' || fromJSON(needs.prep.outputs.IS_CHERRY_PICK) }} # v4 uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 with: @@ -442,7 +440,7 @@ jobs: path: ${{ env.ipaPath }} - name: Upload iOS sourcemap artifact - if: ${{ !fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }} + if: ${{ github.ref == 'refs/heads/staging' || fromJSON(needs.prep.outputs.IS_CHERRY_PICK) }} # v4 uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 with: @@ -450,7 +448,7 @@ jobs: path: /Users/runner/work/App/App/Mobile-Expensify/main.jsbundle.map - name: Warn deployers if iOS production deploy failed - if: ${{ failure() && fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }} + if: ${{ failure() && github.ref == 'refs/heads/production' }} # v3 uses: 8398a7/action-slack@1750b5085f3ec60384090fb7c52965ef822e869e with: @@ -493,7 +491,7 @@ jobs: - name: Build web run: | - if [[ ${{ env.SHOULD_DEPLOY_PRODUCTION }} == 'true' ]]; then + if [[ '${{ github.ref }}' == 'refs/heads/production' ]]; then npm run build else npm run build-staging @@ -502,7 +500,7 @@ jobs: - name: Build storybook docs continue-on-error: true run: | - if [[ ${{ env.SHOULD_DEPLOY_PRODUCTION }} == 'true' ]]; then + if [[ ${{ github.ref }} == 'refs/heads/production' ]]; then npm run storybook-build else npm run storybook-build-staging @@ -514,22 +512,20 @@ jobs: aws s3 cp --acl public-read --content-type 'application/json' --metadata-directive REPLACE ${{ env.S3_URL }}/.well-known/apple-app-site-association ${{ env.S3_URL }}/.well-known/apple-app-site-association aws s3 cp --acl public-read --content-type 'application/json' --metadata-directive REPLACE ${{ env.S3_URL }}/.well-known/apple-app-site-association ${{env.S3_URL }}/apple-app-site-association env: - S3_URL: s3://${{ env.SHOULD_DEPLOY_PRODUCTION != 'true' && 'staging-' || '' }}${{ vars.PRODUCTION_S3_BUCKET }} + S3_URL: s3://${{ github.ref == 'refs/heads/staging' && 'staging-' || '' }}${{ vars.PRODUCTION_S3_BUCKET }} - name: Purge Cloudflare cache run: | /home/runner/.local/bin/cli4 --verbose --delete hosts=["$HOST"] /zones/:9ee042e6cfc7fd45e74aa7d2f78d617b/purge_cache env: CF_API_KEY: ${{ secrets.CLOUDFLARE_TOKEN }} - SHOULD_DEPLOY_PRODUCTION: ${{ env.SHOULD_DEPLOY_PRODUCTION }} - HOST: ${{ env.SHOULD_DEPLOY_PRODUCTION == 'true' && vars.WEB_PRODUCTION_HOST || vars.WEB_STAGING_HOST }} + HOST: ${{ github.ref == 'refs/heads/production' && vars.WEB_PRODUCTION_HOST || vars.WEB_STAGING_HOST }} - name: Verify deploy run: | ./.github/scripts/verifyDeploy.sh "$HOST" "${{ needs.prep.outputs.APP_VERSION }}" env: - SHOULD_DEPLOY_PRODUCTION: ${{ env.SHOULD_DEPLOY_PRODUCTION }} - HOST: ${{ env.SHOULD_DEPLOY_PRODUCTION == 'true' && vars.WEB_PRODUCTION_HOST || vars.WEB_STAGING_HOST }} + HOST: ${{ github.ref == 'refs/heads/production' && vars.WEB_PRODUCTION_HOST || vars.WEB_STAGING_HOST }} - name: Upload web sourcemaps artifact # v4 @@ -813,7 +809,7 @@ jobs: channel: '#announce', attachments: [{ color: 'good', - text: `🎉️ Successfully deployed ${process.env.AS_REPO} to ${{ fromJSON(env.SHOULD_DEPLOY_PRODUCTION) && 'production' || 'staging' }} 🎉️`, + text: `🎉️ Successfully deployed ${process.env.AS_REPO} to ${{ github.ref == 'refs/heads/production' && 'production' || 'staging' }} 🎉️`, }] } env: @@ -830,7 +826,7 @@ jobs: channel: '#deployer', attachments: [{ color: 'good', - text: `🎉️ Successfully deployed ${process.env.AS_REPO} to ${{ fromJSON(env.SHOULD_DEPLOY_PRODUCTION) && 'production' || 'staging' }} 🎉️`, + text: `🎉️ Successfully deployed ${process.env.AS_REPO} to ${{ github.ref == 'refs/heads/production' && 'production' || 'staging' }} 🎉️`, }] } env: @@ -840,7 +836,7 @@ jobs: - name: 'Announces a production deploy in the #expensify-open-source Slack room' # v3 uses: 8398a7/action-slack@1750b5085f3ec60384090fb7c52965ef822e869e - if: ${{ fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }} + if: ${{ github.ref == 'refs/heads/production' }} with: status: custom custom_payload: | From 95dbe3d98b1b47ca86496af555c04e3ab128481d Mon Sep 17 00:00:00 2001 From: rory Date: Mon, 28 Apr 2025 20:11:20 -0700 Subject: [PATCH 11/18] Add conditionals to the cherryPickVersionBump and updateChecklistWithVersionBump --- .github/workflows/deploy.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index ae14d6a279d0..bc8d018e97c6 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -701,6 +701,7 @@ jobs: cherryPickVersionBump: needs: [createNewVersion] + if: ${{ always() && fromJSON(needs.checkDeploymentSuccess.outputs.IS_AT_LEAST_ONE_PLATFORM_DEPLOYED) && github.ref == 'refs/heads/production' && fromJSON(needs.prep.outputs.IS_CHERRY_PICK) }} runs-on: ubuntu-latest steps: # v4.2.2 @@ -790,6 +791,7 @@ jobs: updateChecklistWithVersionBump: name: Update deploy checklist with the new version bump needs: cherryPickVersionBump + if: ${{ always() && fromJSON(needs.checkDeploymentSuccess.outputs.IS_AT_LEAST_ONE_PLATFORM_DEPLOYED) && github.ref == 'refs/heads/production' && fromJSON(needs.prep.outputs.IS_CHERRY_PICK) }} uses: ./.github/workflows/createDeployChecklist.yml secrets: inherit From 0a779f59c7e06216b99400bbe68eacb049381053 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Mon, 28 Apr 2025 20:25:52 -0700 Subject: [PATCH 12/18] rebuild markPullRequestAsDeployed action --- .github/actions/javascript/markPullRequestsAsDeployed/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/javascript/markPullRequestsAsDeployed/index.js b/.github/actions/javascript/markPullRequestsAsDeployed/index.js index 61676d9487db..c3512170ba10 100644 --- a/.github/actions/javascript/markPullRequestsAsDeployed/index.js +++ b/.github/actions/javascript/markPullRequestsAsDeployed/index.js @@ -12787,7 +12787,7 @@ async function run() { }); const prNumForCPMergeCommit = commit.message.match(/Merge pull request #(\d+)[\S\s]*\(cherry picked from commit .*\)/); if (prNumForCPMergeCommit?.at(1) === String(prNumber)) { - const cpActor = commit.message.match(/.*\(CP triggered by (.*)\)/)?.at(1); + const cpActor = commit.message.match(/.*\(cherry-picked to .* by (.*)\)/)?.at(1); if (cpActor) { deployer = cpActor; } From ce26bc3ed8453cd398e09e265accef073bff35ed Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Mon, 28 Apr 2025 20:27:35 -0700 Subject: [PATCH 13/18] update test case based on updated regex --- tests/unit/markPullRequestsAsDeployedTest.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/unit/markPullRequestsAsDeployedTest.ts b/tests/unit/markPullRequestsAsDeployedTest.ts index 3915b213892a..785c174934f6 100644 --- a/tests/unit/markPullRequestsAsDeployedTest.ts +++ b/tests/unit/markPullRequestsAsDeployedTest.ts @@ -265,10 +265,7 @@ platform | result if (commit_sha === 'xyz') { return { data: { - message: `Merge pull request #3 blahblahblah -(cherry picked from commit dagdag) -(CP triggered by freyja)`, - committer: {name: 'freyja'}, + message: `Merge pull request #3 blahblahblah\\n(cherry picked from commit dagdag)\\n(cherry-picked to staging by freyja)`, }, }; } From 591a8567ffe7708592258125506c181ec8ad9e1d Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Mon, 28 Apr 2025 20:47:02 -0700 Subject: [PATCH 14/18] add missing dependencies --- .github/workflows/deploy.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index bc8d018e97c6..e265bf0a402e 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -700,7 +700,7 @@ jobs: SEMVER_LEVEL: BUILD cherryPickVersionBump: - needs: [createNewVersion] + needs: [createNewVersion, checkDeploymentSuccess, prep] if: ${{ always() && fromJSON(needs.checkDeploymentSuccess.outputs.IS_AT_LEAST_ONE_PLATFORM_DEPLOYED) && github.ref == 'refs/heads/production' && fromJSON(needs.prep.outputs.IS_CHERRY_PICK) }} runs-on: ubuntu-latest steps: @@ -790,7 +790,10 @@ jobs: updateChecklistWithVersionBump: name: Update deploy checklist with the new version bump - needs: cherryPickVersionBump + needs: + - cherryPickVersionBump + - checkDeploymentSuccess + - prep if: ${{ always() && fromJSON(needs.checkDeploymentSuccess.outputs.IS_AT_LEAST_ONE_PLATFORM_DEPLOYED) && github.ref == 'refs/heads/production' && fromJSON(needs.prep.outputs.IS_CHERRY_PICK) }} uses: ./.github/workflows/createDeployChecklist.yml secrets: inherit From 97d0beca306c8bc543a24c57a4c6819289b9e48a Mon Sep 17 00:00:00 2001 From: rory Date: Mon, 28 Apr 2025 20:51:28 -0700 Subject: [PATCH 15/18] Hardcode staging --- .github/workflows/deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index e265bf0a402e..1f3348c2e98b 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -780,7 +780,7 @@ jobs: run: | git switch staging git cherry-pick -S -x --mainline 1 --strategy=recursive -Xtheirs ${{ steps.getVersionBumpCommit.outputs.VERSION_BUMP_SHA }} - git commit --amend -m "$(git log -1 --pretty=%B)" -m "(cherry-picked to ${{ inputs.TARGET }} by ${{ github.actor }})" + git commit --amend -m "$(git log -1 --pretty=%B)" -m "(cherry-picked to staging by ${{ github.actor }})" git push origin staging - name: Update the Mobile-Expensify submodule on E/App staging From 80ae7eedb8791422051c0ae600d69859c62480c3 Mon Sep 17 00:00:00 2001 From: rory Date: Mon, 28 Apr 2025 20:56:22 -0700 Subject: [PATCH 16/18] Fix array style --- .github/workflows/deploy.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 1f3348c2e98b..a2d5d8e73a28 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -790,10 +790,7 @@ jobs: updateChecklistWithVersionBump: name: Update deploy checklist with the new version bump - needs: - - cherryPickVersionBump - - checkDeploymentSuccess - - prep + needs: [prep, checkDeploymentSuccess, cherryPickVersionBump] if: ${{ always() && fromJSON(needs.checkDeploymentSuccess.outputs.IS_AT_LEAST_ONE_PLATFORM_DEPLOYED) && github.ref == 'refs/heads/production' && fromJSON(needs.prep.outputs.IS_CHERRY_PICK) }} uses: ./.github/workflows/createDeployChecklist.yml secrets: inherit From b5b2cfb5c34fec3773f0fba682f06ebe65b63f26 Mon Sep 17 00:00:00 2001 From: rory Date: Tue, 29 Apr 2025 09:36:25 -0700 Subject: [PATCH 17/18] Make cherryPick work for Mobile-Expensify-Test-Fork too --- .github/workflows/cherryPick.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/cherryPick.yml b/.github/workflows/cherryPick.yml index 9474d581ae3b..0642c79b0435 100644 --- a/.github/workflows/cherryPick.yml +++ b/.github/workflows/cherryPick.yml @@ -35,7 +35,7 @@ jobs: - name: Verify repository run: | - if [[ "${{ steps.getPRInfo.outputs.REPO_FULL_NAME }}" != ${{ github.repository }} ]] && [[ "${{ steps.getPRInfo.outputs.REPO_FULL_NAME }}" != "Expensify/Mobile-Expensify" ]]; then + if [[ "${{ steps.getPRInfo.outputs.REPO_FULL_NAME }}" != ${{ github.repository }} ]] && [[ ! "${{ steps.getPRInfo.outputs.REPO_FULL_NAME }}" =~ "Expensify/Mobile-Expensify*" ]]; then echo "::error::❌ Cherry picks are only supported for the Expensify/App and Expensify/Mobile-Expensify repositories. Found: ${{ steps.getPRInfo.outputs.REPO_FULL_NAME }}" exit 1 fi @@ -45,7 +45,7 @@ jobs: run: echo "CONFLICT_BRANCH_NAME=cherry-pick-${{ inputs.TARGET }}-${{ steps.getPRInfo.outputs.PR_NUMBER }}-${{ github.run_id }}-${{ github.run_attempt }}" >> "$GITHUB_OUTPUT" - name: Checkout target branch with full history if cherry picking Mobile-Expensify - if: ${{ steps.getPRInfo.outputs.REPO_FULL_NAME == 'Expensify/Mobile-Expensify' }} + if: ${{ startsWith(steps.getPRInfo.outputs.REPO_FULL_NAME, 'Expensify/Mobile-Expensify') }} # v4 uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 with: @@ -152,7 +152,7 @@ jobs: - name: Cherry-pick the merge commit of target PR id: cherryPick # If cherry picking a Mobile-Expensify change, we need to run the cherry pick in the Mobile-Expensify directory - working-directory: ${{ steps.getPRInfo.outputs.REPO_FULL_NAME == 'Expensify/Mobile-Expensify' && 'Mobile-Expensify' || '.' }} + working-directory: ${{ startsWith(steps.getPRInfo.outputs.REPO_FULL_NAME, 'Expensify/Mobile-Expensify') && 'Mobile-Expensify' || '.' }} run: | echo "Attempting to cherry-pick ${{ steps.getCPMergeCommit.outputs.MERGE_COMMIT_SHA }}" if git cherry-pick -S -x --mainline 1 ${{ steps.getCPMergeCommit.outputs.MERGE_COMMIT_SHA }}; then @@ -172,7 +172,7 @@ jobs: git checkout -b ${{ steps.getBranchName.outputs.CONFLICT_BRANCH_NAME }} git push --set-upstream origin ${{ steps.getBranchName.outputs.CONFLICT_BRANCH_NAME }} else - if [[ "${{ steps.getPRInfo.outputs.REPO_FULL_NAME }}" == "Expensify/Mobile-Expensify" ]]; then + if [[ "${{ steps.getPRInfo.outputs.REPO_FULL_NAME }}" =~ "Expensify/Mobile-Expensify*" ]]; then # Push Mobile-Expensify changes first cd Mobile-Expensify git push origin ${{ inputs.TARGET }} From 4548ca306866afe555161ae5657d0163b5b67bbf Mon Sep 17 00:00:00 2001 From: rory Date: Tue, 29 Apr 2025 09:43:26 -0700 Subject: [PATCH 18/18] Remove quotes from regex --- .github/workflows/cherryPick.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cherryPick.yml b/.github/workflows/cherryPick.yml index 0642c79b0435..f9ebeee28b99 100644 --- a/.github/workflows/cherryPick.yml +++ b/.github/workflows/cherryPick.yml @@ -35,7 +35,7 @@ jobs: - name: Verify repository run: | - if [[ "${{ steps.getPRInfo.outputs.REPO_FULL_NAME }}" != ${{ github.repository }} ]] && [[ ! "${{ steps.getPRInfo.outputs.REPO_FULL_NAME }}" =~ "Expensify/Mobile-Expensify*" ]]; then + if [[ "${{ steps.getPRInfo.outputs.REPO_FULL_NAME }}" != ${{ github.repository }} ]] && [[ ! "${{ steps.getPRInfo.outputs.REPO_FULL_NAME }}" =~ Expensify/Mobile-Expensify* ]]; then echo "::error::❌ Cherry picks are only supported for the Expensify/App and Expensify/Mobile-Expensify repositories. Found: ${{ steps.getPRInfo.outputs.REPO_FULL_NAME }}" exit 1 fi @@ -172,7 +172,7 @@ jobs: git checkout -b ${{ steps.getBranchName.outputs.CONFLICT_BRANCH_NAME }} git push --set-upstream origin ${{ steps.getBranchName.outputs.CONFLICT_BRANCH_NAME }} else - if [[ "${{ steps.getPRInfo.outputs.REPO_FULL_NAME }}" =~ "Expensify/Mobile-Expensify*" ]]; then + if [[ "${{ steps.getPRInfo.outputs.REPO_FULL_NAME }}" =~ Expensify/Mobile-Expensify* ]]; then # Push Mobile-Expensify changes first cd Mobile-Expensify git push origin ${{ inputs.TARGET }}