fix: add contribution gate dry run mode

This commit is contained in:
Nightt
2026-06-02 11:17:33 +08:00
committed by Hunter B
parent c8c20e0931
commit dfe1884702
5 changed files with 92 additions and 16 deletions
+2 -1
View File
@@ -2,9 +2,10 @@
#
# Maintainers and collaborators bypass the gate automatically. Use this file
# for external contributors who are allowed through the automated front door.
# Seed active contributors here before switching the gate workflows to enforce mode.
#
# Supported entries:
# pr:username
# issue:username
# all:username
all:Hmbown
all:hmbown
+29 -1
View File
@@ -9,6 +9,10 @@ permissions:
issues: write
pull-requests: write
concurrency:
group: contribution-gate-approval
cancel-in-progress: false
jobs:
approve:
runs-on: ubuntu-latest
@@ -56,12 +60,14 @@ jobs:
const targetLogin = issue.user.login;
const normalizedLogin = targetLogin.toLowerCase();
const entry = `${scope}:${normalizedLogin}`;
const branchSlug = normalizedLogin.replace(/[^a-z0-9._-]+/g, '-').replace(/^-+|-+$/g, '') || 'contributor';
const defaultContent = [
'# Scoped contribution-gate allowlist.',
'#',
'# Maintainers and collaborators bypass the gate automatically. Use this file',
'# for external contributors who are allowed through the automated front door.',
'# Seed active contributors here before switching the gate workflows to enforce mode.',
'#',
'# Supported entries:',
'# pr:username',
@@ -119,6 +125,29 @@ jobs:
return;
}
const { data: openPrs } = await github.rest.pulls.list({
owner,
repo,
state: 'open',
per_page: 100,
});
const repoFullName = `${owner}/${repo}`.toLowerCase();
const pendingPr = openPrs.find(openPr => {
const sameRepo = (openPr.head?.repo?.full_name || '').toLowerCase() === repoFullName;
const body = openPr.body || '';
return sameRepo && body.includes(`Adds \`${entry}\` to \`${path}\`.`);
});
if (pendingPr) {
await github.rest.issues.createComment({
owner,
repo,
issue_number: issue.number,
body: `@${targetLogin} already has a pending allowlist update PR for ${scope} contributions: ${pendingPr.html_url}`,
});
return;
}
const nextContent = `${content.trimEnd()}\n${entry}\n`;
const { data: blob } = await github.rest.git.createBlob({
owner,
@@ -140,7 +169,6 @@ jobs:
],
});
const branchSlug = normalizedLogin.replace(/[^a-z0-9._-]+/g, '-').replace(/^-+|-+$/g, '') || 'contributor';
const branchName = `contribution-gate/${scope}-${branchSlug}-${Date.now()}`;
await github.rest.git.createRef({
owner,
+19 -2
View File
@@ -8,11 +8,16 @@ permissions:
contents: read
issues: write
env:
# Keep new gates observable first. Switch to "enforce" only after maintainers
# have seeded active contributors and reviewed the dry-run signal.
CONTRIBUTION_GATE_MODE: dry-run
jobs:
gate:
runs-on: ubuntu-latest
steps:
- name: Close unapproved external issues
- name: Gate unapproved external issues
uses: actions/github-script@v7
with:
script: |
@@ -20,6 +25,12 @@ jobs:
const owner = context.repo.owner;
const repo = context.repo.repo;
const privileged = new Set(['OWNER', 'MEMBER', 'COLLABORATOR']);
const gateMode = (process.env.CONTRIBUTION_GATE_MODE || 'dry-run').trim().toLowerCase();
const enforceGate = gateMode === 'enforce';
if (!['dry-run', 'enforce'].includes(gateMode)) {
core.warning(`Unknown CONTRIBUTION_GATE_MODE "${gateMode}"; defaulting to dry-run.`);
}
if (privileged.has(issue.author_association)) return;
if (issue.user.login === 'github-actions[bot]') return;
@@ -60,6 +71,10 @@ jobs:
return;
}
const gateMessage = enforceGate
? 'This repository currently uses a maintainer-managed contribution gate, so issues from contributors who are not listed in `.github/APPROVED_CONTRIBUTORS` are closed automatically.'
: 'This repository is currently observing a maintainer-managed contribution gate in dry-run mode, so this issue is staying open. When enforcement is enabled, issues from contributors who are not listed in `.github/APPROVED_CONTRIBUTORS` will be closed automatically.';
await github.rest.issues.createComment({
owner,
repo,
@@ -67,12 +82,14 @@ jobs:
body: [
`Thanks @${issue.user.login} for the report.`,
'',
'This repository currently uses a maintainer-managed contribution gate, so issues from contributors who are not listed in `.github/APPROVED_CONTRIBUTORS` are closed automatically.',
gateMessage,
'',
'Please read `CONTRIBUTING.md` for the expected issue shape. A maintainer can grant issue access by commenting `/lgtmi` on an issue.',
].join('\n'),
});
if (!enforceGate) return;
await github.rest.issues.update({
owner,
repo,
+19 -2
View File
@@ -9,11 +9,16 @@ permissions:
issues: write
pull-requests: write
env:
# Keep new gates observable first. Switch to "enforce" only after maintainers
# have seeded active contributors and reviewed the dry-run signal.
CONTRIBUTION_GATE_MODE: dry-run
jobs:
gate:
runs-on: ubuntu-latest
steps:
- name: Close unapproved external pull requests
- name: Gate unapproved external pull requests
uses: actions/github-script@v7
with:
script: |
@@ -21,6 +26,12 @@ jobs:
const owner = context.repo.owner;
const repo = context.repo.repo;
const privileged = new Set(['OWNER', 'MEMBER', 'COLLABORATOR']);
const gateMode = (process.env.CONTRIBUTION_GATE_MODE || 'dry-run').trim().toLowerCase();
const enforceGate = gateMode === 'enforce';
if (!['dry-run', 'enforce'].includes(gateMode)) {
core.warning(`Unknown CONTRIBUTION_GATE_MODE "${gateMode}"; defaulting to dry-run.`);
}
if (privileged.has(pr.author_association)) return;
if (pr.user.login === 'github-actions[bot]') return;
@@ -61,6 +72,10 @@ jobs:
return;
}
const gateMessage = enforceGate
? 'This repository currently uses a maintainer-managed contribution gate, so pull requests from contributors who are not listed in `.github/APPROVED_CONTRIBUTORS` are closed automatically.'
: 'This repository is currently observing a maintainer-managed contribution gate in dry-run mode, so this pull request is staying open. When enforcement is enabled, pull requests from contributors who are not listed in `.github/APPROVED_CONTRIBUTORS` will be closed automatically.';
await github.rest.issues.createComment({
owner,
repo,
@@ -68,12 +83,14 @@ jobs:
body: [
`Thanks @${pr.user.login} for taking the time to contribute.`,
'',
'This repository currently uses a maintainer-managed contribution gate, so pull requests from contributors who are not listed in `.github/APPROVED_CONTRIBUTORS` are closed automatically.',
gateMessage,
'',
'Please read `CONTRIBUTING.md` for the expected contribution shape. A maintainer can grant PR access by commenting `/lgtm` on a pull request.',
].join('\n'),
});
if (!enforceGate) return;
await github.rest.pulls.update({
owner,
repo,