feat(hooks): daemon /hooks/eval fast-path + guard env-forwarding (B, stage 3a) #441
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # 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)." |