From d931cdcdc2a24e3fb89835523b6d5bfe65b28a1d Mon Sep 17 00:00:00 2001 From: Aric Camarata Date: Tue, 1 Sep 2026 16:35:26 -0400 Subject: [PATCH] ci: consolidate trivial gates into hygiene.yml, cut runner queueing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit clean-root.yml and generated-file-gate.yml were two separate near-instant workflows (a few seconds of real work each) each claiming their own runner allocation on every push/PR. Neither is a required branch-protection check for nsentry, so they're merged into one hygiene.yml job with a single checkout — same steps, same logic, 1 runner claim instead of 2. Trigger scope is the union of the two originals (clean-root ran on [main, develop], generated-file-gate on [main] only), so nothing that used to run stops running. ci.yml and gitleaks.yml are untouched — both carry required checks ('Lint + Typecheck + Test (client + mobile)' and 'gitleaks'). No paths filter was added to either: GitHub does not skip a required check just because paths-ignore excluded it from a push, it blocks the merge waiting on a check that never runs — so path-filtering a required-check workflow is unsafe and was deliberately not attempted. --- .github/workflows/generated-file-gate.yml | 59 ----------------- .../workflows/{clean-root.yml => hygiene.yml} | 64 +++++++++++++++++-- 2 files changed, 60 insertions(+), 63 deletions(-) delete mode 100644 .github/workflows/generated-file-gate.yml rename .github/workflows/{clean-root.yml => hygiene.yml} (66%) diff --git a/.github/workflows/generated-file-gate.yml b/.github/workflows/generated-file-gate.yml deleted file mode 100644 index 0be61ed..0000000 --- a/.github/workflows/generated-file-gate.yml +++ /dev/null @@ -1,59 +0,0 @@ -name: Generated File Gate - -on: - pull_request: - branches: [main] - push: - branches: [main] - -jobs: - generated-file-gate: - name: Block committed generated files - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Scan diff for GENERATED BY markers - shell: bash - run: | - set -euo pipefail - - if [ "${{ github.event_name }}" = "pull_request" ]; then - git fetch origin main --depth=1 - BASE="origin/main" - else - BASE="${{ github.event.before }}" - if [ -z "$BASE" ] || ! git cat-file -e "$BASE" 2>/dev/null; then - BASE="HEAD~1" - fi - fi - - CHANGED=$(git diff --name-only "$BASE"...HEAD || true) - if [ -z "$CHANGED" ]; then - echo "No changed files to scan." - exit 0 - fi - - FAIL=0 - while IFS= read -r f; do - [ -f "$f" ] || continue - if head -n 5 "$f" | grep -q "# GENERATED BY"; then - if git check-ignore -q "$f"; then - echo "OK: $f is generated but gitignored." - else - echo "ERROR: Refusing to commit generated file. Add to .gitignore first." - echo " -> $f" - FAIL=1 - fi - fi - done <<< "$CHANGED" - - if [ "$FAIL" -eq 1 ]; then - echo "ERROR: Refusing to commit generated file. Add to .gitignore first." - exit 1 - fi - - echo "Generated File Gate: clean." diff --git a/.github/workflows/clean-root.yml b/.github/workflows/hygiene.yml similarity index 66% rename from .github/workflows/clean-root.yml rename to .github/workflows/hygiene.yml index f3e4ae5..803f36f 100644 --- a/.github/workflows/clean-root.yml +++ b/.github/workflows/hygiene.yml @@ -1,4 +1,15 @@ -name: Clean Working Tree +name: Hygiene + +# Consolidates the trivial, no-toolchain repo gates into one workflow with a +# single checkout, so a push doesn't queue two separate near-instant jobs for +# two separate runners. Neither job here is a required branch-protection +# check for nsentry (only "Lint + Typecheck + Test (client + mobile)" and +# "gitleaks" are) — see ~/Sites/nself/.claude memory on CI wall-clock work. +# +# Trigger scope is the union of the two former workflows (clean-root ran on +# [main, develop], generated-file-gate on [main] only) — broader, never +# narrower, so nothing that used to run stops running. + on: push: branches: [main, develop] @@ -6,14 +17,17 @@ on: branches: [main, develop] concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: hygiene-${{ github.ref }} cancel-in-progress: true + jobs: - clean-root: - name: clean-root + hygiene: + name: hygiene runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + with: + fetch-depth: 0 - name: Forbid markdown at repo root other than README.md run: | @@ -87,3 +101,45 @@ jobs: echo "::warning::.gitignore missing baseline patterns: ${MISSING[*]}" echo "See ~/.claude/CLAUDE.md (Clean Working Tree - Hard Rule) for full baseline." fi + + - name: Scan diff for GENERATED BY markers + shell: bash + run: | + set -euo pipefail + + if [ "${{ github.event_name }}" = "pull_request" ]; then + git fetch origin main --depth=1 + BASE="origin/main" + else + BASE="${{ github.event.before }}" + if [ -z "$BASE" ] || ! git cat-file -e "$BASE" 2>/dev/null; then + BASE="HEAD~1" + fi + fi + + CHANGED=$(git diff --name-only "$BASE"...HEAD || true) + if [ -z "$CHANGED" ]; then + echo "No changed files to scan." + exit 0 + fi + + FAIL=0 + while IFS= read -r f; do + [ -f "$f" ] || continue + if head -n 5 "$f" | grep -q "# GENERATED BY"; then + if git check-ignore -q "$f"; then + echo "OK: $f is generated but gitignored." + else + echo "ERROR: Refusing to commit generated file. Add to .gitignore first." + echo " -> $f" + FAIL=1 + fi + fi + done <<< "$CHANGED" + + if [ "$FAIL" -eq 1 ]; then + echo "ERROR: Refusing to commit generated file. Add to .gitignore first." + exit 1 + fi + + echo "Generated File Gate: clean."