Skip to content

Commit 76cbc58

Browse files
authored
ci: add cherry-pick PR check for release branches (#24121)
Adds a GitHub Actions workflow that runs on PRs targeting `release/*` branches to flag non-bug-fix cherry-picks. ## What it does - Triggers on `pull_request_target` (opened, reopened, edited) for `release/*` branches - Checks if the PR title starts with `fix:` or `fix(scope):` (conventional commit format) - If not a bug fix, comments on the PR informing the author and emits a warning (via `core.warning`), but does **not** fail the check - Deduplicates comments on title edits by updating an existing comment (identified by a hidden HTML marker) instead of creating a new one > [!NOTE] > Generated by Coder Agents
1 parent 391b22a commit 76cbc58

File tree

1 file changed

+93
-0
lines changed

1 file changed

+93
-0
lines changed
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# Ensures that only bug fixes are cherry-picked to release branches.
2+
# PRs targeting release/* must have a title starting with "fix:" or "fix(scope):".
3+
name: PR Cherry-Pick Check
4+
5+
on:
6+
# zizmor: ignore[dangerous-triggers] Only reads PR metadata and comments; does not checkout PR code.
7+
pull_request_target:
8+
types: [opened, reopened, edited]
9+
branches:
10+
- "release/*"
11+
12+
permissions:
13+
pull-requests: write
14+
15+
jobs:
16+
check-cherry-pick:
17+
runs-on: ubuntu-latest
18+
steps:
19+
- name: Harden Runner
20+
uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
21+
with:
22+
egress-policy: audit
23+
24+
- name: Check PR title for bug fix
25+
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
26+
with:
27+
script: |
28+
const title = context.payload.pull_request.title;
29+
const prNumber = context.payload.pull_request.number;
30+
const baseBranch = context.payload.pull_request.base.ref;
31+
const author = context.payload.pull_request.user.login;
32+
33+
console.log(`PR #${prNumber}: "${title}" -> ${baseBranch}`);
34+
35+
// Match conventional commit "fix:" or "fix(scope):" prefix.
36+
const isBugFix = /^fix(\(.+\))?:/.test(title);
37+
38+
if (isBugFix) {
39+
console.log("PR title indicates a bug fix. No action needed.");
40+
return;
41+
}
42+
43+
console.log("PR title does not indicate a bug fix. Commenting.");
44+
45+
// Check for an existing comment from this bot to avoid duplicates
46+
// on title edits.
47+
const { data: comments } = await github.rest.issues.listComments({
48+
owner: context.repo.owner,
49+
repo: context.repo.repo,
50+
issue_number: prNumber,
51+
});
52+
53+
const marker = "<!-- cherry-pick-check -->";
54+
const existingComment = comments.find(
55+
(c) => c.body && c.body.includes(marker),
56+
);
57+
58+
const body = [
59+
marker,
60+
`👋 Hey @${author}!`,
61+
"",
62+
`This PR is targeting the \`${baseBranch}\` release branch, but its title does not start with \`fix:\` or \`fix(scope):\`.`,
63+
"",
64+
"Only **bug fixes** should be cherry-picked to release branches. If this is a bug fix, please update the PR title to match the conventional commit format:",
65+
"",
66+
"```",
67+
"fix: description of the bug fix",
68+
"fix(scope): description of the bug fix",
69+
"```",
70+
"",
71+
"If this is **not** a bug fix, it likely should not target a release branch.",
72+
].join("\n");
73+
74+
if (existingComment) {
75+
console.log(`Updating existing comment ${existingComment.id}.`);
76+
await github.rest.issues.updateComment({
77+
owner: context.repo.owner,
78+
repo: context.repo.repo,
79+
comment_id: existingComment.id,
80+
body,
81+
});
82+
} else {
83+
await github.rest.issues.createComment({
84+
owner: context.repo.owner,
85+
repo: context.repo.repo,
86+
issue_number: prNumber,
87+
body,
88+
});
89+
}
90+
91+
core.warning(
92+
`PR #${prNumber} targets ${baseBranch} but is not a bug fix. Title must start with "fix:" or "fix(scope):".`,
93+
);

0 commit comments

Comments
 (0)