Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Copilot AI Apr 29, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirm that the updated regex pattern reliably captures the deployer from commit messages and is fully consistent with the commit message format produced during CPs.

Suggested change
const cpActor = commit.message.match(/.*\(cherry-picked to .* by (.*)\)/)?.at(1);
const cpActor = commit.message.match(/\(cherry-picked to [^\)]+ by ([^\)]+)\)/)?.at(1);

Copilot uses AI. Check for mistakes.
if (cpActor) {
deployer = cpActor;
}
Expand Down
75 changes: 44 additions & 31 deletions .github/workflows/cherryPick.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,22 @@ on:
PULL_REQUEST_URL:
description: The full URL of the E/App or E/Mobile-Expensify 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:
# 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:
needs: createNewVersion
Expand All @@ -24,32 +35,32 @@ 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

- 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
if: ${{ steps.getPRInfo.outputs.REPO_FULL_NAME == 'Expensify/Mobile-Expensify' }}
- name: Checkout target branch with full history if cherry picking Mobile-Expensify
if: ${{ startsWith(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 == github.repository }}
# v4
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
with:
ref: staging
ref: ${{ inputs.TARGET }}
token: ${{ secrets.OS_BOTIFY_TOKEN }}
submodules: true

Expand All @@ -74,14 +85,14 @@ 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 == github.repository }}
run: |
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 }}
cd Mobile-Expensify
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 }}

- name: Get E/App version bump commit
id: getVersionBumpCommit
Expand Down Expand Up @@ -119,33 +130,35 @@ 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 commit --amend -m "$(git log -1 --pretty=%B)" -m "(cherry-picked to ${{ inputs.TARGET }} by ${{ github.actor }})"
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 }}
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 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 }}"

- 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
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
Expand All @@ -159,10 +172,10 @@ 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 staging
git push origin ${{ inputs.TARGET }}
cd ..

# Update and commit the submodule reference in E/App
Expand All @@ -171,7 +184,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
Expand All @@ -181,9 +194,9 @@ jobs:
AUTHOR_CHECKLIST=$(sed -n '/### PR Author Checklist/,$p' .github/PULL_REQUEST_TEMPLATE.md)

PR_DESCRIPTION=$(cat <<EOF
🍒 Cherry pick ${{ github.event.inputs.PULL_REQUEST_URL }} to staging 🍒
🍒 Cherry pick ${{ github.event.inputs.PULL_REQUEST_URL }} to ${{ inputs.TARGET }} 🍒

This PR had conflicts when we tried to cherry-pick it to staging. You'll need to manually perform the cherry-pick, using the following steps:
This PR had conflicts when we tried to cherry-pick it to ${{ inputs.TARGET }}. You'll need to manually perform the cherry-pick, using the following steps:

\`\`\`bash
git fetch
Expand All @@ -194,25 +207,25 @@ 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.

_Pro-tip:_ If this PR appears to have conflicts against the _staging_ base, it means that the version on staging has been updated. The easiest thing to do if you see this is to close the PR and re-run the CP.
_Pro-tip:_ If this PR appears to have conflicts against the _${{ inputs.TARGET }}_ base, it means that the version on ${{ inputs.TARGET }} has been updated. The easiest thing to do if you see this is to close the PR and re-run the CP.

$AUTHOR_CHECKLIST
EOF
)

# Create PR
gh pr create \
--title "🍒 Cherry pick PR #${{ steps.getPRInfo.outputs.PR_NUMBER }} to staging 🍒" \
--title "🍒 Cherry pick PR #${{ steps.getPRInfo.outputs.PR_NUMBER }} to ${{ inputs.TARGET }} 🍒" \
--body "$PR_DESCRIPTION" \
--label "Engineering,Hourly" \
--base "staging"
--base "${{ inputs.TARGET }}"
sleep 5
env:
GITHUB_TOKEN: ${{ steps.setupGitForOSBotify.outputs.OS_BOTIFY_API_TOKEN }}
Expand All @@ -228,8 +241,8 @@ jobs:
# In cases when the original PR author is outside the org, the `gh pr edit` command will fail. But we don't want to fail the workflow in that case.
continue-on-error: true

- name: Label original PR with CP Staging
run: gh pr edit ${{ github.event.inputs.PULL_REQUEST_URL }} --add-label 'CP Staging'
- 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' }}'
env:
GITHUB_TOKEN: ${{ secrets.OS_BOTIFY_TOKEN }}

Expand All @@ -245,7 +258,7 @@ jobs:
attachments: [{
color: "#DB4545",
pretext: `<!subteam^S4TJJ3PSL>`,
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:
Expand Down
Loading