Skip to content

feat(hooks): daemon /hooks/eval fast-path + guard env-forwarding (B, stage 3a) #441

feat(hooks): daemon /hooks/eval fast-path + guard env-forwarding (B, stage 3a)

feat(hooks): daemon /hooks/eval fast-path + guard env-forwarding (B, stage 3a) #441

Workflow file for this run

# Auto-merge safety model:
# - the `validate` job is a REQUIRED status check on main (repo ruleset),
# so --auto cannot merge unvalidated synchronize commits (TOCTOU), and
# - "Allow auto-merge" is enabled on the repo.
# To keep `validate` safe as a required check it must report on EVERY PR -
# path-filtered required checks wedge unrelated PRs in "expected" forever.
# So this workflow runs on all PRs and passes trivially when no
# community/users files are touched.
#
# The ruleset requires a SECOND check, `verify` (the code CI gate in ci.yml,
# on `pull_request`). That never runs on a first-time contributor's fork PR
# until a maintainer clicks "Approve and run", so a data-only registration sits
# BLOCKED forever even with validate green and auto-merge armed (#410-adjacent;
# Dariia-Smyrnova/#417). Since a community/users-only change carries no code,
# `verify` is not applicable: once we've validated a `clean` PR we satisfy the
# `verify` context with a success commit status so the armed auto-merge can
# fire. Code PRs (mode none/mixed) never get this status - real ci.yml `verify`
# still gates them.
name: community-users
on:
pull_request_target:
permissions:
contents: read
jobs:
validate:
runs-on: ubuntu-24.04
permissions:
contents: write
pull-requests: write
statuses: write
steps:
- name: Classify changed files
id: scope
# Modes:
# none - no community/users files touched -> trivially pass
# clean - ONLY community/users/<login>.json files -> validate + auto-merge
# mixed - community/users files alongside anything else (or
# non-conforming filenames) -> human review comment
# gh pr diff is API-based: the checkouts below are fetch-depth:1 so a
# local base...head diff would fail with "fatal: bad object".
# Filename filter is strict-lowercase (grep -E; plain grep treats `+`
# literally): GitHub logins are case-insensitive but the convention is
# lowercase filenames and the validator enforces it.
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
changed=$(gh pr diff ${{ github.event.pull_request.number }} --name-only --repo ${{ github.repository }})
echo "$changed"
community=$(echo "$changed" | grep -E '^community/users/' || true)
if [ -z "$community" ]; then
echo "mode=none" >> "$GITHUB_OUTPUT"
echo "no community/users files - passing trivially"
elif echo "$changed" | grep -Ev '^community/users/[a-z0-9-]+\.json$' | grep -q .; then
echo "mode=mixed" >> "$GITHUB_OUTPUT"
else
echo "mode=clean" >> "$GITHUB_OUTPUT"
echo "files<<EOF" >> "$GITHUB_OUTPUT"
echo "$changed" | sed 's|^|pr-head/|' >> "$GITHUB_OUTPUT"
echo "EOF" >> "$GITHUB_OUTPUT"
fi
# pull_request_target runs with base-repo secrets; check out the PR
# head EXPLICITLY and never execute its code - we only read JSON files
# and run OUR validator from the base ref. The pr-head/ checkout is
# DATA-ONLY: nothing inside it is executed, installed, or sourced.
- uses: actions/checkout@v4
if: steps.scope.outputs.mode == 'clean'
with:
ref: ${{ github.event.pull_request.base.sha }}
- uses: actions/checkout@v4
if: steps.scope.outputs.mode == 'clean'
with:
ref: ${{ github.event.pull_request.head.sha }}
path: pr-head
- uses: oven-sh/setup-bun@v2
if: steps.scope.outputs.mode == 'clean'
with:
bun-version: "1.3.10"
- name: Validate registrations
if: steps.scope.outputs.mode == 'clean'
run: |
bun install --frozen-lockfile
echo "${{ steps.scope.outputs.files }}" | xargs bun scripts/validate-community-users.ts \
--author="${{ github.event.pull_request.user.login }}"
# Validation passed (a failed validator fails the job before here), and a
# `clean` PR is community/users/<login>.json files ONLY - no code - so the
# required `verify` code gate doesn't apply. Mark it success on THIS head
# sha so the ruleset is satisfied. Re-evaluated per synchronize commit: a
# later push that adds code re-classifies to `mixed`, this step is skipped,
# and real `verify` is required again (no TOCTOU bypass).
- name: Satisfy the verify gate for data-only registrations
if: steps.scope.outputs.mode == 'clean'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh api -X POST "repos/${{ github.repository }}/statuses/${{ github.event.pull_request.head.sha }}" \
-f state=success \
-f context=verify \
-f description="data-only community registration - code CI not applicable"
- name: Auto-merge
if: steps.scope.outputs.mode == 'clean'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: gh pr merge ${{ github.event.pull_request.number }} --squash --auto --repo ${{ github.repository }}
- name: Comment when human review needed
if: steps.scope.outputs.mode == 'mixed'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh pr comment ${{ github.event.pull_request.number }} --repo ${{ github.repository }} \
--body "This PR touches community/users/ alongside other files (or non-conforming filenames), so it needs normal human review (no auto-merge)."