cherry-pick.yml

  1name: Cherry Pick
  2
  3on:
  4  issue_comment:
  5    types: [created]
  6  pull_request_target:
  7    types: [closed]
  8
  9jobs:
 10  cherry-pick:
 11    # This job will run when a PR is merged with a specific comment,
 12    # or when a comment is added to an already merged PR.
 13    runs-on: ubuntu-latest
 14    # Use pull_request_target so that we can add comments back to the PR
 15    # if the cherry-pick fails.
 16    permissions:
 17      pull-requests: write
 18      contents: write
 19      issues: write
 20
 21    steps:
 22      - name: Checkout
 23        uses: actions/checkout@v4
 24        with:
 25          fetch-depth: 0 # Required to get all history for cherry-picking
 26
 27      - name: Extract info and determine trigger
 28        id: info
 29        run: |
 30          # Default to failure unless a valid trigger is found
 31          echo "valid=false" >> $GITHUB_OUTPUT
 32
 33          if [[ "${{ github.event_name }}" == "pull_request_target" && "${{ github.event.pull_request.merged }}" == "true" ]]; then
 34            echo "Triggered by PR merge"
 35            PR_NUMBER="${{ github.event.pull_request.number }}"
 36
 37            # Check PR body first, then fall back to comments
 38            TEXT_TO_SEARCH="${{ github.event.pull_request.body }}"
 39            if [[ ! "$TEXT_TO_SEARCH" =~ /cherry-pick[[:space:]]+(stable|preview) ]]; then
 40              echo "Command not found in PR body. Checking comments..."
 41              TEXT_TO_SEARCH=$(gh pr view $PR_NUMBER --json comments -q '.comments[].body' | tail -n 100)
 42            fi
 43
 44            if [[ "$TEXT_TO_SEARCH" =~ /cherry-pick[[:space:]]+(stable|preview) ]]; then
 45              echo "Found cherry-pick command."
 46              MERGE_SHA="${{ github.event.pull_request.merge_commit_sha }}"
 47              # Get the last matching command in the text
 48              CHANNEL=$(echo "$TEXT_TO_SEARCH" | grep -oP '/cherry-pick[[:space:]]+\K(stable|preview)' | tail -n1)
 49
 50              echo "valid=true" >> $GITHUB_OUTPUT
 51              echo "merge_sha=$MERGE_SHA" >> $GITHUB_OUTPUT
 52              echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT
 53              echo "channel=$CHANNEL" >> $GITHUB_OUTPUT
 54            else
 55              echo "No cherry-pick command found in PR body or recent comments. Exiting."
 56              exit 0
 57            fi
 58
 59          elif [[ "${{ github.event_name }}" == "issue_comment" && "${{ github.event.issue.pull_request }}" != "" ]]; then
 60            echo "Triggered by issue comment"
 61            COMMENT_BODY="${{ github.event.comment.body }}"
 62            if [[ ! "$COMMENT_BODY" =~ /cherry-pick[[:space:]]+(stable|preview) ]]; then
 63              echo "Comment does not contain cherry-pick command. Exiting."
 64              exit 0
 65            fi
 66
 67            PR_NUMBER="${{ github.event.issue.number }}"
 68
 69            # Check if the PR is merged
 70            MERGE_SHA=$(gh pr view $PR_NUMBER --json mergeCommit -q .mergeCommit.oid)
 71            if [[ -z "$MERGE_SHA" ]]; then
 72              echo "PR #$PR_NUMBER is not merged. Exiting."
 73              exit 0
 74            fi
 75
 76            CHANNEL=$(echo "$COMMENT_BODY" | grep -oP '/cherry-pick[[:space:]]+\K(stable|preview)' | head -n1)
 77
 78            echo "valid=true" >> $GITHUB_OUTPUT
 79            echo "merge_sha=$MERGE_SHA" >> $GITHUB_OUTPUT
 80            echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT
 81            echo "channel=$CHANNEL" >> $GITHUB_OUTPUT
 82          fi
 83        env:
 84          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
 85
 86      - name: Cherry-pick
 87        if: steps.info.outputs.valid == 'true'
 88        run: |
 89          set -e
 90
 91          CHANNEL="${{ steps.info.outputs.channel }}"
 92          MERGE_SHA="${{ steps.info.outputs.merge_sha }}"
 93          PR_NUMBER="${{ steps.info.outputs.pr_number }}"
 94
 95          # Get the latest version for the channel
 96          echo "Fetching latest version for '$CHANNEL' channel..."
 97          query=""
 98          case $CHANNEL in
 99            stable)
100              ;;
101            preview)
102              query="&preview=1"
103              ;;
104            *)
105              echo "Invalid channel: $CHANNEL" >&2
106              exit 1
107              ;;
108          esac
109          LATEST_VERSION=$(curl -s "https://zed.dev/api/releases/latest?asset=zed&os=macos&arch=aarch64$query" | jq -r .version)
110
111          if [[ -z "$LATEST_VERSION" ]]; then
112            echo "Could not fetch latest version for channel '$CHANNEL'"
113            gh pr comment $PR_NUMBER --body "Could not fetch latest version for channel '$CHANNEL' from zed.dev API."
114            exit 1
115          fi
116          echo "Latest version is $LATEST_VERSION"
117
118          # Construct target branch name (e.g., v0.85.4 -> v0.85.x)
119          TARGET_BRANCH=$(echo "$LATEST_VERSION" | sed -E 's/v([0-9]+\.[0-9]+)\..*/v\1.x/')
120          echo "Target branch is $TARGET_BRANCH"
121
122          # Configure git
123          git config user.name "github-actions[bot]"
124          git config user.email "github-actions[bot]@users.noreply.github.com"
125
126          # Create and push the cherry-pick branch
127          NEW_BRANCH="cherry-pick/pr-${PR_NUMBER}-to-${TARGET_BRANCH}"
128
129          git fetch origin $TARGET_BRANCH
130          git checkout -b $NEW_BRANCH "origin/$TARGET_BRANCH"
131
132          echo "Attempting to cherry-pick $MERGE_SHA..."
133          if ! git cherry-pick $MERGE_SHA; then
134            echo "Cherry-pick failed. Please resolve conflicts manually."
135            gh pr comment $PR_NUMBER --body "Automated cherry-pick to \`$TARGET_BRANCH\` failed due to conflicts. Please resolve them manually."
136            exit 1
137          fi
138
139          echo "Pushing new branch $NEW_BRANCH..."
140          git push -u origin $NEW_BRANCH
141
142          # Create the pull request
143          echo "Creating pull request..."
144          gh pr create \
145            --title "Cherry-pick PR #${PR_NUMBER} to ${TARGET_BRANCH}" \
146            --body "This PR cherry-picks the changes from #${PR_NUMBER} to the \`$TARGET_BRANCH\` branch." \
147            --base $TARGET_BRANCH \
148            --head $NEW_BRANCH \
149            --reviewer "${{ github.actor }}"
150        env:
151          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}