claude-plugins-official/.github/workflows/close-external-prs.yml
Bryan Thompson fb608cb095
feat(ci): allow live external contributors to open scoped PRs
Add an opt-in allowlist so a vetted external developer who already has a
plugin live in this marketplace — but cannot use the submission form
(e.g. an enterprise partner without a Claude account) — can open a
reviewable PR instead of having it auto-closed.

- .github/external-contributors.json: username -> allowed_sources map
  (doubles as the allowlist and the per-author source scope).
- close-external-prs.yml: skip the auto-close for allowlisted authors
  (reads the list from the trusted base checkout). Grants ONLY the right
  to open a PR; CI + maintainer approval are unchanged.
- external-pr-scope-guard.yml: required check for allowlisted external
  authors. Fails unless the PR touches ONLY marketplace.json and the
  delta is additions-only, with every added entry's source.url under
  that author's allowed_sources. Anthropic members are unrestricted.
  Reads head marketplace.json as data via the API (no untrusted checkout).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 11:07:53 -05:00

69 lines
2.7 KiB
YAML

name: Close External PRs
on:
pull_request_target:
types: [opened]
permissions:
pull-requests: write
issues: write
jobs:
check-membership:
if: vars.DISABLE_EXTERNAL_PR_CHECK != 'true'
runs-on: ubuntu-latest
steps:
# pull_request_target: this checks out the BASE repo (trusted), so the allowlist
# we read below is this repo's version, never the fork's.
- uses: actions/checkout@v4
- name: Check if author has write access
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const author = context.payload.pull_request.user.login;
const { data } = await github.rest.repos.getCollaboratorPermissionLevel({
owner: context.repo.owner,
repo: context.repo.repo,
username: author
});
if (['admin', 'write'].includes(data.permission)) {
console.log(`${author} has ${data.permission} access, allowing PR`);
return;
}
// Allowlisted external contributors (e.g. enterprise partners who cannot use the
// submission form) may OPEN a PR. This does NOT grant merge: all CI still runs,
// the external-pr-scope-guard check constrains what they can change, and an
// Anthropic maintainer approval is still required by branch protection.
try {
const list = JSON.parse(fs.readFileSync('.github/external-contributors.json', 'utf8'));
const allowed = (list.contributors || []).some(
c => (c.github_username || '').toLowerCase() === author.toLowerCase()
);
if (allowed) {
console.log(`${author} is an allowlisted external contributor, allowing PR (scope guard + maintainer review still apply)`);
return;
}
} catch (e) {
console.log(`Could not read external-contributors allowlist: ${e.message}`);
}
console.log(`${author} has ${data.permission} access, closing PR`);
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number,
body: `Thanks for your interest! This repo only accepts contributions from Anthropic team members. If you'd like to submit a plugin to the marketplace, please submit your plugin [here](https://clau.de/plugin-directory-submission).`
});
await github.rest.pulls.update({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.payload.pull_request.number,
state: 'closed'
});