fix(ci): Stop review bots for closed PRs (#6304)

* fix(ci): stop review bots for closed PRs

* test(ci): scope closed review workflow assertions

* test(ci): cover closed PR review guards
This commit is contained in:
易良 2026-07-04 22:16:47 +08:00 committed by GitHub
parent 4400fe6a38
commit ba7561af49
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 49 additions and 3 deletions

View file

@ -8,6 +8,7 @@ on:
- 'reopened'
- 'ready_for_review'
- 'review_requested'
- 'closed'
issue_comment:
types: ['created']
pull_request_review_comment:
@ -48,18 +49,25 @@ on:
type: 'boolean'
concurrency:
# PR lifecycle events share a PR-scoped group so new pushes restart the delay.
# PR lifecycle events share a PR-scoped group so new pushes restart the delay
# and closed PRs stop any in-flight lifecycle review.
# Comment/review events use per-run groups to avoid cancelling active reviews.
group: >-
${{ github.event_name == 'pull_request_target' &&
format('qwen-pr-review-pr-{0}', github.event.pull_request.number) ||
format('qwen-pr-review-run-{0}', github.run_id) }}
cancel-in-progress: "${{ github.event_name == 'pull_request_target' && github.event.action == 'synchronize' }}"
cancel-in-progress: >-
${{
github.event_name == 'pull_request_target' &&
(github.event.action == 'synchronize' ||
github.event.action == 'closed')
}}
jobs:
precheck-pr:
if: |-
github.event_name == 'pull_request_target' &&
github.event.action != 'closed' &&
github.event.pull_request.head.repo.full_name != github.repository &&
(github.event.action != 'review_requested' ||
github.event.requested_reviewer.login == 'qwen-code-ci-bot')
@ -207,6 +215,8 @@ jobs:
# `if`s still do the exact command body match; this prefix is just a filter.
if: |-
always() &&
(github.event_name != 'pull_request_target' ||
github.event.action != 'closed') &&
(github.event_name != 'pull_request_target' ||
github.event.pull_request.head.repo.full_name == github.repository ||
needs.precheck-pr.outputs.decision == 'allow_triage') &&

View file

@ -62,6 +62,7 @@ jobs:
needs.precheck-pr.outputs.decision == 'allow_triage') &&
(github.event_name == 'pull_request_target' ||
(github.event_name == 'issue_comment' &&
github.event.issue.state == 'open' &&
(startsWith(github.event.comment.body, '@qwen-code /triage') ||
github.event.comment.body == '@qwen-code /tmux' ||
startsWith(github.event.comment.body, '@qwen-code /tmux '))) ||
@ -160,7 +161,8 @@ jobs:
(github.event.pull_request.draft == true ||
needs.authorize.outputs.should_run != 'true')) ||
(github.event_name == 'issue_comment' &&
needs.authorize.outputs.should_run != 'true')
(github.event.issue.state != 'open' ||
needs.authorize.outputs.should_run != 'true'))
) &&
format('{0}-run-{1}', github.workflow, github.run_id) ||
format('{0}-{1}', github.workflow, github.event.issue.number || github.event.pull_request.number || github.event.inputs.number)
@ -172,6 +174,7 @@ jobs:
(((github.event_name == 'pull_request_target' &&
github.event.pull_request.draft == false) ||
(github.event_name == 'issue_comment' &&
github.event.issue.state == 'open' &&
startsWith(github.event.comment.body, '@qwen-code /triage'))) &&
needs.authorize.outputs.should_run == 'true')
}}
@ -197,6 +200,7 @@ jobs:
((github.event_name == 'pull_request_target' &&
github.event.pull_request.draft == false) ||
(github.event_name == 'issue_comment' &&
github.event.issue.state == 'open' &&
startsWith(github.event.comment.body, '@qwen-code /triage'))) &&
needs.authorize.outputs.should_run == 'true'
)
@ -298,6 +302,7 @@ jobs:
(
(github.event_name == 'issue_comment' &&
github.event.issue.pull_request &&
github.event.issue.state == 'open' &&
(github.event.comment.body == '@qwen-code /tmux' ||
startsWith(github.event.comment.body, '@qwen-code /tmux ')) &&
needs.authorize.outputs.should_run == 'true') ||
@ -316,6 +321,7 @@ jobs:
(
((github.event_name == 'issue_comment' &&
github.event.issue.pull_request &&
github.event.issue.state == 'open' &&
(github.event.comment.body == '@qwen-code /tmux' ||
startsWith(github.event.comment.body, '@qwen-code /tmux '))) ||
(github.event_name == 'workflow_dispatch' &&

View file

@ -65,6 +65,20 @@ describe('qwen resolve workflow', () => {
);
});
it('cancels in-flight lifecycle reviews when the PR closes', () => {
const concurrencyStart = workflow.indexOf('\nconcurrency:');
const concurrency = workflow.slice(
concurrencyStart,
workflow.indexOf('\njobs:', concurrencyStart),
);
expect(workflow).toContain("- 'closed'");
expect(concurrency).toContain("github.event.action == 'closed'");
expect(concurrency).toContain(
"format('qwen-pr-review-pr-{0}', github.event.pull_request.number)",
);
});
it('listens for /resolve comments', () => {
expect(workflow).toContain(
"github.event.comment.body == '@qwen-code /resolve'",
@ -178,6 +192,12 @@ describe('qwen resolve workflow', () => {
: workflow.slice(resolveJobStart, resolveJobStart + 1 + nextJob);
const reviewJob = job(workflow, 'review-pr');
const authorizeJob = job(workflow, 'authorize');
const precheckJob = job(workflow, 'precheck-pr');
it('keeps closed PR events from running precheck or authorize jobs', () => {
expect(precheckJob).toContain("github.event.action != 'closed'");
expect(authorizeJob).toContain("github.event.action != 'closed'");
});
it('does not require fork PR authors to have write permission for automatic review', () => {
const authorizeStep = step(

View file

@ -60,6 +60,16 @@ describe('qwen-triage tmux workflow', () => {
);
});
it('requires open issues or PRs for comment-triggered triage', () => {
const authorizeJob = job('authorize');
const triageJob = job('triage');
const tmuxJob = job('tmux-testing');
expect(authorizeJob).toContain("github.event.issue.state == 'open'");
expect(triageJob).toContain("github.event.issue.state == 'open'");
expect(tmuxJob).toContain("github.event.issue.state == 'open'");
});
it('escapes embedded tmux artifacts without bash pattern replacement ampersands', () => {
const postStep = step('Post tmux result comment');