Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
28 changes: 23 additions & 5 deletions .github/workflows/backport.yml
Original file line number Diff line number Diff line change
Expand Up @@ -117,9 +117,18 @@ jobs:
run: |
TARGET_BRANCH="${{ steps.extract-branch.outputs.target_branch }}"
WORKFLOW_URL="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
REASON="${{ steps.backport.outputs.failure_reason }}"

gh pr comment "${{ github.event.pull_request.number }}" --body \
"❌ Failed to cherry-pick to \`$TARGET_BRANCH\` due to conflicts. (🤖) [View backport run]($WORKFLOW_URL)."
# Only the cherry-pick path sets failure_reason=conflict. Anything else
# (API errors, token scopes, push failures) must not be reported as a
# conflict, or people go looking for a merge conflict that never happened.
if [[ "$REASON" == "conflict" ]]; then
MESSAGE="❌ Failed to cherry-pick to \`$TARGET_BRANCH\` due to conflicts. (🤖) [View backport run]($WORKFLOW_URL)."
else
MESSAGE="❌ Backport to \`$TARGET_BRANCH\` failed — not a cherry-pick conflict. (🤖) [View backport run]($WORKFLOW_URL)."
fi

gh pr comment "${{ github.event.pull_request.number }}" --body "$MESSAGE"

- name: Notify Slack and dispatch ClaudeBox on failure
if: steps.backport.outcome == 'failure'
Expand All @@ -133,11 +142,20 @@ jobs:
BRANCH: ${{ steps.extract-branch.outputs.target_branch }}
STAGING_BRANCH: ${{ steps.extract-branch.outputs.staging_branch }}
VERB: ${{ steps.extract-branch.outputs.verb }}
REASON: ${{ steps.backport.outputs.failure_reason }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
run: |
if [[ "$REASON" == "conflict" ]]; then
DETAIL="cherry-pick conflicts"
CLAUDE_PROMPT="${VERB^} PR #$PR ($TITLE) to $BRANCH (staging branch $STAGING_BRANCH). The automatic cherry-pick failed due to conflicts. Follow .claude/claudebox/backport.md to resolve conflicts and create a PR."
else
DETAIL="a script error, not a conflict"
CLAUDE_PROMPT="The automatic $VERB of PR #$PR ($TITLE) to $BRANCH (staging branch $STAGING_BRANCH) failed, but NOT because of a cherry-pick conflict. Read the failing run at $RUN_URL to find the real cause, check whether the staging branch and its PR were already created, and fix the underlying problem."
fi

# Post single message to #backports, derive permalink from response
TEXT=$(printf '⚠️ %s failed: <%s|#%s %s> → `%s` (author: %s) (🤖)' \
"${VERB^}" "$URL" "$PR" "$TITLE" "$BRANCH" "$AUTHOR")
TEXT=$(printf '⚠️ %s failed (%s): <%s|#%s %s> → `%s` (author: %s) (🤖)' \
"${VERB^}" "$DETAIL" "$URL" "$PR" "$TITLE" "$BRANCH" "$AUTHOR")
RESP=$(curl -sS -X POST https://slack.com/api/chat.postMessage \
-H "Authorization: Bearer $SLACK_BOT_TOKEN" \
-H "Content-type: application/json" \
Expand All @@ -152,7 +170,7 @@ jobs:
fi

gh workflow run claudebox.yml \
-f prompt="${VERB^} PR #$PR ($TITLE) to $BRANCH (staging branch $STAGING_BRANCH). The automatic cherry-pick failed due to conflicts. Follow .claude/claudebox/backport.md to resolve conflicts and create a PR." \
-f prompt="$CLAUDE_PROMPT" \
-f link="${LINK:-$URL}" \
-f target_ref="origin/$STAGING_BRANCH" \
-f slack_channel="$CHANNEL_ID" \
Expand Down
14 changes: 12 additions & 2 deletions scripts/backport_to_staging.sh
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,11 @@ if [[ $CONTINUE_MODE -eq 0 ]]; then
exit 0
fi
git cherry-pick --abort 2>/dev/null || true
# Tell the workflow this was a genuine conflict, so it can distinguish it
# from any other way this script can fail.
if [[ -n "${GITHUB_OUTPUT:-}" ]]; then
echo "failure_reason=conflict" >> "$GITHUB_OUTPUT"
fi
echo "Error: Failed to cherry-pick. Fix conflicts manually, then run: ./scripts/backport_to_staging.sh --continue $PR_NUMBER $TARGET_BRANCH" >&2
exit 1
fi
Expand Down Expand Up @@ -259,8 +264,13 @@ else
fi
fi

# Update PR body with commit override markers (same mechanism as merge-trains)
# Update PR body with commit override markers (same mechanism as merge-trains).
# The branch is pushed and the staging PR exists by this point, so a failure
# here must not fail the backport: the caller reports any non-zero exit as a
# cherry-pick conflict, which would be a lie and would page #backports.
echo "Updating PR body with commit list..."
do_or_dryrun "$root/scripts/merge-train/update-pr-body.sh" "$STAGING_BRANCH"
if ! do_or_dryrun "$root/scripts/merge-train/update-pr-body.sh" "$STAGING_BRANCH"; then
echo "Warning: could not update the staging PR body; the backport itself succeeded." >&2
fi

do_or_dryrun echo "Successfully backported PR #$PR_NUMBER to $STAGING_BRANCH"
6 changes: 5 additions & 1 deletion scripts/merge-train/update-pr-body.sh
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ BEGIN_COMMIT_OVERRIDE
$formatted_commits
END_COMMIT_OVERRIDE"

gh pr edit "$pr_number" --body "$new_body"
# Update the body through the REST API rather than `gh pr edit`. gh's PR lookup
# asks for reviewer fields (login/name/slug on teams) that require the read:org
# scope; the bot PAT only carries repo+workflow, so `gh pr edit` dies with a
# GraphQL scope error even though editing a body needs nothing beyond repo.
gh api --method PATCH "repos/{owner}/{repo}/pulls/$pr_number" -f body="$new_body" --silent

echo "PR #$pr_number body updated"
Loading