mirror of
https://github.com/coder/coder.git
synced 2026-06-02 20:48:20 +00:00
ci: add cherry-pick to latest release workflow (#24051)
Adds a GitHub Actions workflow that cherry-picks merged PRs to the latest release branch when the `cherry-pick` label is applied. ## How it works 1. Add the `cherry-pick` label to any PR targeting `main` (before or after merge). 2. On merge (or on label if already merged), the workflow detects the latest `release/*` branch. 3. It cherry-picks the merge commit (`-x -m1`) and opens a PR. This complements the `backport` label (see #24025) which targets the latest **3** release branches. `cherry-pick` targets only the **latest** one — useful for getting fixes into the current release. Created PRs follow existing repo conventions: - **Branch:** `backport/<pr>-to-<version>` - **Title:** `<original PR title> (#<pr>)` — e.g. `fix(site): correct button alignment (#12345)` - **Body:** links back to the original PR and merge commit If the cherry-pick encounters conflicts, the workflow aborts the cherry-pick, creates an empty commit with resolution instructions, and opens the PR with a `[CONFLICT]` prefix so the author can resolve manually. Also: - Removes `scripts/backport-pr.sh` (replaced by this workflow) - Removes `.github/cherry-pick-bot.yml` (old bot config) - Adds a section to the contributing docs explaining the `cherry-pick` label > [!NOTE] > Generated with [Coder Agents](https://coder.com/agents)
This commit is contained in:
@@ -1,136 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Usage: ./scripts/backport-pr.sh [--dry-run] <release-version> <pr-number>
|
||||
#
|
||||
# Backports a merged PR to a release branch by cherry-picking its merge commit
|
||||
# and opening a new PR targeting the release branch.
|
||||
#
|
||||
# Examples:
|
||||
# ./scripts/backport-pr.sh 2.30 23969
|
||||
# ./scripts/backport-pr.sh --dry-run 2.30 23969
|
||||
|
||||
set -euo pipefail
|
||||
# shellcheck source=scripts/lib.sh
|
||||
source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
|
||||
cdroot
|
||||
|
||||
dry_run=0
|
||||
|
||||
# Parse flags.
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--dry-run | -n)
|
||||
dry_run=1
|
||||
shift
|
||||
;;
|
||||
-*)
|
||||
error "Unknown flag: $1"
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ $# -lt 2 ]]; then
|
||||
echo "Usage: $0 [--dry-run] <release-version> <pr-number>" >&2
|
||||
echo " e.g. $0 2.30 23969" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
release_version="$1"
|
||||
pr_number="$2"
|
||||
release_branch="release/${release_version}"
|
||||
|
||||
dependencies gh jq git
|
||||
|
||||
# Authenticate with GitHub.
|
||||
gh_auth
|
||||
|
||||
# Validate that the PR exists and is merged.
|
||||
log "Fetching PR #${pr_number}..."
|
||||
pr_json=$(gh pr view "$pr_number" --json mergeCommit,title,number,state,headRefName,url)
|
||||
|
||||
pr_state=$(echo "$pr_json" | jq -r '.state')
|
||||
if [[ "$pr_state" != "MERGED" ]]; then
|
||||
error "PR #${pr_number} is not merged (state: ${pr_state})."
|
||||
fi
|
||||
|
||||
merge_commit=$(echo "$pr_json" | jq -r '.mergeCommit.oid')
|
||||
pr_title=$(echo "$pr_json" | jq -r '.title')
|
||||
pr_url=$(echo "$pr_json" | jq -r '.url')
|
||||
|
||||
if [[ -z "$merge_commit" || "$merge_commit" == "null" ]]; then
|
||||
error "Could not determine merge commit for PR #${pr_number}."
|
||||
fi
|
||||
|
||||
log "PR: #${pr_number} - ${pr_title}"
|
||||
log "Merge commit: ${merge_commit}"
|
||||
log "Release branch: ${release_branch}"
|
||||
|
||||
# Make sure we have the latest refs.
|
||||
maybedryrun "$dry_run" git fetch origin
|
||||
|
||||
# Validate the release branch exists on the remote.
|
||||
if ! git rev-parse "origin/${release_branch}" >/dev/null 2>&1; then
|
||||
error "Release branch '${release_branch}' does not exist on origin."
|
||||
fi
|
||||
|
||||
backport_branch="backport/${pr_number}-to-${release_version}"
|
||||
log "Backport branch: ${backport_branch}"
|
||||
|
||||
if [[ "$dry_run" == 1 ]]; then
|
||||
log ""
|
||||
log "DRYRUN: Would cherry-pick ${merge_commit} onto ${release_branch} via branch ${backport_branch}"
|
||||
log "DRYRUN: Would create PR targeting ${release_branch}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Check for uncommitted changes that would block checkout.
|
||||
if ! git diff-index --quiet HEAD --; then
|
||||
error "You have uncommitted changes. Please commit or stash them first."
|
||||
fi
|
||||
|
||||
# Create the backport branch from the release branch.
|
||||
log "Creating branch ${backport_branch} from origin/${release_branch}..."
|
||||
git checkout -b "$backport_branch" "origin/${release_branch}"
|
||||
|
||||
# Cherry-pick the merge commit.
|
||||
log "Cherry-picking ${merge_commit}..."
|
||||
if ! git cherry-pick -x "$merge_commit"; then
|
||||
log ""
|
||||
log "Cherry-pick failed due to conflicts."
|
||||
log "Resolve the conflicts, then run:"
|
||||
log " git cherry-pick --continue"
|
||||
log " git push origin ${backport_branch}"
|
||||
log " gh pr create --base ${release_branch} --head ${backport_branch} --title \"chore: backport #${pr_number} to ${release_version}\" --body \"Backport of ${pr_url}\""
|
||||
log ""
|
||||
log "Or abort with: git cherry-pick --abort && git checkout - && git branch -D ${backport_branch}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Push the backport branch.
|
||||
log "Pushing ${backport_branch}..."
|
||||
git push origin "$backport_branch"
|
||||
|
||||
# Create the PR.
|
||||
log "Creating PR..."
|
||||
backport_pr_url=$(gh pr create \
|
||||
--draft \
|
||||
--label "cherry-pick/v${release_version}" \
|
||||
--base "$release_branch" \
|
||||
--head "$backport_branch" \
|
||||
--title "chore: backport #${pr_number} to ${release_version}" \
|
||||
--body "$(
|
||||
cat <<EOF
|
||||
Backport of ${pr_url}
|
||||
|
||||
Original PR: #${pr_number} — ${pr_title}
|
||||
Merge commit: ${merge_commit}
|
||||
EOF
|
||||
)")
|
||||
|
||||
log ""
|
||||
log "Backport PR created: ${backport_pr_url}"
|
||||
|
||||
# Return to previous branch.
|
||||
git checkout -
|
||||
Reference in New Issue
Block a user