Skip to content

Commit cf5f185

Browse files
author
silas.jiang
committed
support auto cherry pick
Signed-off-by: silas.jiang <silas.jiang@zilliz.com>
1 parent da333ee commit cf5f185

File tree

1 file changed

+204
-0
lines changed

1 file changed

+204
-0
lines changed
Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
name: Backport
2+
3+
on:
4+
pull_request_target:
5+
types:
6+
- closed
7+
- labeled
8+
workflow_dispatch:
9+
inputs:
10+
pr_number:
11+
description: 'PR number'
12+
required: true
13+
type: string
14+
target_branch:
15+
description: 'Target branch'
16+
required: true
17+
type: string
18+
19+
concurrency:
20+
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
21+
cancel-in-progress: true
22+
23+
jobs:
24+
auto-backport:
25+
if: github.event_name == 'pull_request_target' && github.event.pull_request.merged == true
26+
permissions:
27+
contents: write
28+
pull-requests: write
29+
runs-on: ubuntu-latest
30+
steps:
31+
- name: Checkout
32+
uses: actions/checkout@v4
33+
with:
34+
fetch-depth: 0
35+
36+
- name: Configure Git
37+
run: |
38+
git config user.name "github-actions[bot]"
39+
git config user.email "github-actions[bot]@users.noreply.github.com"
40+
41+
- name: Check Restricted Paths
42+
id: check_files
43+
env:
44+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
45+
PR_NUMBER: ${{ github.event.pull_request.number }}
46+
PR_AUTHOR: ${{ github.event.pull_request.user.login }}
47+
run: |
48+
CHANGED_FILES=$(gh pr view $PR_NUMBER --json files --jq '.files[].path')
49+
50+
if echo "$CHANGED_FILES" | grep -q "^proto_gen/"; then
51+
echo "should_skip=true" >> $GITHUB_OUTPUT
52+
gh pr comment $PR_NUMBER --body "⚠️ **Backport Skipped**
53+
Hi @$PR_AUTHOR, this PR modifies \`proto_gen/\`. Please backport manually."
54+
else
55+
echo "should_skip=false" >> $GITHUB_OUTPUT
56+
fi
57+
58+
- name: Check Target Branch Existence
59+
id: check_branch
60+
if: steps.check_files.outputs.should_skip != 'true'
61+
env:
62+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
63+
PR_NUMBER: ${{ github.event.pull_request.number }}
64+
PR_AUTHOR: ${{ github.event.pull_request.user.login }}
65+
run: |
66+
LABELS=$(gh pr view $PR_NUMBER --json labels -q '.labels[].name' | grep '^backport-to-')
67+
68+
if [ -z "$LABELS" ]; then
69+
echo "should_skip=true" >> $GITHUB_OUTPUT
70+
exit 0
71+
fi
72+
73+
INVALID_BRANCHES=""
74+
while IFS= read -r label; do
75+
BRANCH_NAME=${label#backport-to-}
76+
if ! git ls-remote --exit-code --heads origin "$BRANCH_NAME" > /dev/null; then
77+
INVALID_BRANCHES="$INVALID_BRANCHES \`$BRANCH_NAME\`"
78+
fi
79+
done <<< "$LABELS"
80+
81+
if [ -n "$INVALID_BRANCHES" ]; then
82+
echo "should_skip=true" >> $GITHUB_OUTPUT
83+
gh pr comment $PR_NUMBER --body "❌ **Backport Failed (Configuration Error)**
84+
Hi @$PR_AUTHOR, I cannot find the target branch(es): $INVALID_BRANCHES.
85+
Please check your label spelling."
86+
else
87+
echo "should_skip=false" >> $GITHUB_OUTPUT
88+
fi
89+
90+
- name: Backport
91+
id: backport_step
92+
if: steps.check_files.outputs.should_skip != 'true' && steps.check_branch.outputs.should_skip != 'true'
93+
uses: korthout/backport-action@v3
94+
with:
95+
label_pattern: '^backport-to-(.*)$'
96+
pull_title: '[Backport ${target_branch}] ${pull_title}'
97+
pull_description: 'Backport of #${pull_number} to `${target_branch}`.'
98+
add_labels: 'backport'
99+
100+
- name: Label Original PR
101+
if: steps.backport_step.outcome == 'success'
102+
env:
103+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
104+
PR_NUMBER: ${{ github.event.pull_request.number }}
105+
run: |
106+
gh pr edit $PR_NUMBER --add-label "cherry-picked"
107+
108+
manual-backport:
109+
if: github.event_name == 'workflow_dispatch'
110+
permissions:
111+
contents: write
112+
pull-requests: write
113+
runs-on: ubuntu-latest
114+
steps:
115+
- name: Checkout
116+
uses: actions/checkout@v4
117+
with:
118+
fetch-depth: 0
119+
120+
- name: Configure Git
121+
run: |
122+
git config user.name "github-actions[bot]"
123+
git config user.email "github-actions[bot]@users.noreply.github.com"
124+
125+
- name: Execute Manual Backport
126+
env:
127+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
128+
PR_NUMBER: ${{ inputs.pr_number }}
129+
TARGET_BRANCH: ${{ inputs.target_branch }}
130+
ACTOR: ${{ github.actor }}
131+
run: |
132+
TARGET_BRANCH=$(echo "$TARGET_BRANCH" | xargs)
133+
134+
echo "Processing Manual Backport: PR #$PR_NUMBER -> $TARGET_BRANCH"
135+
136+
PR_DATA=$(gh pr view $PR_NUMBER --json mergeCommit,title,files,author,state --jq .)
137+
MERGE_COMMIT=$(echo "$PR_DATA" | jq -r .mergeCommit.oid)
138+
PR_TITLE=$(echo "$PR_DATA" | jq -r .title)
139+
PR_AUTHOR=$(echo "$PR_DATA" | jq -r .author.login)
140+
PR_STATE=$(echo "$PR_DATA" | jq -r .state)
141+
CHANGED_FILES=$(echo "$PR_DATA" | jq -r .files[].path)
142+
143+
if [ "$PR_STATE" != "MERGED" ]; then
144+
echo "::error::PR is not merged."
145+
gh pr comment $PR_NUMBER --body "❌ **Manual Backport Failed**
146+
PR #$PR_NUMBER is not merged yet. Please merge it first."
147+
exit 1
148+
fi
149+
150+
if echo "$CHANGED_FILES" | grep -q "^proto_gen/"; then
151+
echo "::error::Skipping. PR modifies proto_gen/."
152+
gh pr comment $PR_NUMBER --body "⚠️ **Manual Backport Skipped**
153+
Hi @$PR_AUTHOR, this PR modifies \`proto_gen/\`. Please backport manually."
154+
exit 1
155+
fi
156+
157+
if ! git ls-remote --exit-code --heads origin $TARGET_BRANCH; then
158+
echo "::error::Target branch '$TARGET_BRANCH' does not exist."
159+
gh pr comment $PR_NUMBER --body "❌ **Manual Backport Failed**
160+
Target branch \`$TARGET_BRANCH\` does not exist. Please check the branch name."
161+
exit 1
162+
fi
163+
164+
NEW_BRANCH="backport-$PR_NUMBER-to-manual-$(date +%s)"
165+
166+
git checkout -b $NEW_BRANCH origin/$TARGET_BRANCH
167+
168+
CP_ARGS="-s"
169+
if git rev-parse -q --verify "$MERGE_COMMIT^2" >/dev/null; then
170+
CP_ARGS="-s -m 1"
171+
fi
172+
173+
if git cherry-pick $CP_ARGS $MERGE_COMMIT; then
174+
git push origin $NEW_BRANCH
175+
176+
NEW_PR_URL=$(gh pr create \
177+
--base $TARGET_BRANCH \
178+
--head $NEW_BRANCH \
179+
--title "[Backport $TARGET_BRANCH] $PR_TITLE" \
180+
--body "Manual backport of #$PR_NUMBER to \`$TARGET_BRANCH\`." \
181+
--label "backport" \
182+
--assignee $ACTOR)
183+
184+
gh pr comment $PR_NUMBER --body "✅ **Manual Backport Success**
185+
Cherry-picked to \`$TARGET_BRANCH\` via manual trigger: $NEW_PR_URL"
186+
187+
gh pr edit $PR_NUMBER --add-label "cherry-picked"
188+
else
189+
echo "::error::Cherry-pick failed due to conflicts."
190+
git cherry-pick --abort || true
191+
192+
gh pr comment $PR_NUMBER --body "❌ **Manual Backport Failed**
193+
Hi @$PR_AUTHOR, I could not cherry-pick this to \`$TARGET_BRANCH\` due to **merge conflicts**.
194+
195+
Please perform the backport locally:
196+
\`\`\`bash
197+
git checkout $TARGET_BRANCH
198+
git pull
199+
git cherry-pick -x $MERGE_COMMIT
200+
# Resolve conflicts...
201+
git push ...
202+
\`\`\`"
203+
exit 1
204+
fi

0 commit comments

Comments
 (0)