fix(rewrite): badge emptied-generation sections with distinct emptied state (#877) #1237
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
| name: CI | |
| on: | |
| pull_request: | |
| branches: [main] | |
| push: | |
| branches: [main] | |
| # Merge queue entries run on a temporary `gh-readonly-queue/*` branch and | |
| # emit a `merge_group` event, not `pull_request`/`push`. Without this the | |
| # required `verify` check never runs on the queue branch and every queued PR | |
| # stalls forever. Keep this in lockstep with the "Require merge queue" branch | |
| # protection on `main`. | |
| merge_group: | |
| jobs: | |
| verify: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| # Required so github/codeql-action/upload-sarif can publish the fallow | |
| # report to GitHub code scanning. Report-only for now (see #15). | |
| security-events: write | |
| # Required by `check:baselines`, which reads the Issues API to judge every | |
| # `issue:` citation in the known-failures files. An explicit permissions | |
| # block sets every UNLISTED scope to `none`, so without this line | |
| # `issueState()` returns null for all of them, every citation fails, and | |
| # the merge queue goes red on every PR. (`moderate-comments.yml` declares | |
| # `issues: write` for the same reason.) | |
| issues: read | |
| steps: | |
| # Bumped 2026-06-01 (#4): Node 20 actions deprecated, runner forces Node 24 by 2026-06-02. | |
| - uses: actions/checkout@v5 | |
| # Full history so `fallow audit` can resolve a merge-base against | |
| # origin/main (its diff gate needs the base commit, not a shallow clone). | |
| with: | |
| fetch-depth: 0 | |
| - uses: actions/setup-node@v5 | |
| with: | |
| node-version: '20' | |
| cache: 'npm' | |
| - run: npm ci | |
| - run: npm run typecheck | |
| - run: npm run lint | |
| # Fixture-PII gate (#478). The repo is public and the committed PDF binary | |
| # is the exposure surface, so this is the one check whose failure mode is | |
| # unrecoverable — it runs before the test suite so a leak fails fast. Reads | |
| # text + metadata via pdfjs-dist (already a dependency), so no poppler-utils | |
| # or any other system binary is needed on the runner. | |
| - run: npm run check:fixtures | |
| # Issue-linked baseline gate (#654). The corpus ratchets can see that an | |
| # exemption still fails; only GitHub knows whether the issue it is charged | |
| # to is still open, so this is the one check that needs a token — hence a | |
| # script, and hence the baselines living in JSON the script can read. It | |
| # runs here rather than only inside `npm run verify` so the token is | |
| # explicit: without `GH_TOKEN` the script skips the issue-state pass and | |
| # the orphaned-baseline check silently does nothing. | |
| - run: npm run check:baselines | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| # Publishable-tarball gate (#772). Mirrored here rather than left inside | |
| # `npm run verify` because branch protection requires THIS job, and every | |
| # other step above is a bundler or a typechecker: none of them loads | |
| # `@offlinecv/core` the way a published consumer does, so the whole class | |
| # of "the tarball is wrong" — `exports` pointing at a path that does not | |
| # ship, a dependency reached from the emit but absent from | |
| # `dependencies` — is invisible to them. Without this line a push that | |
| # skips the pre-push hook merges green and only the daily | |
| # `promote-release.yml` (which does run `npm run verify`) goes red, after | |
| # the fact. | |
| - run: npm run check:core | |
| # Run the suite with coverage so the fallow step below can compute | |
| # accurate per-function CRAP scores (coverage/coverage-final.json). | |
| - run: npm run test:coverage | |
| - run: npm run build | |
| # Report-only static-analysis gate (#15). `fallow audit` is a diff gate, | |
| # so it's scoped to changes vs origin/main; non-blocking for now (a | |
| # follow-up flips it to a hard gate once the baseline is clean). The | |
| # guard guarantees a syntactically valid SARIF even if fallow exits | |
| # non-zero, so the upload step never fails on an empty file. | |
| - name: fallow static analysis | |
| run: | | |
| npx fallow audit --format sarif --base origin/main \ | |
| --coverage coverage/coverage-final.json > fallow.sarif \ | |
| || echo "fallow audit exited non-zero (report-only, ignored)" | |
| if [ ! -s fallow.sarif ]; then | |
| echo '{"$schema":"https://json.schemastore.org/sarif-2.1.0.json","version":"2.1.0","runs":[{"tool":{"driver":{"name":"fallow"}},"results":[]}]}' > fallow.sarif | |
| fi | |
| # fallow emits one SARIF run per sub-analysis (dead-code/health/dupes), | |
| # and the run COUNT varies per branch (the audit is a diff gate vs | |
| # origin/main). A positional category id (`fallow/<index>/`) is | |
| # therefore unstable: a config registered on main can be "not found" | |
| # on a PR whose audit emitted fewer runs, which makes code scanning | |
| # warn it can't diff that category. Collapse all runs into ONE run | |
| # with a single FIXED category `fallow/` — always present on every | |
| # branch, and one run sidesteps CodeQL's "multiple runs same | |
| # category" rule entirely. CodeQL also rejects any result lacking a | |
| # physical location ("locationFromSarifResult: expected at least one | |
| # location"); some fallow findings (project-wide health/dupe | |
| # summaries) carry no location, so drop those before upload. | |
| node -e 'const fs=require("fs");const f="fallow.sarif";const j=JSON.parse(fs.readFileSync(f,"utf8"));const runs=j.runs||[];const driver=runs[0]&&runs[0].tool&&runs[0].tool.driver?runs[0].tool.driver:{name:"fallow"};const rules=[],results=[];for(const r of runs){if(r.tool&&r.tool.driver&&Array.isArray(r.tool.driver.rules))rules.push(...r.tool.driver.rules);if(Array.isArray(r.results))results.push(...r.results.filter(x=>Array.isArray(x.locations)&&x.locations.length>0))}j.runs=[{tool:{driver:{...driver,rules}},automationDetails:{id:"fallow/"},results}];fs.writeFileSync(f,JSON.stringify(j))' | |
| - name: upload fallow SARIF | |
| uses: github/codeql-action/upload-sarif@v3 | |
| with: | |
| sarif_file: fallow.sarif | |
| # Pin the analysis category explicitly (not only via the SARIF's | |
| # automationDetails). Code scanning diffs a PR against the base branch | |
| # PER CATEGORY: a category registered on `main` but absent on a PR | |
| # surfaces as a neutral "configuration not found" warning. A single | |
| # fixed `fallow` category — set here at the action level so it can't | |
| # drift from the SARIF injection — keeps every branch on the same | |
| # category, so the PR-vs-main diff always lines up. (One-time legacy | |
| # `fallow/0`/`fallow/1` configs from the pre-collapse scheme were | |
| # purged from `main`'s code-scanning history so they stop being | |
| # expected.) | |
| category: fallow |