Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ Continuously inspect automatic E2E results for `main`. Coordinate ownership and
2. Keep release operations out of scope. Never change, retag, publish, or otherwise touch a release, tag, or release artifact during this workflow. Route release work to the existing release workflow.
3. Confirm maintainer authorization. Merge only when the request grants it. Otherwise, leave the PR `approval-ready` and continue the loop.
4. Check Git and GitHub access. Follow [Git and GitHub Access Hard Stop](../_shared/git-github-hard-stop.md) on access failure.
5. Fetch trusted `origin/main`. Read its PR-limit policy with `git show origin/main:.github/workflows/pr-limit.yaml`. For a non-exempt author, do not create a claim that would exceed that limit.

Do not declare success or end the loop because the queue is empty or the newest run passes. Wait for the next automatic `main` result.

Expand Down
20 changes: 20 additions & 0 deletions .github/pr-limits.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"apurvvkumaria": "10",
"chengjiew": "10",
"cjagwani": "10",
"cv": "10",
"dongni-yang": "10",
"ericksoa": "10",
"jyaunches": "10",
"laitingsheng": "10",
"prekshivyas": "10",
"rluo8": "10",
"rsliter": "10",
"sandl99": "10",
"senthilr-nv": "10",
"tedy-y": "10",
"tonyluo-nv": "10",
"wscurran": "10",
"yimoj": "10",
"*": "5"
}
101 changes: 70 additions & 31 deletions .github/workflows/pr-limit.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,49 +3,88 @@

name: Automation / PR Limit

# pull_request_target runs in the base repo context, giving the token write
# access even for fork PRs. This is safe here because this workflow never
# checks out or executes code from the PR — it only counts open PRs and
# closes excess ones. Do NOT add a checkout step or run PR-sourced code
# in this workflow to prevent injection attacks.
# pull_request_target runs in the base repository context. This workflow never
# checks out or executes pull request code.
on:
pull_request_target:
types: [opened, reopened]

permissions:
contents: read
pull-requests: write

jobs:
check-pr-limit:
runs-on: ubuntu-latest
timeout-minutes: 5
concurrency:
group: pr-limit-${{ github.repository }}-${{ github.event.pull_request.user.login }}
queue: max
cancel-in-progress: false
Comment thread
coderabbitai[bot] marked this conversation as resolved.
steps:
- name: Check open PR count for author
env:
GH_TOKEN: ${{ github.token }}
AUTHOR: ${{ github.event.pull_request.user.login }}
PR_NUMBER: ${{ github.event.pull_request.number }}
REPO: ${{ github.repository }}
run: |
LIMIT=5
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
const pull = context.payload.pull_request;
const author = pull.user.login;
const response = await github.rest.repos.getContent({
...context.repo,
path: ".github/pr-limits.json",
ref: pull.base.sha,
});
if (Array.isArray(response.data) || response.data.type !== "file") {
throw new Error("Invalid PR limit policy file");
}
const policy = JSON.parse(Buffer.from(response.data.content, "base64").toString());
if (
typeof policy !== "object" ||
policy === null ||
Array.isArray(policy) ||
!("*" in policy) ||
!Object.entries(policy).every(
([login, value]) =>
(login === "*" ||
/^(?=.{1,39}$)[a-z\d](?:(?:[a-z\d]|-(?!-))*[a-z\d])?$/.test(login)) &&
typeof value === "string" &&
/^(0|[1-9]\d*)$/.test(value) &&
Number.isSafeInteger(Number(value)),
)
) {
throw new Error("Invalid PR limit policy values");
}
const normalizedAuthor = author.toLowerCase();
const limit = Number(
Object.hasOwn(policy, normalizedAuthor) ? policy[normalizedAuthor] : policy["*"],
);
const issues = await github.paginate(github.rest.issues.listForRepo, {
...context.repo,
state: "open",
creator: author,
sort: "created",
direction: "asc",
per_page: 100,
});
const openCount = issues.filter(
(issue) =>
issue.pull_request &&
(context.payload.action === "reopened" || issue.number <= pull.number),
).length;

