806b1ac921
@Luca-Timo is on main's review-bypass list so he can self-merge small
bugfixes without waiting for a maintainer review. The bypass list alone
is binary (he can merge anything), so this adds a complementary required
status check that fails when a bypass user's PR exceeds a configured
line-count threshold — blocking merge for genuine features while leaving
small bugfixes flowing.
How it works:
- Trigger: pull_request_target (so the workflow runs in the base repo's
context with permissions to write a check status — script never
executes PR code, so fork-PR-attack-safe).
- For PRs authored by a bypass user (default: @Luca-Timo):
- linesChanged = additions + deletions
- If ≤ LINE_LIMIT (300): check = success → bypass works → self-merge OK
- If > LINE_LIMIT: check = failure → required-check gate blocks merge
regardless of bypass; needs a maintainer review.
- For everyone else: check = success ("not applicable"). They go through
the normal review path and are unaffected.
Both constants (LINE_LIMIT, BYPASS_USERS) are at the top of the workflow
for easy tuning.
After this lands on main, a separate API step adds 'bypass-size-gate' to
the main branch's required_status_checks list so the gate is actually
enforced. Until that's in place the check runs but doesn't block.
71 lines
3.2 KiB
YAML
71 lines
3.2 KiB
YAML
name: Bypass size gate
|
|
|
|
# Caps how large a PR a "review-bypass" collaborator (e.g. @Luca-Timo) can
|
|
# self-merge without a maintainer review. The branch-protection bypass list
|
|
# alone is binary — once a user is on it they can merge anything without
|
|
# review. This workflow reports a REQUIRED status check that fails when a
|
|
# bypass user's PR exceeds the configured size threshold, which blocks the
|
|
# merge even with bypass enabled. Other contributors are unaffected (the
|
|
# check reports success for them so the required-check gate doesn't trip).
|
|
#
|
|
# To tune: edit LINE_LIMIT or BYPASS_USERS below.
|
|
#
|
|
# Trigger note: uses `pull_request_target` so the workflow has the elevated
|
|
# permissions of the base repo's GITHUB_TOKEN (read PR metadata, write
|
|
# checks). The script never executes code FROM the PR — it only reads
|
|
# metadata via the API — so this is safe against fork-PR attacks.
|
|
|
|
on:
|
|
pull_request_target:
|
|
types: [opened, reopened, synchronize, ready_for_review]
|
|
|
|
permissions:
|
|
pull-requests: read
|
|
checks: write
|
|
|
|
jobs:
|
|
size-gate:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Compute PR size and report check status
|
|
uses: actions/github-script@v7
|
|
with:
|
|
script: |
|
|
// Tune these two constants if the policy shifts.
|
|
const LINE_LIMIT = 300;
|
|
const BYPASS_USERS = ['Luca-Timo'];
|
|
|
|
const pr = context.payload.pull_request;
|
|
const author = pr.user.login;
|
|
const linesChanged = pr.additions + pr.deletions;
|
|
const filesChanged = pr.changed_files;
|
|
|
|
let conclusion, title, summary;
|
|
|
|
if (!BYPASS_USERS.includes(author)) {
|
|
// Not a bypass user — this gate doesn't apply to them. They
|
|
// go through normal review. Report success so the required
|
|
// check doesn't block their merge.
|
|
conclusion = 'success';
|
|
title = 'Not applicable';
|
|
summary = `This gate only restricts review-bypass for: ${BYPASS_USERS.join(', ')}. PRs from other authors (${author} here) go through the normal review path and are unaffected.`;
|
|
} else if (linesChanged <= LINE_LIMIT) {
|
|
conclusion = 'success';
|
|
title = `OK — within bypass limit (${linesChanged} lines)`;
|
|
summary = `Small PR: ${linesChanged} lines changed across ${filesChanged} file(s). Within the ${LINE_LIMIT}-line self-merge limit for @${author}. Can be merged without a maintainer review.`;
|
|
} else {
|
|
conclusion = 'failure';
|
|
title = `Too large for bypass (${linesChanged} lines)`;
|
|
summary = `Large PR: ${linesChanged} lines changed across ${filesChanged} file(s). Exceeds the ${LINE_LIMIT}-line self-merge limit for @${author} — needs an approving review from a maintainer before merge. Split into smaller PRs or wait for review.`;
|
|
}
|
|
|
|
await github.rest.checks.create({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
name: 'bypass-size-gate',
|
|
head_sha: pr.head.sha,
|
|
status: 'completed',
|
|
conclusion,
|
|
output: { title, summary }
|
|
});
|