Repository navigation
470 lines (446 loc) · 21.8 KB
/
Copy pathpages.yml
File metadata and controls
470 lines (446 loc) · 21.8 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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
name: Pages
# Builds the mdBook documentation site, runs bca on this very repo to
# produce HTML + Markdown quality reports, and (on push to main) deploys
# both as a single GitHub Pages site. The bca self-scan acts as a
# threshold gate for the workspace; PR runs exercise the gate without
# deploying.
on:
push:
branches: [main]
paths:
- 'big-code-analysis-book/**'
- 'src/**'
- 'big-code-analysis-ast/**'
- 'big-code-analysis-cli/**'
- 'big-code-analysis-web/**'
- 'big-code-analysis-py/**'
# Root manifest + lockfile track workspace-wide dependency state.
# Grammar-pin bumps (e.g. `bca-tree-sitter-mozjs = "=0.23.x"`)
# shift metric values across every module that parses them, so a
# bump must refresh the published report and re-run the gate.
- 'Cargo.toml'
- 'Cargo.lock'
# Vendored grammar crates: scanner.{c,cc} changes can alter node
# categorisation and therefore Halstead / cyclomatic counts.
- 'tree-sitter-ccomment/**'
- 'tree-sitter-mozcpp/**'
- 'tree-sitter-mozjs/**'
- 'tree-sitter-preproc/**'
- 'tree-sitter-tcl/**'
- 'bca.toml'
- '.bca-baseline.toml'
- '.github/workflows/pages.yml'
pull_request:
paths:
- 'big-code-analysis-book/**'
- 'src/**'
- 'big-code-analysis-ast/**'
- 'big-code-analysis-cli/**'
- 'big-code-analysis-web/**'
- 'big-code-analysis-py/**'
- 'Cargo.toml'
- 'Cargo.lock'
- 'tree-sitter-ccomment/**'
- 'tree-sitter-mozcpp/**'
- 'tree-sitter-mozjs/**'
- 'tree-sitter-preproc/**'
- 'tree-sitter-tcl/**'
- 'bca.toml'
- '.bca-baseline.toml'
- '.github/workflows/pages.yml'
workflow_dispatch:
# Default to read-only. The `deploy` job escalates to pages:write +
# id-token:write so the official `actions/deploy-pages` flow can
# upload + claim the Pages environment.
permissions:
contents: read
# Action SHAs are pinned to commit hashes to eliminate supply-chain
# risk from action-release spoofing. Dependabot (configured for the
# github-actions ecosystem in .github/dependabot.yml) keeps these in
# sync with upstream releases. The mdBook tarball is downloaded
# outside the actions/ pipeline and is additionally verified by
# sha256 (see the "Install mdbook" step) — bump the version and the
# checksum together.
#
# `bca` itself is built from the current checkout (see the `scan`
# job's `cargo install` step). A pinned-release install used to live
# here too but it broke whenever main shipped a CLI-artifact schema
# bump the last release didn't understand (e.g., the v3 baseline
# schema from #376 was unreadable to v1.1.0's bca). Building from
# source closes that drift gap; Swatinem/rust-cache keeps cold-build
# cost bounded. The trade-off vs the previous sha256-pinned install
# is real but matches how every other Rust workflow in this repo
# already trusts its own cargo build — see STABILITY.md for the
# schema-bump operational contract this approach enforces.
env:
MDBOOK_VERSION: "0.4.40"
MDBOOK_SHA256: "9ef07fd288ba58ff3b99d1c94e6d414d431c9a61fdb20348e5beb74b823d546b"
# Provides the mdbook-gettext preprocessor for the Japanese book
# build (big-code-analysis-book/po/ja.po). Must stay on the 0.3.x
# line while MDBOOK_VERSION is 0.4.x — mdbook-i18n-helpers 0.4.0+
# targets the mdbook 0.5 preprocessor crates. Bump the two pins
# together. Installed via `cargo install --locked` (no prebuilt
# binaries are published upstream), matching how this workflow
# already trusts its own cargo builds (see the note above).
MDBOOK_I18N_HELPERS_VERSION: "0.3.6"
# Workspace-wide Rust env, mirrored from ci.yml so the `bca` build
# in this workflow gates on the same warnings, retries, and
# incremental settings as the rest of CI. Without these, a warning
# introduced on main can compile green here while failing ci.yml
# for the same commit.
CARGO_TERM_COLOR: always
CARGO_INCREMENTAL: 0
CARGO_NET_RETRY: 10
RUSTUP_MAX_RETRIES: 10
RUSTFLAGS: "-D warnings"
jobs:
build-book:
name: build mdBook
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 1
submodules: false
- name: Install mdbook
run: |
set -euo pipefail
tarball="mdbook-v${MDBOOK_VERSION}-x86_64-unknown-linux-gnu.tar.gz"
url="https://github.com/rust-lang/mdBook/releases/download/v${MDBOOK_VERSION}/${tarball}"
mkdir -p "$HOME/.local/bin"
curl -fsSL --proto '=https' --tlsv1.2 -o "/tmp/${tarball}" "$url"
echo "${MDBOOK_SHA256} /tmp/${tarball}" | sha256sum --check --strict -
tar -xzf "/tmp/${tarball}" -C "$HOME/.local/bin"
rm -f "/tmp/${tarball}"
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
- uses: dtolnay/rust-toolchain@683b588c487a69d01f6c6ddf40f036ff682cc2c8 # stable tip
with:
toolchain: stable
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
- name: Install mdbook-i18n-helpers
run: cargo install mdbook-i18n-helpers --version "=${MDBOOK_I18N_HELPERS_VERSION}" --locked
- name: Build book (English)
run: mdbook build big-code-analysis-book
# The Japanese book is served from /ja/ under the same Pages
# site. Build order matters: the English build cleans book/,
# so the ja build (which only cleans book/ja/) must run second.
- name: Build book (Japanese)
run: MDBOOK_BOOK__LANGUAGE=ja mdbook build big-code-analysis-book -d book/ja
# Belt-and-braces: `actions/deploy-pages` already bypasses
# Jekyll, but the same artifact directory is also what the
# manual fallback script (`utils/deploy-book-to-gh-pages.sh`)
# consumes when publishing to a `gh-pages` branch — where
# Jekyll WOULD strip `_FontAwesome/`, the search index, etc.
# Keeping `.nojekyll` here means both publish paths produce
# an identical, Jekyll-safe artifact.
- name: Disable Jekyll processing
run: touch big-code-analysis-book/book/.nojekyll
# Uploaded as a plain workflow artifact (not the Pages artifact)
# so the `assemble` job can extend it with the bca reports
# before the single Pages artifact is produced.
# `include-hidden-files: true` is REQUIRED here: upload-artifact
# v4+ strips dotfiles by default, which would silently drop
# `.nojekyll` from the artifact and break the gh-pages fallback
# invariant the step above guards.
- name: Upload book artifact
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: book-html
path: big-code-analysis-book/book
include-hidden-files: true
if-no-files-found: error
# PR-built artifacts are never consumed downstream (assemble
# only runs on main pushes). Keep them around for one day so
# a reviewer can poke at the book preview if needed, but
# avoid burning the per-repo artifact-storage quota under
# steady PR traffic. Main pushes keep the standard 7-day
# retention so a deploy investigation has the source artifact
# in reach.
retention-days: ${{ github.event_name == 'pull_request' && 1 || 7 }}
scan:
name: bca self-scan
runs-on: ubuntu-latest
# Generous to accommodate cold-cache release builds of the whole
# bca workspace (many tree-sitter grammar C crates plus the Rust
# workspace itself). Warm-cache runs finish in 3-5 min; the
# ceiling absorbs the worst-case "Cargo.lock churned + Swatinem
# cache evicted" combo without killing useful runs.
timeout-minutes: 30
# security-events:write lets the upload-sarif step publish bca's own
# self-scan offenders to GitHub code scanning (Security tab + PR
# annotations) — dogfooding the SARIF path we document for adopters
# in commands/check.md and recipes/ci.md. actions:read is required by
# upload-sarif on private/internal repos (harmless on public ones)
# and matches ci.yml's clippy job. The workflow-level default is
# contents:read.
permissions:
contents: read
security-events: write
actions: read
# The walk deny-set — build output, vendored trees, prose, and the
# third-party corpora under `tests/repositories/` — lives in
# `.bcaignore` at the repo root and is wired in via `exclude_from`
# in the auto-discovered `bca.toml` manifest, so the invocations
# below pick it up without an explicit `--exclude-from` flag. If you
# edit `.bcaignore`, also refresh `.bca-baseline.toml` in the same
# commit — the baseline keys are sensitive to which files the
# walker actually visits.
#
# The manifest's `[check] exclude` is a separate, gate-only list
# (#1146): the dev-tooling trees named there are walked, so they
# appear in the reports and hotspot tables published below, and are
# exempt only from the threshold gate step.
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
# `--since "origin/${GITHUB_BASE_REF:-main}"` (set via
# BCA_SINCE on the threshold-gate step below, #387) resolves a
# merge-base, which needs full history; the default shallow
# `fetch-depth: 1` would make that ref unreachable. `bca`
# soft-warns and falls back to an unpartitioned footer if the
# base is still unresolvable, so this is a readability win, not
# a correctness dependency.
fetch-depth: 0
submodules: false
# Build `bca` from the current checkout so the gate always
# runs against HEAD. This avoids the schema-skew failure mode
# the previous "download pinned release" approach hit when
# main shipped a CLI artifact bump (e.g., the v3 baseline
# from #376 was unreadable to the last v1.1.0 release).
# Swatinem/rust-cache below keeps the cold-build cost
# bounded — warm runs are dominated by incremental relink,
# not full recompile.
- uses: dtolnay/rust-toolchain@683b588c487a69d01f6c6ddf40f036ff682cc2c8 # stable tip
with:
toolchain: stable
# `shared-key` (vs `key`) lets Swatinem fall back to
# restore-keys when the exact Cargo.lock+rustc fingerprint
# changes, so a dep bump still warm-restores most of the
# workspace. The `--release` profile here is intentionally
# distinct from ci.yml's default-profile builds — mixing them
# in one cache would thrash both.
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
with:
shared-key: pages-scan-release
# `cargo install --path …` lands the binary in `~/.cargo/bin`
# (already on PATH on every GHA runner) so we don't have to
# hand-roll a PATH prepend that would break if a future
# `CARGO_TARGET_DIR` override moved the build artefacts.
# `--locked` matches the rest of CI's `--locked` builds.
- name: Install bca from current checkout
run: cargo install --path big-code-analysis-cli --locked --bin bca
- name: bca version
run: bca --version
# The deny-set (`exclude_from = ".bcaignore"`) is auto-discovered
# from the repo-root `bca.toml` manifest, so the report
# invocations no longer pass `--exclude-from` — workflow, recipe,
# and local bootstrap all share the same list (issue #355) by
# reading the manifest. `bca` resolves the manifest by climbing
# from the working directory to the repo root.
#
# Path selection is left to the manifest's `paths = ["."]` key.
# The manifest resolves it to an absolute root, but the walker
# re-anchors that root to the `./`-prefixed form (#488), so the
# `./`-anchored `.bcaignore` deny-set matches and the
# `--strip-prefix "./"` below still lands on the emitted prefix.
# The report-generation steps below run BEFORE the threshold gate.
# A failure here must not *mask* the gate (skipping it would hide
# real threshold violations), but it must still fail the job (a
# broken report is a real defect). So the steps are left as normal
# (no `continue-on-error`) — a failure correctly reds the job — and
# the gate itself carries `if: ${{ !cancelled() }}`, so it runs
# regardless of a prior report failure. (An earlier revision used
# `continue-on-error` on the reports; that masked a single broken
# report on PR runs, where `assemble` does not run to catch it.)
# `--vcs` folds a "Change-history risk" section into the flagship
# reports, so the AST hotspots and the VCS risk ranking sit
# side-by-side in one page (issue #573) — the project's primary
# dogfood of `bca report --vcs`. It builds the history index with
# default windows from the same `fetch-depth: 0` checkout the VCS
# JSON step below relies on. SECURITY: `--vcs` never
# emits author-identity hashes (that is `bca vcs
# --emit-author-details` only), so nothing author-sensitive reaches
# the public Pages site.
- name: Generate Markdown report
run: |
set -euo pipefail
bca \
report markdown \
--vcs \
--top 30 \
--strip-prefix "./" \
--output report.md
- name: Generate HTML report
run: |
set -euo pipefail
bca \
report html \
--vcs \
--top 50 \
--strip-prefix "./" \
--output index.html
# The rendered change-history report is now folded into the
# flagship `report.md` / `index.html` above via `--vcs`. This step
# additionally publishes the full top-100 ranking as a
# machine-readable JSON download (deeper than the flagship report's
# top-30/50 section), using the same `bca.toml` path selection and
# the job's `fetch-depth: 0` full history.
#
# SECURITY: this report is published to the *public* Pages site.
# Do NOT add `--emit-author-details` here: it would publish
# SHA-256 hashes of author emails. The counts/paths/scores emitted
# without it are all derivable from the public git history.
#
# `--output` writes a single whole-repo file (#573).
- name: Generate VCS risk JSON
run: |
set -euo pipefail
bca vcs --top 100 --format json --output vcs-report.json
# Run the gate last so the report artifacts above always upload
# even when thresholds fail — the report is most useful when CI
# is red. `if: ${{ !cancelled() }}` makes the gate run even if a
# report step above failed, so a report defect can never mask a
# threshold violation (while still failing the job on its own).
# Delegate to `make self-scan` so the local pre-commit
# gate and the CI gate share one canonical invocation; the
# repo-root `bca.toml` manifest owns path selection,
# `exclude_from`, the per-function thresholds, the cyclomatic `?`
# policy, and the baseline file, while `--jobs` defaults to
# `auto`. Drift between local and CI is now impossible by
# construction.
#
# `BCA_SINCE` opts the gate into the #356 diff-aware footer
# partition (#387): the per-file rollup splits into "Files in this
# range:" (offenders this PR touched) vs "Other offenders:", so a
# reviewer skimming a red gate sees what the PR introduced. On a
# `push` to main `GITHUB_BASE_REF` is empty, so it resolves to
# `origin/main` (a near-empty range — harmless). `--github-annotations`,
# the `$GITHUB_STEP_SUMMARY` digest, and the remediation block all
# auto-enable from GitHub Actions env vars, so no flag is needed for
# them. `--since` is presentational only; it never changes the
# gate's pass/fail.
- name: Threshold gate (baseline-ratcheted, diff-aware)
if: ${{ !cancelled() }}
run: make self-scan
env:
BCA_SINCE: origin/${{ github.base_ref || 'main' }}
# Dogfood bca's own SARIF writer: re-run the gate as a pure
# emitter (`--no-fail` keeps exit 0 regardless of offenders) so
# this step runs even when the gate above is red, and upload the
# result to Code Scanning below. Path selection, thresholds, and
# the baseline all come from the auto-discovered `bca.toml`
# manifest — same config the gate uses — so the published alerts
# match the gate's offender set. `if: always()` mirrors the report
# steps: the SARIF is most useful precisely when CI is red.
#
# We deliberately DROP `--report-suppressed` here: GitHub code
# scanning does not honor the SARIF `suppressions` property
# natively (it is absent from GitHub's supported-SARIF subset), so
# offenders emitted as `suppressions` entries surface as *open*
# alerts anyway — the opposite of the intent. Without the flag the
# SARIF carries only genuinely-active offenders (those not silenced
# by an in-source `bca: suppress` marker or covered by the
# baseline); suppressed/baselined debt is tracked where it lives —
# the markers in source and `.bca-baseline.toml` — not as phantom
# Code Scanning alerts. `--tier=soft=0.95` keeps the
# published alert set aligned with the soft (headroom) gate, so an
# unsuppressed function creeping into the 95-100% band shows up as
# an early warning before the hard gate trips.
- name: Generate self-scan SARIF
if: always()
run: >-
bca check --output-format sarif --no-fail
--tier=soft=0.95 --output bca.sarif
# Fork PRs get a read-only GITHUB_TOKEN without security-events
# scope, so the upload would error; skip it there (and when no
# SARIF was produced). `category: bca` keeps these alerts a
# distinct analysis from ci.yml's `category: clippy` upload.
- name: Upload self-scan SARIF to code scanning
if: always() && hashFiles('bca.sarif') != '' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository)
uses: github/codeql-action/upload-sarif@6f2ac81f5cd81fe351a197ba0b00d91d03cd2aee # v4.39.0
with:
sarif_file: bca.sarif
category: bca
- name: Upload reports artifact
if: always()
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: bca-reports
path: |
report.md
index.html
vcs-report.json
if-no-files-found: error
# Same PR-vs-main retention split as the book artifact above —
# see that comment for the rationale.
retention-days: ${{ github.event_name == 'pull_request' && 1 || 7 }}
assemble:
name: assemble Pages artifact
# PRs validate scan + book build but do not deploy.
if: github.event_name != 'pull_request'
needs: [build-book, scan]
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: book-html
path: site
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: bca-reports
path: reports-staging
- name: Merge reports into site
run: |
set -euo pipefail
mkdir -p site/reports
cp reports-staging/index.html site/reports/index.html
cp reports-staging/report.md site/reports/report.md
# Change-history (VCS) risk (issues #328, #573): the rendered
# ranking is folded into index.html / report.md via `--vcs`
# above; this is the deeper top-100 machine-readable download.
cp reports-staging/vcs-report.json site/reports/vcs-report.json
# Belt-and-braces in two places:
# * `site/.nojekyll` should already have travelled with
# book-html (build-book uploads with include-hidden-files
# true), but recreate it here to harden against a future
# upload-artifact regression silently dropping dotfiles.
# * `site/reports/.nojekyll` keeps the gh-pages fallback
# script from running Jekyll over the bca reports subtree.
touch site/.nojekyll site/reports/.nojekyll
# `configure-pages` exposes the Pages environment metadata
# (base URL, etc.) to downstream steps and populates the
# `environment.url` output that `actions/deploy-pages` surfaces
# on the run page. It is also where Pages-source enablement is
# validated; without it, `deploy-pages` can succeed against a
# mis-configured repo and produce an empty URL.
- name: Configure Pages
uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6.0.0
- name: Upload Pages artifact
uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
with:
path: site
deploy:
name: deploy to GitHub Pages
if: github.event_name != 'pull_request'
needs: assemble
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
# Serialize Pages deployments at the JOB level (not workflow
# level) so a slow deploy cannot overwrite a newer commit's
# output, without serialising the upstream scan/build-book jobs
# for every PR. cancel-in-progress=false: finishing the current
# deploy is preferable to interrupting it mid-upload.
concurrency:
group: pages-deploy
cancel-in-progress: false
steps:
- id: deployment
uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5.0.1