Skip to content

Commit 75dcc14

Browse files
ci: Merge PR validation workflows and add reason-specific labels (#5898)
Combine `close-unvetted-pr.yml` and `enforce-draft-pr.yml` into a single `validate-pr.yml` workflow so the draft enforcer doesn't run on PRs that were already closed by the vetting check. **How it works:** - `validate-non-maintainer-pr` job runs first on `opened` and `reopened` events, outputs `was-closed` if it closes the PR - `enforce-draft` job depends on the first job via `needs:` and skips when `was-closed == 'true'` (uses `if: always()` so it still runs when the first job is skipped or succeeds without closing) Each closure reason now also gets a specific label alongside `violating-contribution-guidelines`: - `missing-issue-reference` — no issue ref in body, or referenced issues not found - `missing-maintainer-discussion` — no prior discussion between author and a maintainer - `issue-already-assigned` — referenced issue assigned to someone other than the PR author Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent de650c8 commit 75dcc14

File tree

2 files changed

+84
-81
lines changed

2 files changed

+84
-81
lines changed

.github/workflows/enforce-draft-pr.yml

Lines changed: 0 additions & 72 deletions
This file was deleted.
Lines changed: 84 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
name: Close Unvetted Non-Maintainer PRs
1+
name: Validate PR
22

33
on:
44
pull_request_target:
5-
types: [opened]
5+
types: [opened, reopened]
66

77
jobs:
88
validate-non-maintainer-pr:
@@ -11,6 +11,8 @@ jobs:
1111
permissions:
1212
pull-requests: write
1313
contents: write
14+
outputs:
15+
was-closed: ${{ steps.validate.outputs.was-closed }}
1416
steps:
1517
- name: Generate GitHub App token
1618
id: app-token
@@ -20,6 +22,7 @@ jobs:
2022
private-key: ${{ secrets.SDK_MAINTAINER_BOT_PRIVATE_KEY }}
2123

2224
- name: Validate PR
25+
id: validate
2326
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
2427
with:
2528
github-token: ${{ steps.app-token.outputs.token }}
@@ -101,12 +104,12 @@ jobs:
101104
102105
core.info(`Found ${issueRefs.length} issue reference(s): ${[...seen].join(', ')}`);
103106
104-
// --- Helper: close PR with comment and label ---
105-
async function closePR(message) {
107+
// --- Helper: close PR with comment and labels ---
108+
async function closePR(message, reasonLabel) {
106109
await github.rest.issues.addLabels({
107110
...repo,
108111
issue_number: pullRequest.number,
109-
labels: ['violating-contribution-guidelines'],
112+
labels: ['violating-contribution-guidelines', reasonLabel],
110113
});
111114
112115
await github.rest.issues.createComment({
@@ -120,6 +123,8 @@ jobs:
120123
pull_number: pullRequest.number,
121124
state: 'closed',
122125
});
126+
127+
core.setOutput('was-closed', 'true');
123128
}
124129
125130
// --- Step 3: No issue references ---
@@ -134,7 +139,7 @@ jobs:
134139
'3. Once a maintainer has acknowledged your proposed approach, open a new PR referencing the issue',
135140
'',
136141
`Please review our [contributing guidelines](${contributingUrl}) for more details.`,
137-
].join('\n'));
142+
].join('\n'), 'missing-issue-reference');
138143
return;
139144
}
140145
@@ -222,7 +227,7 @@ jobs:
222227
'If you believe this assignment is outdated, please comment on the issue to discuss before opening a new PR.',
223228
'',
224229
`Please review our [contributing guidelines](${contributingUrl}) for more details.`,
225-
].join('\n'));
230+
].join('\n'), 'issue-already-assigned');
226231
return;
227232
}
228233
@@ -234,7 +239,7 @@ jobs:
234239
'To avoid wasted effort on both sides, please discuss your proposed approach in the issue first and wait for a maintainer to respond before opening a PR.',
235240
'',
236241
`Please review our [contributing guidelines](${contributingUrl}) for more details.`,
237-
].join('\n'));
242+
].join('\n'), 'missing-maintainer-discussion');
238243
return;
239244
}
240245
@@ -249,4 +254,74 @@ jobs:
249254
'3. Once a maintainer has acknowledged your proposed approach, open a new PR referencing the issue',
250255
'',
251256
`Please review our [contributing guidelines](${contributingUrl}) for more details.`,
252-
].join('\n'));
257+
].join('\n'), 'missing-issue-reference');
258+
259+
enforce-draft:
260+
name: Enforce Draft PR
261+
needs: [validate-non-maintainer-pr]
262+
if: |
263+
always()
264+
&& github.event.pull_request.draft == false
265+
&& needs.validate-non-maintainer-pr.outputs.was-closed != 'true'
266+
runs-on: ubuntu-24.04
267+
permissions:
268+
pull-requests: write
269+
contents: write
270+
steps:
271+
- name: Generate GitHub App token
272+
id: app-token
273+
uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v2
274+
with:
275+
app-id: ${{ vars.SDK_MAINTAINER_BOT_APP_ID }}
276+
private-key: ${{ secrets.SDK_MAINTAINER_BOT_PRIVATE_KEY }}
277+
278+
- name: Convert PR to draft
279+
env:
280+
GH_TOKEN: ${{github.token}}
281+
PR_URL: ${{ github.event.pull_request.html_url }}
282+
run: |
283+
gh pr ready "$PR_URL" --undo
284+
285+
- name: Label and comment
286+
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
287+
with:
288+
github-token: ${{ steps.app-token.outputs.token }}
289+
script: |
290+
const pullRequest = context.payload.pull_request;
291+
const repo = context.repo;
292+
293+
// Label the PR so maintainers can filter/track violations
294+
await github.rest.issues.addLabels({
295+
...repo,
296+
issue_number: pullRequest.number,
297+
labels: ['converted-to-draft'],
298+
});
299+
300+
// Check for existing bot comment to avoid duplicates on reopen
301+
const comments = await github.rest.issues.listComments({
302+
...repo,
303+
issue_number: pullRequest.number,
304+
});
305+
const botComment = comments.data.find(c =>
306+
c.user.type === 'Bot' &&
307+
c.body.includes('automatically converted to draft')
308+
);
309+
if (botComment) {
310+
core.info('Bot comment already exists, skipping.');
311+
return;
312+
}
313+
314+
const contributingUrl = `https://github.com/${repo.owner}/${repo.repo}/blob/master/CONTRIBUTING.md`;
315+
316+
await github.rest.issues.createComment({
317+
...repo,
318+
issue_number: pullRequest.number,
319+
body: [
320+
`This PR has been automatically converted to draft. All PRs must start as drafts per our [contributing guidelines](${contributingUrl}).`,
321+
'',
322+
'**Next steps:**',
323+
'1. Ensure CI passes',
324+
'2. Fill in the PR description completely',
325+
'3. Mark as "Ready for review" when you\'re done'
326+
].join('\n')
327+
});

0 commit comments

Comments
 (0)