backport #5
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: Blathers Backport | |
| on: | |
| repository_dispatch: | |
| types: [backport] | |
| jobs: | |
| backport: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| issues: write | |
| steps: | |
| - name: Generate GitHub App token | |
| id: app-token | |
| uses: actions/create-github-app-token@v1 | |
| with: | |
| app-id: ${{ secrets.BLATHERS_APP_ID }} | |
| private-key: ${{ secrets.BLATHERS_PRIVATE_KEY }} | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| token: ${{ steps.app-token.outputs.token }} | |
| - name: Configure git identity | |
| run: | | |
| git config user.name "blathers-crl[bot]" | |
| git config user.email "blathers-crl[bot]@users.noreply.github.com" | |
| - name: Backport PR | |
| env: | |
| GH_TOKEN: ${{ steps.app-token.outputs.token }} | |
| PR_NUMBER: ${{ github.event.client_payload.pr_number }} | |
| BRANCHES: ${{ github.event.client_payload.branches }} | |
| PR_AUTHOR: ${{ github.event.client_payload.pr_author }} | |
| RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | |
| run: | | |
| set -euo pipefail | |
| # Fetch PR title and body for use in backport PRs. | |
| PR_TITLE=$(gh pr view "$PR_NUMBER" --json title --jq '.title') | |
| PR_BODY=$(gh pr view "$PR_NUMBER" --json body --jq '.body') | |
| # Get commit SHAs from the merged PR. | |
| COMMIT_SHAS=$(gh pr view "$PR_NUMBER" --json commits --jq '.commits[].oid') | |
| COMMIT_COUNT=$(echo "$COMMIT_SHAS" | wc -l | tr -d ' ') | |
| # Collect reviewers from the original PR. | |
| REVIEWERS=$(gh pr view "$PR_NUMBER" --json reviews --jq '[.reviews[].author.login] | unique | join(",")') | |
| FAILED_BRANCHES="" | |
| SUCCEEDED_BRANCHES="" | |
| for BRANCH in $BRANCHES; do | |
| echo "=== Backporting to $BRANCH ===" | |
| # Normalize branch name: try as-is first, then with release- prefix. | |
| # Also strip .x suffix (backport labels use e.g. "24.1.x"). | |
| TARGET_BRANCH="${BRANCH%.x}" | |
| if ! git rev-parse --verify "origin/$TARGET_BRANCH" >/dev/null 2>&1; then | |
| TARGET_BRANCH="release-$TARGET_BRANCH" | |
| if ! git rev-parse --verify "origin/$TARGET_BRANCH" >/dev/null 2>&1; then | |
| echo "::error::Branch $BRANCH not found (tried $BRANCH and release-${BRANCH%.x})" | |
| FAILED_BRANCHES="$FAILED_BRANCHES $BRANCH" | |
| continue | |
| fi | |
| fi | |
| BACKPORT_BRANCH="blathers/backport-${TARGET_BRANCH}-${PR_NUMBER}" | |
| # Clean up any existing backport branch. | |
| git branch -D "$BACKPORT_BRANCH" 2>/dev/null || true | |
| git push origin --delete "$BACKPORT_BRANCH" 2>/dev/null || true | |
| # Create backport branch from target. | |
| git checkout -b "$BACKPORT_BRANCH" "origin/$TARGET_BRANCH" | |
| # Cherry-pick all commits from the PR. | |
| CHERRY_PICK_FAILED=false | |
| for SHA in $COMMIT_SHAS; do | |
| if ! git cherry-pick "$SHA"; then | |
| echo "::error::Cherry-pick of $SHA failed on branch $TARGET_BRANCH" | |
| git cherry-pick --abort 2>/dev/null || true | |
| CHERRY_PICK_FAILED=true | |
| break | |
| fi | |
| done | |
| if [ "$CHERRY_PICK_FAILED" = "true" ]; then | |
| FAILED_BRANCHES="$FAILED_BRANCHES $BRANCH" | |
| git checkout -f HEAD 2>/dev/null || true | |
| continue | |
| fi | |
| # Push the backport branch. | |
| if ! git push origin "$BACKPORT_BRANCH"; then | |
| echo "::error::Failed to push branch $BACKPORT_BRANCH" | |
| FAILED_BRANCHES="$FAILED_BRANCHES $BRANCH" | |
| continue | |
| fi | |
| # Transform version-specific closing keywords. | |
| # e.g. "Fixes-26.1 #159676" -> "Fixes #159676" when targeting release-26.1. | |
| VERSION="${TARGET_BRANCH#release-}" | |
| ESCAPED_VERSION=$(echo "$VERSION" | sed 's/\./\\./g') | |
| TRANSFORMED_BODY=$(echo "$PR_BODY" | sed -E "s/(Fixes|Closes|Resolves|Addresses)-${ESCAPED_VERSION}:?[[:space:]]+/\1 /gi") | |
| # Create backport PR. | |
| BACKPORT_PR_URL=$(gh pr create \ | |
| --base "$TARGET_BRANCH" \ | |
| --head "$BACKPORT_BRANCH" \ | |
| --title "${TARGET_BRANCH}: ${PR_TITLE}" \ | |
| --body "$(cat <<EOF | |
| Backport ${COMMIT_COUNT}/${COMMIT_COUNT} commits from #${PR_NUMBER} on behalf of @${PR_AUTHOR}. | |
| ---- | |
| ${TRANSFORMED_BODY} | |
| ---- | |
| Release justification: | |
| EOF | |
| )") | |
| if [ -z "$BACKPORT_PR_URL" ]; then | |
| echo "::error::Failed to create PR for branch $TARGET_BRANCH" | |
| FAILED_BRANCHES="$FAILED_BRANCHES $BRANCH" | |
| continue | |
| fi | |
| BACKPORT_PR_NUMBER=$(echo "$BACKPORT_PR_URL" | grep -oE '[0-9]+$') | |
| # Add labels. | |
| gh pr edit "$BACKPORT_PR_NUMBER" --add-label "O-robot,blathers-backport" | |
| # Remove "backport-failed" label if it was previously added by a failed attempt. | |
| gh pr edit "$PR_NUMBER" --remove-label "backport-failed" || true | |
| # Add assignee (original PR author). | |
| gh pr edit "$BACKPORT_PR_NUMBER" --add-assignee "$PR_AUTHOR" || true | |
| # Request reviewers from the original PR. | |
| if [ -n "$REVIEWERS" ]; then | |
| gh pr edit "$BACKPORT_PR_NUMBER" --add-reviewer "$REVIEWERS" || true | |
| fi | |
| SUCCEEDED_BRANCHES="$SUCCEEDED_BRANCHES $BRANCH" | |
| echo "=== Successfully created backport PR: $BACKPORT_PR_URL ===" | |
| done | |
| # Post results on the original PR. | |
| if [ -n "$(echo "$SUCCEEDED_BRANCHES" | xargs)" ]; then | |
| SUCCESS_MSG="Successfully created backport PRs for:$SUCCEEDED_BRANCHES" | |
| if [ -n "$(echo "$FAILED_BRANCHES" | xargs)" ]; then | |
| SUCCESS_MSG="$SUCCESS_MSG | |
| Failed to backport to:$FAILED_BRANCHES | |
| Please create these backports manually using the [backport tool](https://github.com/cockroachdb/backport). | |
| [See action run for details]($RUN_URL)." | |
| fi | |
| gh pr comment "$PR_NUMBER" --body "$SUCCESS_MSG" | |
| fi | |
| if [ -n "$(echo "$FAILED_BRANCHES" | xargs)" ]; then | |
| if [ -z "$(echo "$SUCCEEDED_BRANCHES" | xargs)" ]; then | |
| gh pr comment "$PR_NUMBER" --body "$(cat <<EOF | |
| Encountered an error creating backports. Some common things that can go wrong: | |
| 1. The backport branch might have already existed. | |
| 2. There was a merge conflict. | |
| 3. The backport branch contained merge commits. | |
| You might need to create your backport manually using the [backport](https://github.com/cockroachdb/backport) tool. | |
| [See action run for details]($RUN_URL). | |
| ---- | |
| Failed branches:$FAILED_BRANCHES | |
| EOF | |
| )" | |
| fi | |
| gh issue edit "$PR_NUMBER" --add-label "backport-failed" || true | |
| exit 1 | |
| fi |