Skip to content

Commit bf87bdb

Browse files
committed
Automate PR backport workflow
Allow backports to be added via PR comments and automatically processed on merge. - Added 'add-backports' subcommand to append PRs to active release tracking issue. - Added 'find-release-issue' subcommand to locate the tracking issue for a PR. - Added unit tests for new subcommands. - Created 'release_add_backports' reusable workflow. - Updated comment workflow to parse PR comments. - Created 'on_pr_closed' workflow to automate processing on merge.
1 parent f1db067 commit bf87bdb

10 files changed

Lines changed: 708 additions & 105 deletions

File tree

.github/workflows/on_comment.yaml

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
name: "On Comment"
2+
3+
on:
4+
issue_comment:
5+
types: [created]
6+
7+
permissions:
8+
contents: write
9+
issues: write
10+
pull-requests: write
11+
12+
jobs:
13+
# This job always runs to prevent GHA from marking the run as failed when
14+
# all other jobs are skipped.
15+
noop:
16+
runs-on: ubuntu-latest
17+
steps:
18+
- run: echo "No-op"
19+
20+
parse_comment:
21+
runs-on: ubuntu-latest
22+
if: |
23+
github.event.comment.author_association == 'OWNER' ||
24+
github.event.comment.author_association == 'MEMBER' ||
25+
github.event.comment.author_association == 'COLLABORATOR'
26+
outputs:
27+
command: ${{ steps.parse.outputs.command }}
28+
issue_number: ${{ steps.parse.outputs.issue_number }}
29+
pr_number: ${{ steps.parse.outputs.pr_number }}
30+
backports: ${{ steps.parse.outputs.backports }}
31+
steps:
32+
- name: Parse comment
33+
id: parse
34+
env:
35+
COMMENT_BODY: ${{ github.event.comment.body }}
36+
IS_PR: "${{ github.event.issue.pull_request != null }}"
37+
EVENT_NUMBER: "${{ github.event.issue.number }}"
38+
HAS_RELEASE_LABEL: "${{ contains(github.event.issue.labels.*.name, 'type: release') }}"
39+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
40+
run: |
41+
if [ "$IS_PR" = "false" ] && [ "$HAS_RELEASE_LABEL" = "true" ]; then
42+
issue_number=$EVENT_NUMBER
43+
if echo "$COMMENT_BODY" | grep -qE '^[[:space:]]*/create-rc([[:space:]]|$)'; then
44+
echo "command=create-rc" >> "$GITHUB_OUTPUT"
45+
echo "issue_number=$issue_number" >> "$GITHUB_OUTPUT"
46+
elif echo "$COMMENT_BODY" | grep -qE '^[[:space:]]*/prepare([[:space:]]|$)'; then
47+
echo "command=prepare" >> "$GITHUB_OUTPUT"
48+
echo "issue_number=$issue_number" >> "$GITHUB_OUTPUT"
49+
elif echo "$COMMENT_BODY" | grep -qE '^[[:space:]]*/process-backports([[:space:]]|$)'; then
50+
echo "command=process-backports" >> "$GITHUB_OUTPUT"
51+
echo "issue_number=$issue_number" >> "$GITHUB_OUTPUT"
52+
elif echo "$COMMENT_BODY" | grep -qE '^[[:space:]]*/add-backports([[:space:]]|$)'; then
53+
args=$(echo "$COMMENT_BODY" | grep -E '^[[:space:]]*/add-backports([[:space:]]|$)' | sed -E 's/^[[:space:]]*\/add-backports[[:space:]]*//')
54+
args=$(echo "$args" | sed -e 's/^[[:space:],]*//' -e 's/[[:space:],]*$//')
55+
csv=$(echo "$args" | sed -E 's/[[:space:],]+/ /g' | tr ' ' ',')
56+
if [ -n "$csv" ]; then
57+
echo "command=add-backports" >> "$GITHUB_OUTPUT"
58+
echo "backports=$csv" >> "$GITHUB_OUTPUT"
59+
echo "issue_number=$issue_number" >> "$GITHUB_OUTPUT"
60+
else
61+
echo "command=none" >> "$GITHUB_OUTPUT"
62+
echo "Error: No PRs specified for add-backports." >&2
63+
gh api \
64+
--method POST \
65+
-H "Accept: application/vnd.github+json" \
66+
-H "X-GitHub-Api-Version: 2022-11-28" \
67+
/repos/${{ github.repository }}/issues/comments/${{ github.event.comment.id }}/reactions \
68+
-f "content=-1"
69+
fi
70+
elif echo "$COMMENT_BODY" | grep -qE '^[[:space:]]*/promote([[:space:]]|$)'; then
71+
echo "command=promote" >> "$GITHUB_OUTPUT"
72+
echo "issue_number=$issue_number" >> "$GITHUB_OUTPUT"
73+
else
74+
echo "command=none" >> "$GITHUB_OUTPUT"
75+
fi
76+
elif [ "$IS_PR" = "true" ]; then
77+
pr_number=$EVENT_NUMBER
78+
if echo "$COMMENT_BODY" | grep -qE '^[[:space:]]*/backport([[:space:]]|$)'; then
79+
echo "command=pr-backport" >> "$GITHUB_OUTPUT"
80+
echo "pr_number=$pr_number" >> "$GITHUB_OUTPUT"
81+
else
82+
echo "command=none" >> "$GITHUB_OUTPUT"
83+
fi
84+
else
85+
echo "command=none" >> "$GITHUB_OUTPUT"
86+
fi
87+
88+
call_create_rc:
89+
needs: parse_comment
90+
if: needs.parse_comment.outputs.command == 'create-rc'
91+
uses: ./.github/workflows/release_create_rc.yaml
92+
with:
93+
issue: ${{ needs.parse_comment.outputs.issue_number }}
94+
comment_id: "${{ github.event.comment.id }}"
95+
secrets: inherit
96+
97+
call_prepare:
98+
needs: parse_comment
99+
if: needs.parse_comment.outputs.command == 'prepare'
100+
uses: ./.github/workflows/release_prepare.yaml
101+
with:
102+
issue: ${{ needs.parse_comment.outputs.issue_number }}
103+
secrets: inherit
104+
105+
call_add_backports:
106+
needs: parse_comment
107+
if: |
108+
needs.parse_comment.outputs.command == 'add-backports' ||
109+
needs.parse_comment.outputs.command == 'pr-backport'
110+
uses: ./.github/workflows/release_add_backports.yaml
111+
with:
112+
prs: ${{ needs.parse_comment.outputs.command == 'pr-backport' && needs.parse_comment.outputs.pr_number || needs.parse_comment.outputs.backports }}
113+
issue: ${{ needs.parse_comment.outputs.issue_number }}
114+
secrets: inherit
115+
116+
call_process_backports_after_add:
117+
needs: [parse_comment, call_add_backports]
118+
if: needs.parse_comment.outputs.command == 'add-backports'
119+
uses: ./.github/workflows/release_process_backports.yaml
120+
with:
121+
issue: ${{ needs.parse_comment.outputs.issue_number }}
122+
comment_id: "${{ github.event.comment.id }}"
123+
secrets: inherit
124+
125+
call_process_backports_only:
126+
needs: parse_comment
127+
if: needs.parse_comment.outputs.command == 'process-backports'
128+
uses: ./.github/workflows/release_process_backports.yaml
129+
with:
130+
issue: ${{ needs.parse_comment.outputs.issue_number }}
131+
comment_id: "${{ github.event.comment.id }}"
132+
secrets: inherit
133+
134+
call_promote:
135+
needs: parse_comment
136+
if: needs.parse_comment.outputs.command == 'promote'
137+
uses: ./.github/workflows/release_promote_rc.yaml
138+
with:
139+
issue: ${{ needs.parse_comment.outputs.issue_number }}
140+
secrets: inherit

