ci: add workflow to auto-create backport PRs from backport labels (#9028)

* ci: add backport workflow to create backport PRs from backport labels

Signed-off-by: Ning Sun <sunning@greptime.com>

* ci: document backport labels in PR template and AGENTS.md

Signed-off-by: Ning Sun <sunning@greptime.com>

---------

Signed-off-by: Ning Sun <sunning@greptime.com>
This commit is contained in:
Ning Sun
2026-09-04 08:56:33 +00:00
committed by GitHub
parent cf9a9639b0
commit 27b0ca6676
3 changed files with 101 additions and 0 deletions
+1
View File
@@ -24,3 +24,4 @@ Please convert it to a draft if some of the following conditions are not met.
- [ ] This PR requires documentation updates.
- [ ] API changes are backward compatible.
- [ ] Schema or data changes are backward compatible.
- [ ] This PR needs to be backported to release branches, and I have added the `backport-<target>` labels (e.g. `backport-v1.3` targets `release/v1.3`).
+95
View File
@@ -0,0 +1,95 @@
name: Backport
on:
pull_request_target:
types: [closed]
permissions:
contents: write
pull-requests: write
jobs:
backport:
name: Backport PR
if: github.event.pull_request.merged == true
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
token: ${{ secrets.GH_PERSONAL_ACCESS_TOKEN }}
- name: Extract backport labels
id: extract-labels
env:
LABELS: ${{ toJson(github.event.pull_request.labels.*.name) }}
run: |
labels=$(echo "$LABELS" | jq -r '.[]' | grep -oP '^backport-\K.+' || true)
if [ -z "$labels" ]; then
echo "No backport labels found, skipping."
echo "has_labels=false" >> "$GITHUB_OUTPUT"
exit 0
fi
echo "has_labels=true" >> "$GITHUB_OUTPUT"
echo "targets<<EOF" >> "$GITHUB_OUTPUT"
echo "$labels" >> "$GITHUB_OUTPUT"
echo "EOF" >> "$GITHUB_OUTPUT"
- name: Cherry-pick and create backport PRs
if: steps.extract-labels.outputs.has_labels == 'true'
env:
GH_TOKEN: ${{ secrets.GH_PERSONAL_ACCESS_TOKEN }}
PR_NUMBER: ${{ github.event.pull_request.number }}
PR_TITLE: ${{ github.event.pull_request.title }}
PR_BODY: ${{ github.event.pull_request.body }}
AUTHOR: ${{ github.event.pull_request.merged_by.login }}
BASE_BRANCH: ${{ github.event.pull_request.base.ref }}
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
MERGE_COMMIT=$(gh pr view "$PR_NUMBER" --json mergeCommit --jq '.mergeCommit.oid')
while IFS= read -r target; do
release_branch="release/${target}"
head_branch="backport/${target}/pr-${PR_NUMBER}"
echo "=== Backporting to ${release_branch} ==="
if ! git ls-remote --exit-code origin "${release_branch}" >/dev/null 2>&1; then
echo "WARNING: Branch '${release_branch}' does not exist, skipping."
continue
fi
git fetch origin "${release_branch}"
git checkout -b "${head_branch}" "origin/${release_branch}"
if ! git cherry-pick -x "${MERGE_COMMIT}"; then
echo "ERROR: Cherry-pick failed for ${release_branch}. Creating an issue instead."
gh issue create \
--title "Backport failure: PR #${PR_NUMBER} to ${release_branch}" \
--body "Cherry-pick of ${MERGE_COMMIT} (PR #${PR_NUMBER}) failed on ${release_branch}. Manual backport required." \
git cherry-pick --abort
git checkout "${BASE_BRANCH}"
git branch -D "${head_branch}"
continue
fi
git push origin "${head_branch}"
gh pr create \
--base "${release_branch}" \
--head "${head_branch}" \
--title "${PR_TITLE} [Backport ${release_branch}]" \
--body "Backport of #${PR_NUMBER} to ${release_branch}.
---
Original PR: #${PR_NUMBER}
Merged by: @${AUTHOR}"
git checkout "${BASE_BRANCH}"
git branch -D "${head_branch}"
done <<< "${{ steps.extract-labels.outputs.targets }}"
+5
View File
@@ -136,6 +136,11 @@ blast radius requires it.
[`.github/pull_request_template.md`](.github/pull_request_template.md): include
the CLA statement, fill the change-intention section with enough detail, and
update checklist items accurately.
11. If the change must also land on release branches, add the matching
`backport-<target>` labels (e.g. `backport-v1.3` targets `release/v1.3`). The
Backport workflow (`.github/workflows/backport.yml`) cherry-picks a merged PR
onto each target branch and opens a backport PR automatically; on conflicts
it aborts and files an issue for a manual backport.
## More