Cherry-pick bot2

Conrad Irwin created

Change summary

.github/workflows/cherry-pick.yml | 151 +++++++++++++++++++++++++++++++++
1 file changed, 151 insertions(+)

Detailed changes

.github/workflows/cherry-pick.yml 🔗

@@ -0,0 +1,151 @@
+name: Cherry Pick
+
+on:
+  issue_comment:
+    types: [created]
+  pull_request_target:
+    types: [closed]
+
+jobs:
+  cherry-pick:
+    # This job will run when a PR is merged with a specific comment,
+    # or when a comment is added to an already merged PR.
+    runs-on: ubuntu-latest
+    # Use pull_request_target so that we can add comments back to the PR
+    # if the cherry-pick fails.
+    permissions:
+      pull-requests: write
+      contents: write
+      issues: write
+
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 0 # Required to get all history for cherry-picking
+
+      - name: Extract info and determine trigger
+        id: info
+        run: |
+          # Default to failure unless a valid trigger is found
+          echo "valid=false" >> $GITHUB_OUTPUT
+
+          if [[ "${{ github.event_name }}" == "pull_request_target" && "${{ github.event.pull_request.merged }}" == "true" ]]; then
+            echo "Triggered by PR merge"
+            PR_NUMBER="${{ github.event.pull_request.number }}"
+
+            # Check PR body first, then fall back to comments
+            TEXT_TO_SEARCH="${{ github.event.pull_request.body }}"
+            if [[ ! "$TEXT_TO_SEARCH" =~ /cherry-pick[[:space:]]+(stable|preview) ]]; then
+              echo "Command not found in PR body. Checking comments..."
+              TEXT_TO_SEARCH=$(gh pr view $PR_NUMBER --json comments -q '.comments[].body' | tail -n 100)
+            fi
+
+            if [[ "$TEXT_TO_SEARCH" =~ /cherry-pick[[:space:]]+(stable|preview) ]]; then
+              echo "Found cherry-pick command."
+              MERGE_SHA="${{ github.event.pull_request.merge_commit_sha }}"
+              # Get the last matching command in the text
+              CHANNEL=$(echo "$TEXT_TO_SEARCH" | grep -oP '/cherry-pick[[:space:]]+\K(stable|preview)' | tail -n1)
+
+              echo "valid=true" >> $GITHUB_OUTPUT
+              echo "merge_sha=$MERGE_SHA" >> $GITHUB_OUTPUT
+              echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT
+              echo "channel=$CHANNEL" >> $GITHUB_OUTPUT
+            else
+              echo "No cherry-pick command found in PR body or recent comments. Exiting."
+              exit 0
+            fi
+
+          elif [[ "${{ github.event_name }}" == "issue_comment" && "${{ github.event.issue.pull_request }}" != "" ]]; then
+            echo "Triggered by issue comment"
+            COMMENT_BODY="${{ github.event.comment.body }}"
+            if [[ ! "$COMMENT_BODY" =~ /cherry-pick[[:space:]]+(stable|preview) ]]; then
+              echo "Comment does not contain cherry-pick command. Exiting."
+              exit 0
+            fi
+
+            PR_NUMBER="${{ github.event.issue.number }}"
+
+            # Check if the PR is merged
+            MERGE_SHA=$(gh pr view $PR_NUMBER --json mergeCommit -q .mergeCommit.oid)
+            if [[ -z "$MERGE_SHA" ]]; then
+              echo "PR #$PR_NUMBER is not merged. Exiting."
+              exit 0
+            fi
+
+            CHANNEL=$(echo "$COMMENT_BODY" | grep -oP '/cherry-pick[[:space:]]+\K(stable|preview)' | head -n1)
+
+            echo "valid=true" >> $GITHUB_OUTPUT
+            echo "merge_sha=$MERGE_SHA" >> $GITHUB_OUTPUT
+            echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT
+            echo "channel=$CHANNEL" >> $GITHUB_OUTPUT
+          fi
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+      - name: Cherry-pick
+        if: steps.info.outputs.valid == 'true'
+        run: |
+          set -e
+
+          CHANNEL="${{ steps.info.outputs.channel }}"
+          MERGE_SHA="${{ steps.info.outputs.merge_sha }}"
+          PR_NUMBER="${{ steps.info.outputs.pr_number }}"
+
+          # Get the latest version for the channel
+          echo "Fetching latest version for '$CHANNEL' channel..."
+          query=""
+          case $CHANNEL in
+            stable)
+              ;;
+            preview)
+              query="&preview=1"
+              ;;
+            *)
+              echo "Invalid channel: $CHANNEL" >&2
+              exit 1
+              ;;
+          esac
+          LATEST_VERSION=$(curl -s "https://zed.dev/api/releases/latest?asset=zed&os=macos&arch=aarch64$query" | jq -r .version)
+
+          if [[ -z "$LATEST_VERSION" ]]; then
+            echo "Could not fetch latest version for channel '$CHANNEL'"
+            gh pr comment $PR_NUMBER --body "Could not fetch latest version for channel '$CHANNEL' from zed.dev API."
+            exit 1
+          fi
+          echo "Latest version is $LATEST_VERSION"
+
+          # Construct target branch name (e.g., v0.85.4 -> v0.85.x)
+          TARGET_BRANCH=$(echo "$LATEST_VERSION" | sed -E 's/v([0-9]+\.[0-9]+)\..*/v\1.x/')
+          echo "Target branch is $TARGET_BRANCH"
+
+          # Configure git
+          git config user.name "github-actions[bot]"
+          git config user.email "github-actions[bot]@users.noreply.github.com"
+
+          # Create and push the cherry-pick branch
+          NEW_BRANCH="cherry-pick/pr-${PR_NUMBER}-to-${TARGET_BRANCH}"
+
+          git fetch origin $TARGET_BRANCH
+          git checkout -b $NEW_BRANCH "origin/$TARGET_BRANCH"
+
+          echo "Attempting to cherry-pick $MERGE_SHA..."
+          if ! git cherry-pick $MERGE_SHA; then
+            echo "Cherry-pick failed. Please resolve conflicts manually."
+            gh pr comment $PR_NUMBER --body "Automated cherry-pick to \`$TARGET_BRANCH\` failed due to conflicts. Please resolve them manually."
+            exit 1
+          fi
+
+          echo "Pushing new branch $NEW_BRANCH..."
+          git push -u origin $NEW_BRANCH
+
+          # Create the pull request
+          echo "Creating pull request..."
+          gh pr create \
+            --title "Cherry-pick PR #${PR_NUMBER} to ${TARGET_BRANCH}" \
+            --body "This PR cherry-picks the changes from #${PR_NUMBER} to the \`$TARGET_BRANCH\` branch." \
+            --base $TARGET_BRANCH \
+            --head $NEW_BRANCH \
+            --reviewer "${{ github.actor }}"
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}