diff --git a/.github/workflows/dependabot-review.yml b/.github/workflows/dependabot-review.yml index c36dfa9..887d2aa 100644 --- a/.github/workflows/dependabot-review.yml +++ b/.github/workflows/dependabot-review.yml @@ -21,6 +21,10 @@ permissions: contents: read pull-requests: write +concurrency: + group: dependabot-review-${{ github.ref }} + cancel-in-progress: true + jobs: dependabot-review: name: Dependabot PR Gate 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/doc-sync.yml b/.github/workflows/hygiene.yml similarity index 60% rename from .github/workflows/doc-sync.yml rename to .github/workflows/hygiene.yml index 955f378..63bc09b 100644 --- a/.github/workflows/doc-sync.yml +++ b/.github/workflows/hygiene.yml @@ -1,27 +1,104 @@ -name: Doc-Sync Ritual - -# Enforces the Doc-Sync Ritual (P93 Theme 27). -# Doctrine: https://github.com/nself-org/ — ~/Sites/nself/.claude/docs/doctrines/doc-sync-ritual.md -# Blocks any PR that changes a user-visible surface without matching doc updates. +name: Hygiene + +# Consolidates the trivial, no-toolchain repo gates into one workflow with a +# single checkout, so a push doesn't queue three separate near-instant jobs +# for three separate runners. +# +# clean-root.yml and gitleaks.yml are DELIBERATELY NOT folded in here, even +# though clean-root is exactly this class of job. Both are required +# branch-protection checks ("clean-root" and "gitleaks") and GitHub matches +# a required check by job name/id — moving either job into a shared +# workflow file is a legitimate way to preserve the name, but the safer +# choice for two checks branch protection depends on every PR is to leave +# them as their own standalone workflow files untouched. Neither is touched +# by this change. +# +# Trigger scope is the union of the three originals — generated-file-gate +# and multi-arch-check ran on push/pull_request[main] only (multi-arch-check +# also allowed workflow_dispatch), doc-sync ran on pull_request +# [opened, synchronize, reopened] with no branch restriction (the bare +# `pull_request:` default below covers the same three types). Union is +# broader, never narrower, so nothing that used to run stops running. The +# doc-sync steps are guarded to PR events only (the script reads +# github.base_ref, which push events don't set). +# +# nself-first-check.yml is deliberately NOT folded in — it's already scoped +# to `paths: ['**/docker-compose*.yml', ...]` and fires on almost no +# pushes. Merging it into a workflow that must run on every push would make +# it run far more often, which is the opposite of the goal. on: + push: + branches: [main] pull_request: - types: [opened, synchronize, reopened] + workflow_dispatch: concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: hygiene-${{ github.ref }} cancel-in-progress: true + jobs: - doc-sync: + hygiene: + name: hygiene runs-on: ubuntu-latest steps: - - name: Checkout - uses: actions/checkout@v4 + - uses: actions/checkout@v4 with: fetch-depth: 0 + # ── from generated-file-gate.yml ───────────────────────────── + - 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." + + # ── from multi-arch-check.yml ──────────────────────────────── + - name: Multi-arch check (N/A) + run: | + echo "Repo type: TypeScript app surfaces (RN/Expo, Vite, Tauri, rn-tvos) + nSelf backend" + echo "No Go binaries or Docker images produced. Multi-arch check N/A. PASS." + + # ── from doc-sync.yml — PR-only, needs github.base_ref ─────── - name: Compute PR diff id: diff + if: github.event_name == 'pull_request' run: | git fetch origin "${{ github.base_ref }}" --depth=1 changed=$(git diff --name-only "origin/${{ github.base_ref }}"...HEAD || true) @@ -31,6 +108,7 @@ jobs: echo "$changed" - name: Doc-Sync matrix check + if: github.event_name == 'pull_request' env: CHANGED: ${{ steps.diff.outputs.changed }} run: | diff --git a/.github/workflows/multi-arch-check.yml b/.github/workflows/multi-arch-check.yml deleted file mode 100644 index 65c3c87..0000000 --- a/.github/workflows/multi-arch-check.yml +++ /dev/null @@ -1,28 +0,0 @@ -name: Multi-Arch Check - -# Asserts multi-architecture build compatibility. -# Repo type: TypeScript app surfaces + nSelf backend (nself-org/ntask) -# Authority: S30-T05, STORM-CROSS-PLATFORM -# Template: .claude/docs/doctrines/multi-arch-check.workflow.yml - -on: - pull_request: - branches: [main] - push: - branches: [main] - workflow_dispatch: - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true -jobs: - multi-arch-stub: - name: Multi-arch check (N/A) - runs-on: ubuntu-latest - - steps: - - name: Report repo type - run: | - echo "Repo type: TypeScript app surfaces (RN/Expo, Vite, Tauri, rn-tvos) + nSelf backend" - echo "No Go binaries or Docker images produced. Multi-arch check N/A. PASS." - diff --git a/.github/workflows/wiki-sync.yml b/.github/workflows/wiki-sync.yml index 7d888cf..15714fb 100644 --- a/.github/workflows/wiki-sync.yml +++ b/.github/workflows/wiki-sync.yml @@ -9,6 +9,10 @@ on: - '.github/workflows/wiki-sync.yml' workflow_dispatch: +concurrency: + group: wiki-sync-${{ github.ref }} + cancel-in-progress: true + jobs: sync-wiki: runs-on: ubuntu-latest