-
Notifications
You must be signed in to change notification settings - Fork 4
115 lines (113 loc) · 6.75 KB
/
Copy pathci.yml
File metadata and controls
115 lines (113 loc) · 6.75 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
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