Repository navigation
cherry-pick-command #18
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Cherry Pick Command | |
| on: | |
| repository_dispatch: | |
| types: [cherry-pick-command] | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| issues: write | |
| jobs: | |
| prepare: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| branches: ${{ steps.parse-args.outputs.branches }} | |
| error: ${{ steps.parse-args.outputs.error }} | |
| message: ${{ steps.parse-args.outputs.message }} | |
| steps: | |
| - name: Add reaction to trigger comment | |
| uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5.0.0 | |
| with: | |
| token: ${{ secrets.SBR_BOT_TOKEN }} | |
| repository: ${{ github.event.client_payload.github.payload.repository.full_name }} | |
| comment-id: ${{ github.event.client_payload.github.payload.comment.id }} | |
| reactions: "+1" | |
| - name: Get target branches from command args | |
| id: parse-args | |
| run: | | |
| # Parse all unnamed arguments from the slash command | |
| ARGS_JSON='${{ toJson(github.event.client_payload.slash_command.args.unnamed) }}' | |
| # Debug: show what we received | |
| echo "Raw args JSON: $ARGS_JSON" | |
| # Extract branch names from the JSON object, filtering out "all" field if present | |
| # The unnamed args come as {arg1: "branch1", arg2: "branch2", ...} | |
| BRANCHES=$(echo "$ARGS_JSON" | jq -r '[to_entries[] | select(.key | startswith("arg")) | .value | select(. != null and . != "")] | @json') | |
| echo "Parsed branches: $BRANCHES" | |
| if [ "$BRANCHES" = "[]" ] || [ -z "$BRANCHES" ]; then | |
| echo "error=true" >> $GITHUB_OUTPUT | |
| echo "message=Missing target branch(es). Usage: /cherry-pick <target-branch> [<target-branch2> ...]" >> $GITHUB_OUTPUT | |
| else | |
| echo "branches=$BRANCHES" >> $GITHUB_OUTPUT | |
| echo "error=false" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Comment on error | |
| if: steps.parse-args.outputs.error == 'true' | |
| uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5.0.0 | |
| with: | |
| token: ${{ secrets.SBR_BOT_TOKEN }} | |
| repository: ${{ github.event.client_payload.github.payload.repository.full_name }} | |
| issue-number: ${{ github.event.client_payload.github.payload.issue.number }} | |
| body: | | |
| ❌ **Cherry-pick failed**: ${{ steps.parse-args.outputs.message }} | |
| **Usage**: `/cherry-pick <target-branch> [<target-branch2> ...]` | |
| **Examples**: | |
| - `/cherry-pick release-v1.0` | |
| - `/cherry-pick release-v1.0 release-v1.1 release-v2.0` | |
| cherry-pick: | |
| needs: prepare | |
| if: needs.prepare.outputs.error == 'false' | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| branch: ${{ fromJson(needs.prepare.outputs.branches) }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 | |
| with: | |
| token: ${{ secrets.SBR_BOT_TOKEN }} | |
| fetch-depth: 0 | |
| - name: Perform cherry-pick | |
| id: cherry-pick | |
| continue-on-error: true | |
| env: | |
| GH_TOKEN: ${{ secrets.SBR_BOT_TOKEN }} | |
| TARGET_BRANCH: ${{ matrix.branch }} | |
| PR_NUMBER: ${{ github.event.client_payload.pull_request.number }} | |
| run: | | |
| set +e # Don't exit on error, we want to capture it | |
| # Capture all output | |
| OUTPUT_FILE=$(mktemp) | |
| { | |
| echo "🤖 Starting cherry-pick process..." | |
| git config user.name "Shortbrain bot" | |
| git config user.email "vincent+bot@sbr.pm" | |
| # Get PR information | |
| echo "Fetching PR #$PR_NUMBER information..." | |
| PR_DATA=$(gh pr view $PR_NUMBER --repo ${{ github.repository }} --json state,mergeCommit,mergedAt) | |
| # Check if PR is merged | |
| PR_STATE=$(echo "$PR_DATA" | jq -r '.state') | |
| MERGED_AT=$(echo "$PR_DATA" | jq -r '.mergedAt') | |
| if [ "$PR_STATE" != "MERGED" ] || [ "$MERGED_AT" = "null" ]; then | |
| echo "❌ ERROR: PR #$PR_NUMBER is not merged yet (state: $PR_STATE). Cherry-pick requires merged PRs." | |
| exit 1 | |
| fi | |
| MERGE_COMMIT=$(echo "$PR_DATA" | jq -r '.mergeCommit.oid') | |
| echo "Found merge commit: $MERGE_COMMIT" | |
| # Fetch target branch | |
| echo "Fetching target branch: $TARGET_BRANCH..." | |
| if ! git fetch origin "$TARGET_BRANCH" 2>&1; then | |
| echo "❌ ERROR: Target branch '$TARGET_BRANCH' does not exist or cannot be fetched." | |
| exit 1 | |
| fi | |
| # Check if a cherry-pick PR already exists | |
| CHERRY_PICK_BRANCH="cherry-pick-$PR_NUMBER-to-$TARGET_BRANCH" | |
| echo "Checking for existing cherry-pick PR..." | |
| EXISTING_PR=$(gh pr list \ | |
| --repo ${{ github.repository }} \ | |
| --head "$CHERRY_PICK_BRANCH" \ | |
| --base "$TARGET_BRANCH" \ | |
| --json number,url \ | |
| --jq '.[0] | select(. != null)') | |
| if [ -n "$EXISTING_PR" ]; then | |
| PR_URL=$(echo "$EXISTING_PR" | jq -r '.url') | |
| PR_NUM=$(echo "$EXISTING_PR" | jq -r '.number') | |
| echo "ℹ️ Cherry-pick PR already exists: #$PR_NUM" | |
| echo "URL: $PR_URL" | |
| echo "existing_pr_url=$PR_URL" >> $GITHUB_OUTPUT | |
| echo "existing_pr_number=$PR_NUM" >> $GITHUB_OUTPUT | |
| exit 0 | |
| fi | |
| # Create new branch for cherry-pick | |
| echo "Creating cherry-pick branch: $CHERRY_PICK_BRANCH..." | |
| git checkout -b "$CHERRY_PICK_BRANCH" "origin/$TARGET_BRANCH" | |
| # Perform cherry-pick | |
| echo "Cherry-picking commit $MERGE_COMMIT..." | |
| if ! git cherry-pick -m 1 "$MERGE_COMMIT" 2>&1; then | |
| echo "❌ ERROR: Cherry-pick failed due to conflicts or other errors." | |
| git cherry-pick --abort 2>/dev/null || true | |
| exit 1 | |
| fi | |
| # Push the new branch | |
| echo "Pushing cherry-pick branch..." | |
| git push origin "$CHERRY_PICK_BRANCH" | |
| # Create pull request | |
| echo "Creating pull request..." | |
| gh pr create \ | |
| --repo ${{ github.repository }} \ | |
| --base "$TARGET_BRANCH" \ | |
| --head "$CHERRY_PICK_BRANCH" \ | |
| --title "Cherry-pick #$PR_NUMBER to $TARGET_BRANCH" \ | |
| --body "Automatic cherry-pick of #$PR_NUMBER to \`$TARGET_BRANCH\`" | |
| echo "✅ Cherry-pick completed successfully!" | |
| } 2>&1 | tee "$OUTPUT_FILE" | |
| EXIT_CODE=${PIPESTATUS[0]} | |
| # Save output for use in comments | |
| EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64) | |
| echo "output<<$EOF" >> $GITHUB_OUTPUT | |
| cat "$OUTPUT_FILE" >> $GITHUB_OUTPUT | |
| echo "$EOF" >> $GITHUB_OUTPUT | |
| rm -f "$OUTPUT_FILE" | |
| exit $EXIT_CODE | |
| - name: Comment on existing PR | |
| if: steps.cherry-pick.outcome == 'success' && steps.cherry-pick.outputs.existing_pr_url != '' | |
| uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5.0.0 | |
| with: | |
| token: ${{ secrets.SBR_BOT_TOKEN }} | |
| repository: ${{ github.event.client_payload.github.payload.repository.full_name }} | |
| issue-number: ${{ github.event.client_payload.github.payload.issue.number }} | |
| body: | | |
| ℹ️ **Cherry-pick to `${{ matrix.branch }}` already exists!** | |
| A pull request for this cherry-pick already exists: #${{ steps.cherry-pick.outputs.existing_pr_number }} | |
| **PR**: ${{ steps.cherry-pick.outputs.existing_pr_url }} | |
| - name: Comment on success | |
| if: steps.cherry-pick.outcome == 'success' && steps.cherry-pick.outputs.existing_pr_url == '' | |
| uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5.0.0 | |
| with: | |
| token: ${{ secrets.SBR_BOT_TOKEN }} | |
| repository: ${{ github.event.client_payload.github.payload.repository.full_name }} | |
| issue-number: ${{ github.event.client_payload.github.payload.issue.number }} | |
| body: | | |
| ✅ **Cherry-pick to `${{ matrix.branch }}` successful!** | |
| A new pull request has been created to cherry-pick this change to `${{ matrix.branch }}`. | |
| Please review and merge the cherry-pick PR. | |
| - name: Comment on failure | |
| if: steps.cherry-pick.outcome == 'failure' | |
| uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5.0.0 | |
| with: | |
| token: ${{ secrets.SBR_BOT_TOKEN }} | |
| repository: ${{ github.event.client_payload.github.payload.repository.full_name }} | |
| issue-number: ${{ github.event.client_payload.github.payload.issue.number }} | |
| body: | | |
| ❌ **Cherry-pick to `${{ matrix.branch }}` failed!** | |
| The automatic cherry-pick to `${{ matrix.branch }}` failed. | |
| **Output:** | |
| ``` | |
| ${{ steps.cherry-pick.outputs.output }} | |
| ``` | |
| **Next steps:** | |
| - Check the [action logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for complete details | |
| - If the PR is not merged, merge it first and try again | |
| - If there are conflicts, you'll need to manually cherry-pick this PR |