Repository navigation
Repo Health #2369
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
| name: Repo Health | |
| on: | |
| pull_request: | |
| branches: [dev] | |
| types: [opened, synchronize, reopened] | |
| # Unprivileged half of Repo Health. The github org blocks pull_request_target, | |
| # so every job runs under `pull_request` with a read-only token and no secrets. | |
| # - A pull_request run uses this file as it is in the PR merge commit. Every | |
| # checkout below is the PR's base commit (`ref: base.sha`), so the scanners and | |
| # checks are base-controlled only when the PR leaves this file unchanged. A PR | |
| # that edits this file controls every job of its own run, including the result | |
| # of the Bootstrap Protection check. The compensating control is code-owner | |
| # review: .github/CODEOWNERS line 4 requests it on every PR (whether dev | |
| # requires it is a branch rule; see .github/REPO_HEALTH_RUNBOOK.md). | |
| # - The PR head is fetched only as Git data for diff/show and is never checked | |
| # out or executed. | |
| # - Bootstrap Protection and Diff Size Guard report as PR checks, as before. | |
| # - Leakage, architectural and security results are uploaded as artifacts. | |
| # squad-repo-health-report.yml (workflow_run, default branch) validates them, | |
| # binds them to this PR head and posts the marker comments. It posts only when | |
| # the run's head commit holds the same copy of this file as its merge base | |
| # with dev (see .github/REPO_HEALTH_RUNBOOK.md). | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.event.pull_request.number }} | |
| cancel-in-progress: true | |
| jobs: | |
| # ─── Bootstrap Protection (BLOCKING) ──────────────────────────────── | |
| bootstrap-protection: | |
| name: Bootstrap Protection | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 3 | |
| if: github.actor != 'dependabot[bot]' | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 #v7 | |
| with: | |
| ref: ${{ github.event.pull_request.base.sha }} | |
| sparse-checkout: | | |
| scripts/check-bootstrap-deps.mjs | |
| sparse-checkout-cone-mode: false | |
| - name: Fetch PR head (data only — not executed) | |
| run: git fetch origin ${{ github.event.pull_request.head.sha }} | |
| - uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 #v6 | |
| with: | |
| node-version: '22' | |
| - name: Check bootstrap dependencies | |
| id: bootstrap | |
| run: | | |
| set +e | |
| OUTPUT=$(node scripts/check-bootstrap-deps.mjs --ref ${{ github.event.pull_request.head.sha }} 2>&1) | |
| EXIT_CODE=$? | |
| echo "$OUTPUT" | |
| echo "result<<EOF" >> "$GITHUB_OUTPUT" | |
| echo "$OUTPUT" >> "$GITHUB_OUTPUT" | |
| echo "EOF" >> "$GITHUB_OUTPUT" | |
| echo "exit_code=$EXIT_CODE" >> "$GITHUB_OUTPUT" | |
| exit $EXIT_CODE | |
| # ─── Diff Size Guard (WARNING) ───────────────────────────────────── | |
| diff-guard: | |
| name: Diff Size Guard | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 2 | |
| if: github.actor != 'dependabot[bot]' | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 #v7 | |
| with: | |
| ref: ${{ github.event.pull_request.base.sha }} | |
| fetch-depth: 0 | |
| - name: Fetch PR head (data only — not executed) | |
| run: git fetch origin ${{ github.event.pull_request.head.sha }} | |
| - name: Check for likely contamination | |
| run: | | |
| git fetch origin dev --quiet | |
| FILE_COUNT=$(git diff --name-only origin/dev...${{ github.event.pull_request.head.sha }} | wc -l) | |
| COMMIT_COUNT=$(git rev-list --count origin/dev..${{ github.event.pull_request.head.sha }}) | |
| # Heuristic: if a single-commit PR touches more than 30 files, it's likely contaminated | |
| if [ "$COMMIT_COUNT" -le 2 ] && [ "$FILE_COUNT" -gt 30 ]; then | |
| echo "::warning::⚠️ This PR has $COMMIT_COUNT commit(s) but touches $FILE_COUNT files." | |
| echo "::warning::This may indicate branch contamination from broad staging (--all or .) on a stale branch." | |
| echo "::warning::Please verify all changed files are intentional: git diff --name-only origin/dev...${{ github.event.pull_request.head.sha }}" | |
| else | |
| echo "✅ Diff looks proportional: $COMMIT_COUNT commit(s), $FILE_COUNT file(s)." | |
| fi | |
| # ─── Squad File Leakage (WARNING) ─────────────────────────────────── | |
| squad-leakage: | |
| name: Squad File Leakage | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 3 | |
| if: github.actor != 'dependabot[bot]' | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 #v7 | |
| with: | |
| ref: ${{ github.event.pull_request.base.sha }} | |
| fetch-depth: 0 | |
| - name: Fetch PR head (data only — not executed) | |
| run: git fetch origin ${{ github.event.pull_request.head.sha }} | |
| - uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 #v6 | |
| with: | |
| node-version: '22' | |
| - name: Detect .squad/ leakage | |
| run: | | |
| git fetch origin dev --quiet | |
| mkdir -p "$RUNNER_TEMP/repo-health" | |
| node scripts/check-squad-leakage.mjs origin/dev ${{ github.event.pull_request.head.sha }} > "$RUNNER_TEMP/repo-health/output.txt" | |
| cat "$RUNNER_TEMP/repo-health/output.txt" | |
| # Scanner output is data for squad-repo-health-report.yml, never script source (#1770). | |
| - name: Package result for Repo Health Report | |
| env: | |
| PR_NUMBER: ${{ github.event.pull_request.number }} | |
| HEAD_SHA: ${{ github.event.pull_request.head.sha }} | |
| run: | | |
| jq -n --arg job leakage --argjson pr_number "$PR_NUMBER" --arg head_sha "$HEAD_SHA" \ | |
| --rawfile output "$RUNNER_TEMP/repo-health/output.txt" \ | |
| '{schema: "squad-repo-health-result/v1", job: $job, pr_number: $pr_number, head_sha: $head_sha, output: $output}' \ | |
| > "$RUNNER_TEMP/repo-health/leakage.json" | |
| - name: Upload result for Repo Health Report | |
| uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 #v7 | |
| with: | |
| name: repo-health-leakage | |
| path: ${{ runner.temp }}/repo-health/leakage.json | |
| if-no-files-found: error | |
| retention-days: 1 | |
| overwrite: true | |
| # ─── Architectural Review (INFORMATIONAL) ─────────────────────────── | |
| architectural-review: | |
| name: Architectural Review — Structure & Design Rules | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| if: github.actor != 'dependabot[bot]' | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 #v7 | |
| with: | |
| ref: ${{ github.event.pull_request.base.sha }} | |
| fetch-depth: 0 | |
| - name: Fetch PR head (data only — not executed) | |
| run: git fetch origin ${{ github.event.pull_request.head.sha }} | |
| - uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 #v6 | |
| with: | |
| node-version: '22' | |
| - name: Run architectural review | |
| run: | | |
| git fetch origin dev --quiet | |
| mkdir -p "$RUNNER_TEMP/repo-health" | |
| node scripts/architectural-review.mjs origin/dev ${{ github.event.pull_request.head.sha }} > "$RUNNER_TEMP/repo-health/output.txt" | |
| cat "$RUNNER_TEMP/repo-health/output.txt" | |
| # Scanner output is data for squad-repo-health-report.yml, never script source (#1770). | |
| - name: Package result for Repo Health Report | |
| env: | |
| PR_NUMBER: ${{ github.event.pull_request.number }} | |
| HEAD_SHA: ${{ github.event.pull_request.head.sha }} | |
| run: | | |
| jq -n --arg job architectural --argjson pr_number "$PR_NUMBER" --arg head_sha "$HEAD_SHA" \ | |
| --rawfile output "$RUNNER_TEMP/repo-health/output.txt" \ | |
| '{schema: "squad-repo-health-result/v1", job: $job, pr_number: $pr_number, head_sha: $head_sha, output: $output}' \ | |
| > "$RUNNER_TEMP/repo-health/architectural.json" | |
| - name: Upload result for Repo Health Report | |
| uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 #v7 | |
| with: | |
| name: repo-health-architectural | |
| path: ${{ runner.temp }}/repo-health/architectural.json | |
| if-no-files-found: error | |
| retention-days: 1 | |
| overwrite: true | |
| # ─── Security Review (INFORMATIONAL) ──────────────────────────────── | |
| security-review: | |
| name: Security Review — Permissions & Secrets | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| if: github.actor != 'dependabot[bot]' | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 #v7 | |
| with: | |
| ref: ${{ github.event.pull_request.base.sha }} | |
| fetch-depth: 0 | |
| - name: Fetch PR head (data only — not executed) | |
| run: git fetch origin ${{ github.event.pull_request.head.sha }} | |
| - uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 #v6 | |
| with: | |
| node-version: '22' | |
| - name: Run security review | |
| run: | | |
| git fetch origin dev --quiet | |
| mkdir -p "$RUNNER_TEMP/repo-health" | |
| node scripts/security-review.mjs origin/dev ${{ github.event.pull_request.head.sha }} > "$RUNNER_TEMP/repo-health/output.txt" | |
| cat "$RUNNER_TEMP/repo-health/output.txt" | |
| # Scanner output is data for squad-repo-health-report.yml, never script source (#1770). | |
| - name: Package result for Repo Health Report | |
| env: | |
| PR_NUMBER: ${{ github.event.pull_request.number }} | |
| HEAD_SHA: ${{ github.event.pull_request.head.sha }} | |
| run: | | |
| jq -n --arg job security --argjson pr_number "$PR_NUMBER" --arg head_sha "$HEAD_SHA" \ | |
| --rawfile output "$RUNNER_TEMP/repo-health/output.txt" \ | |
| '{schema: "squad-repo-health-result/v1", job: $job, pr_number: $pr_number, head_sha: $head_sha, output: $output}' \ | |
| > "$RUNNER_TEMP/repo-health/security.json" | |
| - name: Upload result for Repo Health Report | |
| uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 #v7 | |
| with: | |
| name: repo-health-security | |
| path: ${{ runner.temp }}/repo-health/security.json | |
| if-no-files-found: error | |
| retention-days: 1 | |
| overwrite: true |