# Core maintainers are exempt from the PR limit.
EXEMPT="ericksoa kjw3 jacobtomlinson cv jyaunches"
for user in $EXEMPT; do
if [ "$AUTHOR" = "$user" ]; then
echo "Author $AUTHOR is a core maintainer — exempt from PR limit"
exit 0
fi
done
core.info(`Author ${author} has ${openCount} open PR(s); limit is ${limit}`);
if (openCount <= limit) return;

OPEN_COUNT=$(gh pr list --repo "$REPO" --author "$AUTHOR" --state open --json number --jq 'length')

echo "Author $AUTHOR has $OPEN_COUNT open PR(s)"

if [ "$OPEN_COUNT" -gt "$LIMIT" ]; then
gh pr comment "$PR_NUMBER" --repo "$REPO" --body \
"This repository limits contributors to $LIMIT open pull requests. Please close or merge existing PRs before opening new ones."
gh pr close "$PR_NUMBER" --repo "$REPO"
echo "::error::PR closed — author $AUTHOR exceeds the $LIMIT open PR limit"
exit 1
fi
const body = limit === 0
? "This account cannot have open pull requests under the repository policy."
: `This repository limits you to ${limit} open pull requests. Please close or merge an existing PR before opening another one.`;
await github.rest.issues.createComment({
...context.repo,
issue_number: pull.number,
body,
});
await github.rest.pulls.update({
...context.repo,
pull_number: pull.number,
state: "closed",
});
core.setFailed(`PR closed because ${author} exceeds the ${limit}-open-PR limit`);
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -344,4 +344,4 @@ If the command trace contains no reviewer-request write, report the event as an
- Direct PRs follow `.github/PULL_REQUEST_TEMPLATE.md`; the managed documentation workflow uses its generated body
- PRs that change `scripts/prepare-dgx-station-host.sh` must include reviewable DGX Station test evidence identifying the tested commit, Station profile or scenario, result, and a supporting link. Any maintainer may review the evidence; without acceptable evidence, the PR is not ready to approve or merge. Treat the evidence as human-reviewed, not authenticated hardware provenance. Exceptional bypasses use existing repository governance and must document the reason on the PR.
- No secrets, API keys, or credentials committed
- Apply the open PR limit from `.github/workflows/pr-limit.yaml` only to accounts that the workflow does not exempt
- Check `.github/pr-limits.json` for the contributor's open PR limit.
8 changes: 4 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -511,8 +511,7 @@ For Markdown docs routing, user-skill guidance, and release-prep documentation w

## Pull Requests

We welcome contributions. Every PR requires maintainer review before merge. Contributors may have up to 5 open PRs at one time.
Core maintainers listed in `.github/workflows/pr-limit.yaml` are exempt from this limit.
We welcome contributions. Every PR requires maintainer review before merge. The default limit is 5 open PRs at one time.
Maintainers review pull requests according to project priority, security impact, release readiness, and reviewer availability.
PRs that solve issues with Priority set to Urgent or High are more likely to receive earlier review when maintainers have capacity.
For substantial features or behavior changes, start with a GitHub Discussion before opening a large implementation PR.
Expand Down Expand Up @@ -561,8 +560,9 @@ If the PR description is missing the DCO declaration, update the PR description
If any commit is missing GitHub verification, fix the branch before opening a PR.
If force-push is not allowed after an unverified commit is published, open a fresh branch and fresh PR with a clean compliant history.

> [!WARNING]
> Non-exempt accounts that repeatedly exceed this limit or submit automated bulk PRs may have their PRs closed or their access restricted.
When an opened or reopened PR exceeds the account's limit, automation comments on and closes that PR.

> Accounts that submit automated bulk PRs may have their PRs closed or their access restricted.

### No External Project Links

Expand Down
Loading