-
Notifications
You must be signed in to change notification settings - Fork 1
490 lines (477 loc) · 28.8 KB
/
Copy pathci.yml
File metadata and controls
490 lines (477 loc) · 28.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
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
# Sovereign CI — calls reusable workflow from paiml/.github
# Change once in paiml/.github → applies to all repos
#
# Jobs provided by sovereign-ci.yml:
# test: cargo test --lib (self-hosted clean-room)
# lint: cargo clippy --all-targets -- -D warnings + cargo fmt --check
# coverage: cargo llvm-cov + codecov upload
# security: cargo audit (fleet, continue-on-error)
# provenance: SLSA attest-build-provenance
# gate: aggregates test+lint results
#
# NOTE: sovereign-ci compiles NO doctests. Its test job is hard-scoped to
# `cargo test --lib` and, with use_nextest: true, is run by cargo-nextest,
# which cannot execute doctests at all. See the `doctests` job below (#318).
name: CI
on:
push:
branches: [main, master]
# forjar#604: sovereign-ci's `coverage_on: tag` is TWO edits -- the input
# below AND a v* tag trigger here. A push filtered to branches never fires
# for a tag, so with the input alone coverage would run on manual dispatch
# only (paiml/.github#74 says so in the input's own description).
tags: ['v*']
pull_request:
branches: [main, master]
workflow_dispatch:
concurrency:
group: ci-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
# PMAT-237: what can this change break? One job, one decision, and every
# heavy job below gates on it. The release gate (`make dogfood-release`) is
# untouched and still runs everything.
classify:
runs-on: [self-hosted, clean-room, X64]
outputs:
code: ${{ steps.class.outputs.code }}
gate_c: ${{ steps.class.outputs.gate_c }}
gate_d: ${{ steps.class.outputs.gate_d }}
gates: ${{ steps.class.outputs.gates }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- id: class
uses: ./.github/actions/changed-class
ci:
if: needs.classify.outputs.code == 'true'
needs: classify
uses: paiml/.github/.github/workflows/sovereign-ci.yml@main
with:
repo: ${{ github.event.repository.name }}
# PMAT-155 Phase 2 nextest pilot (build-performance.md §4.3 + §7 Phase 2).
# forjar = medium workload, healthy --lib suite. cargo-nextest is baked into
# sovereign-ci:stable (infra Dockerfile). Pilot only — keep until F11 test-job
# p95 ≤ 300s is verified over 7 days, then promote fleet-wide.
use_nextest: true
# forjar#604: coverage on tagged releases only. On a PR or a branch push
# the coverage job is SKIPPED and the gate prints "coverage: NOT MEASURED"
# -- never green. On a v* tag push it is mandatory: skipped there is RED.
coverage_on: tag
secrets: inherit
# Release preflight: a stale Cargo.lock must fail PR CI, not the tag.
# Every v1.4.x release failed because Cargo.toml was bumped without
# committing the refreshed Cargo.lock — release.yml's `cargo package`
# then dirties the tree and skips release creation entirely.
lockfile:
name: lockfile-preflight
runs-on: [self-hosted, clean-room, X64]
# infra#430: sixteen clean-room runners share one ~/.cargo, and an hourly
# reaper deletes registry/src entries by mtime -- under a live build, which
# makes rustc read a source file that has just vanished:
# couldn't read .../registry/src/.../<crate>/src/<file>.rs: No such file
# error: could not parse/generate dep info at: .../deps/<crate>.d
# MEASURED on this branch: msrv and coverage both died that way the first
# time PMAT-547 moved them off GitHub-hosted runners, where each job had a
# private ~/.cargo by construction. A registry private to this runner takes
# the job off the shared surface. The path is outside the workspace, so it
# survives the checkout clean and is a cache rather than a cold download.
# The host-side fix is paiml/infra's.
env:
CARGO_HOME: ${{ github.workspace }}/../cargo-home-${{ github.job }}
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Verify Cargo.lock is committed and current
# #423: the contract crates are workspace members, so the root crate's
# path dependencies are unpublished until the release publishes them in
# order. `--workspace` packages the members first and resolves the root
# against that overlay (cargo >= 1.90), which is exactly what
# `cargo publish --workspace` does at release time.
run: cargo package --locked --no-verify --workspace
# Regression guard (#179): every shipped standalone example config must pass
# `forjar validate`. The sovereign-ci `test` job only runs `cargo test --lib`,
# so this integration test (tests/examples_validate.rs) needs an explicit step.
# Deterministic and offline — validate never opens a network/SSH connection.
#
# THAT `--lib` IS THE POINT OF THIS JOB, and it generalises: NOTHING in this
# repo's CI runs `tests/*.rs` except the targets named here by hand. A
# falsification test under tests/ that is not in this list is written, is
# green on a developer's machine, and is never executed by CI again — which
# is the same "guard nobody runs" shape as #242 and #298. Add new structural
# guards to this list, or accept that they are documentation.
examples-validate:
if: needs.classify.outputs.code == 'true'
needs: classify
name: examples-validate
runs-on: [self-hosted, clean-room, X64]
# infra#430: sixteen clean-room runners share one ~/.cargo, and an hourly
# reaper deletes registry/src entries by mtime -- under a live build, which
# makes rustc read a source file that has just vanished:
# couldn't read .../registry/src/.../<crate>/src/<file>.rs: No such file
# error: could not parse/generate dep info at: .../deps/<crate>.d
# MEASURED on this branch: msrv and coverage both died that way the first
# time PMAT-547 moved them off GitHub-hosted runners, where each job had a
# private ~/.cargo by construction. A registry private to this runner takes
# the job off the shared surface. The path is outside the workspace, so it
# survives the checkout clean and is a cache rather than a cold download.
# The host-side fix is paiml/infra's.
env:
CARGO_HOME: ${{ github.workspace }}/../cargo-home-${{ github.job }}
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Validate all standalone example configs
run: cargo test --locked --test examples_validate
# #292: a .rs file under src/ that no `mod`, `#[path]` or `include!`
# names is compiled by nothing — not linted, not type-checked, and its
# tests do not run. Three were in that state, one of which did not parse
# as Rust at all. Cheap: a directory walk, no build of the crate needed.
- name: No source file may be orphaned or duplicated
run: cargo test --locked --test falsification_no_orphaned_source_files
# #298: every citation in contracts/ must resolve to the exact item it
# names, in the file it names, and every binding must name an equation
# its contract defines. This replaces the Python resolver that used to
# live in proofs.yml, whose regex read 77 of 211 citations and reported
# "every resolvable falsifier citation resolves" over seven dangling
# ones. One resolver, in one dialect, in a place that runs it.
- name: Every contract citation must resolve
run: cargo test --locked --test falsification_contract_citations_resolve
# forjar#549: a drift query the target never answered is UNMEASURED, not
# drift. Asks 203.0.113.9 (TEST-NET-3) over ssh, so it waits out the
# connect timeout once per test.
- name: An unanswered drift query is not drift
run: cargo test --locked --test falsification_drift_unmeasured_is_not_drift
# FALSIFY-FVS-005. Spawns the built binary, so it is the slow one here.
- name: The binary advertises no undeclared transport
run: cargo test --locked --test e2e_transport_absence_t
# #376: `undo` re-converged to the CURRENT config instead of the target
# generation, so it exited 0 printing "1 converged" while the managed
# bytes never moved. These assert the BYTES AT THE PATH, never the
# summary line — the summary is what the defect printed. Includes the
# control that fails an "always rewrite" implementation, and the jidoka
# cases: an undo that cannot faithfully replay must exit non-zero and
# leave the host untouched.
- name: undo must actually undo, or refuse
run: cargo test --locked --test falsification_undo_actually_undoes
# #377: `undo` paired the cwd forjar.yaml with an unrelated --state-dir
# and applied it. Carries the over-correction guards too: one operator
# editing and undoing their own stack must NEVER be refused.
- name: undo must refuse a foreign state dir, and only a foreign one
run: cargo test --locked --test falsification_undo_state_dir_interlock
# #386: the Coverage lane cached `target`, and `cargo llvm-cov` over this
# crate's 242 integration test binaries is a 70.70 GiB tree — already the
# size of a hosted runner's disk. The cache asked tar+zstd for a second
# copy of it on the same filesystem: the SAVE ENOSPC'd on every run (a
# `##[warning]`, so the job stayed green and the cache was never written),
# and when the margin was tighter the ENOSPC landed mid-build and killed
# the runner's Worker process, taking the job's logs with it. Parses the
# workflows and prints its denominator; self-hosted jobs are exempt.
- name: No hosted job may cache a Rust build directory
run: cargo test --locked --test falsification_hosted_jobs_do_not_cache_target
# #401: fourteen paths were BOTH tracked and matched by .gitignore
# (3,759,246 bytes). Nothing visibly broke — gitignore does not un-track a
# file — which is exactly why it survived, while the post-commit hook
# re-staged a 1.2 MB `.pmat/baseline.json` after every commit and any two
# branches conflicted on its `created_at` line. Asserts the index against
# the rules, with `--no-index` (without it git answers about the index and
# reports every tracked path as not-ignored, hiding the defect) and
# WITHOUT `-v` (which prints negation matches and exits 0, making the
# assertion unsatisfiable while any file is deliberately re-included).
- name: No tracked file may be gitignored
run: cargo test --locked --test falsification_no_tracked_file_is_gitignored
# #400: the quorum gate took the branch NAME from the pushed ref and
# everything else — diff, receipt, falsification test — from the local
# checkout. Pushing a branch you were not standing on was refused with
# "no quorum receipt" for a receipt that was in the pushed commit, and an
# UNTRACKED receipt carrying a `waived.reason` bypassed the whole gate
# silently, for exactly the enforced authors QUORUM_SKIP is refused to.
# Drives the real scripts/quorum-gate.sh against synthetic repos.
- name: The quorum gate must judge the pushed commit
run: cargo test --locked --test falsification_quorum_gate_reads_the_pushed_ref
# #629: nightly.yml rebuilt only when a commit landed in the last 24h, so a
# commit followed by a quiet day, or a failed nightly, was never built and
# the nightly sat 12 days behind main. Executes the gate script from the
# parsed workflow against synthetic histories, and asserts the release tags
# the commit it built (`target_commitish`), which the tag-vs-HEAD gate needs.
- name: The nightly must rebuild whenever its tag is not HEAD
run: cargo test --locked --test falsification_nightly_gate_is_tag_vs_head
# #614: under `shell: bash` the nightly's Windows leg resolved Git's msys
# perl, which lacks Params::Check, so openssl-src died and the release was
# skipped for 11 days. Executes the parsed build step with stub cargo/cross
# and a planted Strawberry perl: Windows builds with it and fails before
# cargo without it, and the other legs leave OPENSSL_SRC_PERL unset.
- name: The nightly's Windows leg must build OpenSSL with Strawberry perl
run: cargo test --locked --test falsification_nightly_windows_openssl_perl
# PMAT-159: the sudo transport moved from `sudo bash /dev/fd/3` (sudo closes
# fd 3 -> exit 127 for every sudo: true resource) to a private temp file. This
# is the ALWAYS-ON falsifier: a fake sudo that closes every fd >= 3 and a fake
# id that forces the non-root branch, then the real emitted wrapper. It went
# RED on the old emitter and needs no privilege. The reusable test job is
# `cargo test --lib`, so it runs here or nowhere.
- name: The sudo transport survives closefrom
run: cargo test --locked --test falsification_sudo_transport_closefrom_emulated
# And the LIVE half of the same falsifier. The emulated test fakes `sudo`,
# so it can show the transport surviving an emulated closefrom but never
# the host's real one with a real uid 0 on the other side.
# tests/falsification_sudo_transport_survives_closefrom.rs does exactly
# that, but every test in it is gated on `sudo -n true` and SKIPS where
# that is missing — and a skip is an absence of evidence: on a host
# without passwordless sudo that file was green against the fd-3 emitter
# too. FORJAR_REQUIRE_SUDO_TESTS=1 turns each of those skips into a panic
# naming the missing capability, so a green step here means the gated
# tests RAN, under real sudo, not that they declined to.
#
# PMAT-547 moved this job from ubuntu-latest to the fleet, and with it
# the only thing the old comment could offer: GitHub DOCUMENTS that a
# hosted runner's `runner` user is non-root with passwordless sudo, and
# documents nothing about ours. So the citation is gone and only the
# switch is left — which is the half that was ever load-bearing. If a
# fleet runner does not give this step passwordless sudo, the step fails
# loudly naming the missing capability instead of skipping silently.
# Unmeasured is not the same as satisfied, and this is how it says so.
- name: The sudo transport survives closefrom under real sudo (required, not skippable)
env:
FORJAR_REQUIRE_SUDO_TESTS: "1"
run: cargo test --locked --test falsification_sudo_transport_survives_closefrom
# PMAT-159: a release ships ONE version and every artefact that states it
# must state the same one — manifest, lockfile, changelog, and the binary's
# own `--version`. The target existed and ran in NO CI job, which is the
# same absence as not having it. `--locked` is the load-bearing flag, not
# house style: under a bare `cargo test` cargo repairs a stale `Cargo.lock`
# before the test body runs, so the #131 lockfile invariant cannot go red
# here; `--locked` makes cargo refuse a lagging lock at resolution and the
# test is the message-bearing witness for it. Drop the flag and this step
# measures three of its four legs.
- name: The release version is coherent (manifest, lock, changelog, binary)
run: cargo test --locked --test falsification_version_matches_manifest
# Regression guard (#237): `default-features = false` must yield a LIBRARY that
# compiles. tests/falsification_default_features_trim.rs pins the manifest's
# shape, but only a real build proves the module gates are consistent — trait
# impls, macro expansions and glob re-exports are invisible to a manifest test.
# The gates are load-bearing in one direction only: dropping `cli` must not
# break the library, and nothing in the default lane would ever notice if it did.
no-default-features:
if: needs.classify.outputs.code == 'true'
needs: classify
name: no-default-features
runs-on: [self-hosted, clean-room, X64]
# infra#430: sixteen clean-room runners share one ~/.cargo, and an hourly
# reaper deletes registry/src entries by mtime -- under a live build, which
# makes rustc read a source file that has just vanished:
# couldn't read .../registry/src/.../<crate>/src/<file>.rs: No such file
# error: could not parse/generate dep info at: .../deps/<crate>.d
# MEASURED on this branch: msrv and coverage both died that way the first
# time PMAT-547 moved them off GitHub-hosted runners, where each job had a
# private ~/.cargo by construction. A registry private to this runner takes
# the job off the shared surface. The path is outside the workspace, so it
# survives the checkout clean and is a cache rather than a cold download.
# The host-side fix is paiml/infra's.
env:
CARGO_HOME: ${{ github.workspace }}/../cargo-home-${{ github.job }}
timeout-minutes: 20
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Library must build with every default feature off
run: cargo check --locked --no-default-features --lib
# Regression guard (#318): no PR job compiled a single one of this crate's 87
# doctests. sovereign-ci's `test` is hard-scoped to `cargo test --lib` and, with
# use_nextest: true, is run by cargo-nextest, which cannot execute doctests at
# all; `lockfile` is --no-verify and `examples-validate` selects one integration
# target. Doctests were therefore first compiled at the clean-room release gate
# (GATE B3), where one wrong ``` fence costs a full release cycle — which is how
# paiml/forjar#315 passed every PR check and then failed the release.
# sovereign-ci has no doctest input and `--doc` cannot ride in on `test_args`,
# because it conflicts with that job's `--lib` target selection. So the PR lane
# needs its own job. Deterministic and offline; ~8s of doctests once built.
doctests:
if: needs.classify.outputs.code == 'true'
needs: classify
name: doctests
runs-on: [self-hosted, clean-room, X64]
# infra#430: sixteen clean-room runners share one ~/.cargo, and an hourly
# reaper deletes registry/src entries by mtime -- under a live build, which
# makes rustc read a source file that has just vanished:
# couldn't read .../registry/src/.../<crate>/src/<file>.rs: No such file
# error: could not parse/generate dep info at: .../deps/<crate>.d
# MEASURED on this branch: msrv and coverage both died that way the first
# time PMAT-547 moved them off GitHub-hosted runners, where each job had a
# private ~/.cargo by construction. A registry private to this runner takes
# the job off the shared surface. The path is outside the workspace, so it
# survives the checkout clean and is a cache rather than a cold download.
# The host-side fix is paiml/infra's.
env:
CARGO_HOME: ${{ github.workspace }}/../cargo-home-${{ github.job }}
timeout-minutes: 20
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Compile and run every doctest
run: cargo test --locked --doc
# Top-level gate: satisfies org ruleset which requires check named "gate".
# The reusable workflow produces "ci / gate" but rulesets need exact match on "gate".
# PMAT-163: the tool-free dogfood gates run here on every PR — surface
# derived from the built artifact (C) and every documented invocation
# executed against fixtures (D), plus the guard tests. Gates B (pmat comply),
# F (coverage + mutants), G (pv contracts) and H (crux reconcile) need pmat
# and pv, which are provisioned on the clean-room hosts by forjar.yaml, so
# they run there through `make dogfood-release`.
# PMAT-542: the gates that MEASURE THE BUILT BINARY, and the release build
# they need, run only when the change can move them. Measured on the 25 PRs
# merged as of cddf78cd: 7 were code=true and could reach neither gate, and
# each paid 21.3 minutes of a 25.2-minute critical path for a surface its diff
# could not touch. The guard tests below are a separate job because they are
# cheap and every code change can move them.
#
# THIS IS A LATENCY CHANGE, NOT A COVERAGE CHANGE. `make dogfood-release` runs
# A-H and T over the whole window before any tag, and the `gate` job below
# refuses this job's skip whenever `gates` says it should have run.
dogfood-surface:
if: needs.classify.outputs.code == 'true' && needs.classify.outputs.gates != 'none'
needs: classify
runs-on: [self-hosted, clean-room, X64]
# infra#430: sixteen clean-room runners share one ~/.cargo, and an hourly
# reaper deletes registry/src entries by mtime -- under a live build, which
# makes rustc read a source file that has just vanished:
# couldn't read .../registry/src/.../<crate>/src/<file>.rs: No such file
# error: could not parse/generate dep info at: .../deps/<crate>.d
# MEASURED on this branch: msrv and coverage both died that way the first
# time PMAT-547 moved them off GitHub-hosted runners, where each job had a
# private ~/.cargo by construction. A registry private to this runner takes
# the job off the shared surface. The path is outside the workspace, so it
# survives the checkout clean and is a cache rather than a cold download.
# The host-side fix is paiml/infra's.
env:
CARGO_HOME: ${{ github.workspace }}/../cargo-home-${{ github.job }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Build the artifact the surface gates measure
run: cargo build --release --locked
- name: Gate C — surface from the built artifact
if: needs.classify.outputs.gate_c == 'true'
run: bash scripts/dogfood/surface.sh
- name: Gate C — NOT-SELECTED
if: needs.classify.outputs.gate_c != 'true'
run: echo "GATE C NOT-SELECTED this change touches nothing gate C reads (the built binary, docs/audits/surface_audit.csv, scripts/dogfood/surface.sh) — measured in full by make dogfood-release before the tag"
- name: Cookbook — clone paiml/forjar-cookbook (public) beside the workspace for gate D
if: needs.classify.outputs.gate_d == 'true'
run: git clone --quiet --depth 1 --branch master https://github.com/paiml/forjar-cookbook "$RUNNER_TEMP/forjar-cookbook"
- name: Gate D — documented invocations run against fixtures
if: needs.classify.outputs.gate_d == 'true'
run: bash scripts/dogfood/docs.sh
env:
COOKBOOK: ${{ runner.temp }}/forjar-cookbook
- name: Gate D — NOT-SELECTED
if: needs.classify.outputs.gate_d != 'true'
run: echo "GATE D NOT-SELECTED this change touches nothing gate D reads (the built binary, README.md, docs/audits/surface_audit.csv, scripts/dogfood/docs.sh) — measured in full by make dogfood-release before the tag"
dogfood-guards:
if: needs.classify.outputs.code == 'true'
needs: classify
runs-on: [self-hosted, clean-room, X64]
# infra#430: sixteen clean-room runners share one ~/.cargo, and an hourly
# reaper deletes registry/src entries by mtime -- under a live build, which
# makes rustc read a source file that has just vanished:
# couldn't read .../registry/src/.../<crate>/src/<file>.rs: No such file
# error: could not parse/generate dep info at: .../deps/<crate>.d
# MEASURED on this branch: msrv and coverage both died that way the first
# time PMAT-547 moved them off GitHub-hosted runners, where each job had a
# private ~/.cargo by construction. A registry private to this runner takes
# the job off the shared surface. The path is outside the workspace, so it
# survives the checkout clean and is a cache rather than a cold download.
# The host-side fix is paiml/infra's.
env:
CARGO_HOME: ${{ github.workspace }}/../cargo-home-${{ github.job }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Guard tests
run: cargo test --locked --test falsification_dogfood_skill_is_named --test falsification_dogfood_scripts_declare_mutations --test falsification_dogfood_release_check_pr_window --test falsification_dogfood_harness_and_quorum --test falsification_crux_gate_reads_the_release_section --test falsification_coverage_gate_mutation_scope --test falsification_cb200_ratchet_measures_this_tree --test falsification_pr_lane_runs_what_the_change_can_break --test falsification_pr_lane_selects_the_gate_the_change_can_move --test falsification_gate_a_the_pr_and_its_commits_name_one_ticket
gate:
runs-on: [self-hosted, clean-room, X64]
needs: [classify, ci, lockfile, examples-validate, no-default-features, doctests, dogfood-surface, dogfood-guards]
if: always()
steps:
- name: Check required jobs
env:
CODE: ${{ needs.classify.outputs.code }}
GATES: ${{ needs.classify.outputs.gates }}
run: |
# PMAT-237: a job may be SKIPPED only because the change cannot reach
# it. The gate re-reads the class rather than trusting the skip: if
# the change IS code and a heavy job did not run, that is a hole, not
# a saving, and this gate refuses it by name.
# Written with explicit `if`, not `[ … ] && return 0`: an AND-list
# that fails at statement level exits the step under `bash -e` before
# the message is printed, and a gate that fails silently is half a
# gate.
# THE CLASS ITSELF MUST HAVE BEEN MEASURED (PMAT-237).
#
# If `classify` fails or is cancelled its output is EMPTY, every heavy
# job's `if` is false so they all skip, and a gate that read an empty
# class as "not code" would pass over a change nothing tested. A
# review lane found exactly that. An unmeasured class is code.
if [ "${{ needs.classify.result }}" != "success" ]; then
echo "::error::classify did not run (${{ needs.classify.result }}), so nothing knows what this change can break"
exit 1
fi
if [ -z "$CODE" ]; then
echo "::error::the class is empty — treating it as code and refusing"
exit 1
fi
# PMAT-542: the SELECTION must have been measured too. The classifier
# prints `none` and never the empty string, exactly so that "nothing
# was selected" and "nothing computed a selection" cannot look the
# same here. An empty value is the second one.
if [ -z "$GATES" ]; then
echo "::error::the gate selection is empty — nothing measured which gates this change can move, and an unmeasured selection is not a selection"
exit 1
fi
ok() {
case "$2" in
success) return 0 ;;
skipped)
if [ "$CODE" != "true" ]; then
echo "$1: skipped — the change touches nothing it reads"
return 0
fi
echo "::error::$1 was skipped on a change the classifier called code"
return 1
;;
*)
echo "::error::$1: $2"
return 1
;;
esac
}
ok "ci" "${{ needs.ci.result }}" || exit 1
if [ "${{ needs.lockfile.result }}" != "success" ]; then
echo "lockfile preflight failed: ${{ needs.lockfile.result }}"
exit 1
fi
ok "examples-validate" "${{ needs.examples-validate.result }}" || exit 1
ok "no-default-features" "${{ needs.no-default-features.result }}" || exit 1
ok "doctests" "${{ needs.doctests.result }}" || exit 1
ok "dogfood-guards" "${{ needs.dogfood-guards.result }}" || exit 1
# PMAT-542: dogfood-surface skips on its OWN condition, which is
# `gates != none` rather than the class. Re-read the selection here
# for the same reason `ok` re-reads the class: a skip is a claim until
# the thing that decided it says so.
case "${{ needs.dogfood-surface.result }}" in
success) echo "dogfood-surface: ran gates $GATES" ;;
skipped)
if [ "$CODE" != "true" ]; then
echo "dogfood-surface: skipped — the change touches nothing it reads"
elif [ "$GATES" = "none" ]; then
echo "dogfood-surface: NOT-SELECTED — this change can move neither gate C nor gate D; both are measured in full by make dogfood-release before the tag"
else
echo "::error::dogfood-surface was skipped while the selection says $GATES"
exit 1
fi
;;
*)
echo "::error::dogfood-surface: ${{ needs.dogfood-surface.result }}"
exit 1
;;
esac
echo "All required jobs passed"