.github/workflows/on_issue_comment.yaml

Lines changed: 0 additions & 105 deletions
This file was deleted.
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
name: "On PR Closed"
2+
3+
on:
4+
pull_request:
5+
types: [closed]
6+
7+
permissions:
8+
contents: read
9+
issues: read
10+
pull-requests: read
11+
12+
jobs:
13+
# This job always runs to prevent GHA from marking the run as failed when
14+
# all other jobs are skipped.
15+
noop:
16+
runs-on: ubuntu-latest
17+
steps:
18+
- run: echo "No-op"
19+
20+
check_if_backport:
21+
runs-on: ubuntu-latest
22+
if: github.event.pull_request.merged == true
23+
outputs:
24+
should_process: ${{ steps.check.outputs.should_process }}
25+
steps:
26+
- name: Check if PR is a backport candidate
27+
id: check
28+
env:
29+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
30+
PR_NUMBER: ${{ github.event.pull_request.number }}
31+
run: |
32+
# Check if there is any active release issue
33+
ACTIVE_ISSUES=$(gh issue list --repo ${{ github.repository }} --label "type: release" --state open --json number)
34+
if [ "$ACTIVE_ISSUES" = "[]" ] || [ -z "$ACTIVE_ISSUES" ]; then
35+
echo "No active release tracking issue found. Skipping."
36+
echo "should_process=false" >> "$GITHUB_OUTPUT"
37+
exit 0
38+
fi
39+
40+
# Check if PR has "/backport" in comments (only comments, not body)
41+
PR_DATA=$(gh pr view "$PR_NUMBER" --repo ${{ github.repository }} --json comments)
42+
43+
if echo "$PR_DATA" | jq -r '.comments[].body' | grep -qE '^[[:space:]]*/backport([[:space:]]|$)'; then
44+
echo "Found /backport comment. Proceeding."
45+
echo "should_process=true" >> "$GITHUB_OUTPUT"
46+
else
47+
echo "No /backport comment found. Skipping."
48+
echo "should_process=false" >> "$GITHUB_OUTPUT"
49+
fi
50+
51+
find_issue:
52+
needs: check_if_backport
53+
if: needs.check_if_backport.outputs.should_process == 'true'
54+
runs-on: ubuntu-latest
55+
outputs:
56+
issue: ${{ steps.find.outputs.issue }}
57+
steps:
58+
- name: Checkout repository
59+
uses: actions/checkout@v7
60+
with:
61+
fetch-depth: 0
62+
63+
- name: Setup Bazel
64+
uses: bazel-contrib/setup-bazel@0.19.0
65+
with:
66+
bazelisk-version: 1.20.0
67+
68+
- name: Find Release Issue
69+
id: find
70+
env:
71+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
72+
PR_NUMBER: ${{ github.event.pull_request.number }}
73+
run: |
74+
bazel run //tools/private/release -- find-release-issue "$PR_NUMBER"
75+
76+
call_process_backports:
77+
needs: find_issue
78+
if: needs.find_issue.outputs.issue != ''
79+
uses: ./.github/workflows/release_process_backports.yaml
80+
with:
81+
issue: ${{ needs.find_issue.outputs.issue }}
82+
secrets: inherit
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
name: "Release: Add Backports"
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
prs:
7+
description: 'CSV list of PR numbers to add (e.g., 123,456)'
8+
required: true
9+
type: string
10+
issue:
11+
description: 'The Release Tracking Issue Number (optional)'
12+
required: false
13+
type: string
14+
workflow_call:
15+
inputs:
16+
prs:
17+
description: 'CSV list of PR numbers to add (e.g., 123,456)'
18+
required: true
19+
type: string
20+
issue:
21+
description: 'The Release Tracking Issue Number (optional)'
22+
required: false
23+
type: string
24+
25+
permissions:
26+
issues: write
27+
28+
jobs:
29+
add_backports:
30+
runs-on: ubuntu-latest
31+
steps:
32+
- name: Checkout repository
33+
uses: actions/checkout@v7
34+
with:
35+
fetch-depth: 0
36+
37+
- name: Setup Bazel
38+
uses: bazel-contrib/setup-bazel@0.19.0
39+
with:
40+
bazelisk-version: 1.20.0
41+
42+
- name: Add Backports to Tracking Issue
43+
env:
44+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
45+
PRS: ${{ inputs.prs }}
46+
ISSUE: ${{ inputs.issue }}
47+
run: |
48+
ARGS=()
49+
if [ -n "$ISSUE" ]; then
50+
ARGS+=("--issue=$ISSUE")
51+
fi
52+
53+
# Convert CSV to array
54+
IFS=',' read -r -a pr_array <<< "$PRS"
55+
56+
bazel run //tools/private/release -- add-backports \
57+
"${pr_array[@]}" \
58+
"${ARGS[@]}"

0 commit comments

Comments
 (0)