Fix Maintainer Approval on fork PRs; drop PR Template required check

Fork PRs get a read-only pull_request token, so the Maintainer Approval
status POST 403'd and failed the run. Skip the POST on the fork
pull_request event; the required check stays unreported until a
maintainer review posts it from base context.

Remove the PR Template required check: outside contributors can't match
the structured template, so it would block every fork PR. Delete
pr-template.yml and drop "PR Template" from required.sh.

Signed-off-by: dbczumar <corey.zumar@databricks.com>
This commit is contained in:
dbczumar
2026-06-13 12:53:40 -07:00
parent a003b8f169
commit 0d4d636171
3 changed files with 34 additions and 59 deletions
+2 -3
View File
@@ -1,11 +1,10 @@
# Sourced by evaluate-checks.sh. Only always-on checks are required (unit
# tests, lint, type-check, PR template). The e2e and integration suites run
# on schedule/dispatch only and are intentionally absent from REQUIRED and
# tests, lint, type-check). The e2e and integration suites run on
# schedule/dispatch only and are intentionally absent from REQUIRED and
# ALLOW_SKIP so missing checks do not block contributor PRs.
# Generated file -- do not hand-edit; it is replaced wholesale on every sync.
REQUIRED=(
"PR Template"
"Pre-commit checks"
"Pytest (runtime-harnesses)"
"Pytest (runtime-policies)"
+32 -8
View File
@@ -17,15 +17,25 @@ name: Maintainer Approval
# self-grant the gate. The same property is documented in
# merge-ready.yml's force-merge eligibility step.
#
# Defense-in-depth note: a malicious PR could also edit *this*
# workflow to drop `?ref=main`. On its own `pull_request_review` and
# `pull_request` events the modified workflow would run (those events
# use the workflow on the PR branch). That would let the PR
# self-grant the Maintainer Approval status. The remaining defense is
# Fork-PR token note: the status is posted through the GitHub API,
# which needs `statuses: write`. A fork PR's `pull_request` token is
# forced read-only regardless of the `permissions:` block, so the POST
# would 403 for any contributor PR from a fork. The step below detects
# that case and skips the POST (rather than erroring): with no status
# reported, branch protection leaves the required `Maintainer Approval`
# check unsatisfied -- which is exactly the gate we want -- until a
# maintainer reviews. The `pull_request_review` event runs in the base
# repo with a writable token even for forks, so it posts the result
# then. (We deliberately do not use a base-context PR trigger that runs
# with secrets on fork code: the public CI leak gate forbids it.)
#
# Defense-in-depth note: a malicious PR could edit *this* workflow to
# drop `?ref=main`. On its own `pull_request_review` and `pull_request`
# events the modified workflow would run from the PR branch, which
# could let the PR self-grant the status. The remaining defense is
# `required_pull_request_reviews` in branch protection -- a real
# maintainer still has to click Merge, and the PR has to come through
# a PR (no direct push to main). For an internal repo this is
# adequate; for a public repo we'd want a separate enforcement path.
# maintainer still has to click Merge, and every change arrives through
# a PR (no direct push to main).
on:
pull_request:
@@ -58,6 +68,8 @@ jobs:
REPO: ${{ github.repository }}
PR: ${{ github.event.pull_request.number }}
SHA: ${{ github.event.pull_request.head.sha }}
EVENT: ${{ github.event_name }}
IS_FORK: ${{ github.event.pull_request.head.repo.fork }}
run: |
# --- Load maintainers from .github/MAINTAINER at main's tip ---
set +e
@@ -126,6 +138,18 @@ jobs:
DESC="${DESC:0:137}..."
fi
# A fork PR's pull_request token is read-only, so the status
# POST would 403. Skip it on that event/fork combination -- the
# required check stays unreported (which blocks merge, the
# intended gate) until a maintainer reviews, at which point the
# writable pull_request_review token posts the result.
if [[ "$EVENT" == "pull_request" && "$IS_FORK" == "true" ]]; then
echo "Fork PR on pull_request event: token is read-only, " \
"skipping status POST. Awaiting maintainer review " \
"(would post Maintainer Approval=$STATE: $DESC)."
exit 0
fi
gh api "repos/$REPO/statuses/$SHA" \
-f state="$STATE" \
-f context="Maintainer Approval" \
-48
View File
@@ -1,48 +0,0 @@
name: PR Template
on:
pull_request:
types: [opened, edited, reopened, ready_for_review, synchronize]
permissions:
contents: read
pull-requests: read
concurrency:
group: pr-template-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
validate:
name: PR Template
if: ${{ !github.event.pull_request.draft }}
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Checkout base branch validator
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ github.event.pull_request.base.sha }}
sparse-checkout: .github/scripts/pr-template
persist-credentials: false
- name: Fall back to PR branch validator
if: ${{ hashFiles('.github/scripts/pr-template/validate.py') == '' }}
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
sparse-checkout: .github/scripts/pr-template
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version-file: ".python-version"
# Dependabot writes its own PR body that can't match the template;
# skip validation for it. The job still reports the `PR Template`
# check as success, so Merge Ready (which requires it) stays green.
- name: Validate PR description follows template
if: ${{ github.event.pull_request.user.login != 'dependabot[bot]' }}
env:
PR_BODY: ${{ github.event.pull_request.body }}
run: .github/scripts/pr-template/validate.py