Repository navigation
526 lines (482 loc) · 25.3 KB
/
Copy pathci.yml
File metadata and controls
526 lines (482 loc) · 25.3 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
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
contents: read
# Pushes to main queue so each merge gets a test result; PR updates cancel the
# run for the superseded head.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name != 'push' }}
jobs:
# Restore the required relevance contexts removed by the baseline sync (#178).
# A failed comparison fails its gate; a docs-only diff can skip expensive jobs.
gate:
name: Code relevance
runs-on: ubuntu-24.04
timeout-minutes: 10
outputs:
relevant: ${{ steps.push.outputs.relevant || steps.pr.outputs.relevant }}
steps:
# A push only starts this workflow on main (the branch filter above), so
# the gate has nothing to decide: every push is relevant.
- name: A push is already branch-filtered
id: push
if: github.event_name == 'push'
run: echo 'relevant=true' >> "$GITHUB_OUTPUT"
# Full history, because the three-dot diff names the merge base of the
# PR's base tip and its merge commit, and a stacked PR's merge base sits
# further back still.
- name: Checkout the change
if: github.event_name == 'pull_request'
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
with:
persist-credentials: false # nothing here pushes
fetch-depth: 0
# --no-renames, so a file renamed out of a listed path is reported as a
# deletion under its original name and still counts. -z with a
# NUL-delimited read, so a pathname Git would quote or that contains a
# newline arrives raw and matches the pattern. The base SHA travels
# through env, not template interpolation into the script.
- name: Does the change touch anything the suite exercises?
id: pr
if: github.event_name == 'pull_request'
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
run: |
relevant=false
# Every pattern is quoted, so the glob survives array assignment and
# fires at `case` time, against the diff -- an unquoted `src/*` would
# expand against the checkout the runner happens to be standing in.
patterns=('src/*' 'test/*' 'scripts/*' 'frontend/*' 'config/*'
'bench/*' 'data/*' 'proofs/*' 'R/*' 'renv/*' 'Project.toml'
'Manifest.toml' 'renv.lock' '.Rprofile' 'install.sh' 'install.jl'
'start.sh' 'docs/occupancy/*' 'docs/occupancy-2026-10.adoc'
'.github/workflows/*')
# The diff goes to a file first because a process substitution hides
# git diff's exit status: an unavailable comparison SHA (a force-push
# on the base branch, for instance) or a repository error would read
# as "no changes", and the gate would post relevant=false -- skipping
# the Julia suite and the proof checker, whose `skipped` conclusion
# satisfies a required check -- so a relevant PR would pass silently.
# A failed diff must fail the gate itself: that is the one failure
# the required-check design cannot afford to skip. As in the
# occupancy gate, which this mirrors.
changed="$RUNNER_TEMP/ci-changed-files"
if ! git diff --no-renames --name-only -z "$BASE_SHA...HEAD" > "$changed"; then
echo "ERROR: git diff $BASE_SHA...HEAD failed" >&2
exit 1
fi
while IFS= read -r -d '' file; do
for pattern in "${patterns[@]}"; do
case "$file" in
$pattern) relevant=true; break 2 ;;
esac
done
done < "$changed"
echo "relevant=$relevant" >> "$GITHUB_OUTPUT"
occupancy_gate:
name: Occupancy relevance
runs-on: ubuntu-24.04
timeout-minutes: 10
outputs:
relevant: ${{ steps.push.outputs.relevant || steps.pr.outputs.relevant }}
steps:
# All pushes to main are relevant; PRs use the path classification below.
- name: A push to main is relevant
id: push
if: github.event_name == 'push'
run: echo 'relevant=true' >> "$GITHUB_OUTPUT"
# Full history, because the three-dot diff names the merge base of the
# PR's base tip and its merge commit -- a shallow checkout does not have
# both, and a stacked PR's merge base sits further back still.
- name: Checkout the change
if: github.event_name == 'pull_request'
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
with:
persist-credentials: false # nothing here pushes
fetch-depth: 0
- name: Does the change touch the occupancy reading?
id: pr
if: github.event_name == 'pull_request'
env:
# zizmor flags template expressions interpolated into run scripts
# (template-injection), and the gate script is easier to run locally
# with the value in an environment variable in any case. The base SHA
# is GitHub-supplied, but the indirection costs nothing.
BASE_SHA: ${{ github.event.pull_request.base.sha }}
run: |
relevant=false
patterns=('src/core/memory_probe.jl' 'src/core/provenance.jl' 'src/pipeline/tools.jl'
'src/server/routes/pipeline.jl' 'test/occupancy_disagreement_test.jl'
'test/unit/test_occupancy_grade.jl' 'scripts/check-occupancy-grades.jl'
'scripts/lint-occupancy-readings.sh' 'docs/occupancy/*'
'docs/occupancy-2026-10.adoc' 'scripts/check-occupancy.sh'
'scripts/fetch-siblings.sh' 'scripts/install-ci-julia.sh'
'Project.toml' 'Manifest.toml' 'renv.lock' 'config/defaults/tool_versions.yml'
'.github/workflows/*')
# The diff goes to a file first because a process substitution hides
# git diff's exit status: an unavailable comparison SHA or a repository
# error would read as "no changes", and the gate would post
# relevant=false -- skipping the two occupancy jobs, whose `skipped`
# conclusion satisfies a required check -- so a relevant PR would
# pass silently. A failed diff must fail the gate itself: that is
# the one failure the required-check design cannot afford to skip.
changed="$RUNNER_TEMP/occupancy-changed-files"
if ! git diff --no-renames --name-only -z "$BASE_SHA...HEAD" > "$changed"; then
echo "ERROR: git diff $BASE_SHA...HEAD failed" >&2
exit 1
fi
while IFS= read -r -d '' file; do
for pattern in "${patterns[@]}"; do
case "$file" in
$pattern) relevant=true; break 2 ;;
esac
done
done < "$changed"
echo "relevant=$relevant" >> "$GITHUB_OUTPUT"
test:
# The name is a fixed string, it carries no version, and the job has NO matrix.
# Both are required, and the first without the second is a trap that has already
# been sprung here once.
#
# A branch ruleset matches a required status check by the DISPLAY NAME GitHub posts
# for the job. A renamed check does not fail -- it is simply absent, and an absent
# required check can never be satisfied, so every pull request deadlocks until an
# admin bypasses the rule, triggered by nothing more alarming than editing a version
# number.
#
# MEASURED on hyperpolymath/MetaManifold-WebUI#39 (2026-09-21): this job, named exactly `Julia tests` but still
# carrying a 1x1 `strategy.matrix`, posted `Julia tests (1.12.5, ubuntu-24.04)`.
# GitHub appends the matrix combination whenever the name does not already reference
# the matrix, so a 1x1 matrix is still a matrix and the pins were still embedded in
# the check name. The matrix was therefore removed rather than merely renamed around:
# it selected exactly one combination and bought nothing.
#
# The two values it held are literals below, each keeping its own reasoning.
# `test/unit/test_install_pins.jl` asserts, for EVERY job in this file, that the name
# interpolates nothing AND that the job has no matrix -- so this cannot regress
# silently. If a matrix is ever genuinely wanted here, the required context must move
# to a matrix-free aggregator job first.
name: Julia tests
needs: gate
if: needs.gate.outputs.relevant == 'true'
# Explicit, not ubuntu-latest: the R pin below names an apt package revision built
# for 24.04, and a runner that silently rolled to the next LTS would take that pin
# with it. `runs-on` cannot read `env`, so this is a literal.
runs-on: ubuntu-24.04
# The repository's root .Rprofile sources renv/activate.R, so R started from
# the checkout auto-activates renv and rewrites the library paths. renv is a
# local-development convenience; CI installs into the system library and
# reaches it through R_LIBS_SITE, so the autoloader is switched off here.
# This variable covers the unprivileged R that Julia embeds through RCall.
# It cannot cover the installs themselves, which run under sudo: sudo resets
# the environment, so those commands pass --no-init-file instead.
env:
RENV_CONFIG_AUTOLOADER_ENABLED: "FALSE"
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- name: Set up Julia
run: bash scripts/install-ci-julia.sh
- name: Cache Julia packages
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: |
~/.julia/artifacts
~/.julia/packages
~/.julia/registries
key: julia-${{ runner.os }}-${{ hashFiles('Manifest.toml', 'config/defaults/tool_versions.yml') }}
# Every external version CI installs is read from the committed pin file, so CI
# and a developer's machine cannot drift apart. A temporary environment is used
# because the project environment cannot be instantiated until R is present:
# RCall's build step requires it.
- name: Read pinned versions
run: |
julia -e '
using Pkg
Pkg.activate(temp = true)
Pkg.add("YAML")
using YAML
pins = YAML.load_file("config/defaults/tool_versions.yml")
open(ENV["GITHUB_ENV"], "a") do io
println(io, "R_APT_VERSION=", pins["runtimes"]["r"]["apt_version"])
println(io, "BUN_VERSION=", pins["toolchain"]["bun"]["version"])
println(io, "CUTADAPT_SPEC=cutadapt==", pins["tools"]["cutadapt"]["version"])
println(io, "MULTIQC_SPEC=multiqc==", pins["tools"]["multiqc"]["version"])
fastqc = pins["tools"]["fastqc"]["archives"]["any"]
println(io, "FASTQC_URL=", fastqc["url"])
println(io, "FASTQC_SHA256=", fastqc["sha256"])
for tool in ("vsearch", "swarm")
archive = pins["tools"][tool]["archives"]["linux-x86_64"]
println(io, uppercase(tool), "_VERSION=", pins["tools"][tool]["version"])
println(io, uppercase(tool), "_URL=", archive["url"])
println(io, uppercase(tool), "_SHA256=", archive["sha256"])
end
end'
# R + required packages (needed by RCall)
# Install R directly via apt to avoid r-lib/actions/setup-r mangling
# R_HOME and stripping default packages (utils, methods, stats, etc.)
# The version is pinned to the apt revision named in the pin file, which tracks
# R.Version in renv.lock; CRAN's Ubuntu repository retains older revisions, so
# this resolves rather than merely requesting the newest.
- name: Set up R
run: |
wget --https-only -qO- https://cloud.r-project.org/bin/linux/ubuntu/marutter_pubkey.asc \
| sudo gpg --dearmor -o /usr/share/keyrings/r-project.gpg
echo "deb [signed-by=/usr/share/keyrings/r-project.gpg] https://cloud.r-project.org/bin/linux/ubuntu $(lsb_release -cs)-cran40/" \
| sudo tee /etc/apt/sources.list.d/r-project.list
sudo apt-get update -qq
sudo apt-get install -y \
r-base-core="$R_APT_VERSION" \
r-base-dev="$R_APT_VERSION" \
r-base-html="$R_APT_VERSION" \
r-recommended="$R_APT_VERSION" \
r-base="$R_APT_VERSION"
Rscript -e 'cat(R.version.string, "\n")'
- name: Install R system dependencies
run: sudo apt-get install -y libcurl4-openssl-dev libssl-dev libxml2-dev libfontconfig1-dev
# The R packages are restored from renv.lock, the same pin a developer restores
# from, rather than resolved against whatever the repositories serve that day.
# Asking for a bare package name pins nothing: a Bioconductor release is frozen,
# so dada2 and its kin held still, but CRAN publishes only its newest revision,
# so vegan silently moved off the pinned 2.7-3 and test_provenance caught the
# drift. Restoring into .Library keeps the system library that the RCall steps
# and R_LIBS_SITE below expect, and renv is invoked explicitly here rather than
# left to activate itself. --no-init-file stops R sourcing the repository's
# .Rprofile, and with it the renv autoloader, which would otherwise rebind
# .Library to its own sandbox under root's cache; the flag is used rather than
# the environment variable above because sudo resets the environment first.
#
# renv's package cache persists across runs, so a restore that fails partway
# resumes from the packages it already built. The cache sits outside ~ because
# the restore runs as root. The run id makes each key unique so every attempt
# saves its progress; restore-keys picks the newest match.
- name: Restore the renv cache
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: /opt/renv-cache
key: renv-${{ runner.os }}-R${{ env.R_APT_VERSION }}-${{ hashFiles('renv.lock') }}-${{ github.run_id }}
restore-keys: |
renv-${{ runner.os }}-R${{ env.R_APT_VERSION }}-${{ hashFiles('renv.lock') }}-
renv-${{ runner.os }}-R${{ env.R_APT_VERSION }}-
# R_CLI_DYNAMIC and TERM make renv print progress one line at a time in the log.
- name: Install R packages
env:
RENV_PATHS_CACHE: /opt/renv-cache
run: |
sudo mkdir -p "$RENV_PATHS_CACHE"
sudo chmod 777 "$RENV_PATHS_CACHE"
sudo env R_CLI_DYNAMIC=false TERM=dumb Rscript --no-init-file -e 'install.packages(c("renv", "BiocManager"), repos="https://cloud.r-project.org", lib=.Library)'
sudo env RENV_PATHS_CACHE="$RENV_PATHS_CACHE" R_CLI_DYNAMIC=false TERM=dumb Rscript --no-init-file -e 'renv::restore(project=".", library=.Library, prompt=FALSE)'
Rscript --no-init-file -e 'for (pkg in c("dada2","Biostrings","ShortRead","vegan","MASS","zCompositions","glmGamPoi")) if (!require(pkg,character.only=TRUE,quietly=TRUE)) stop(pkg, " failed to install")'
- name: Check DADA2 CSV assembly
run: Rscript --vanilla test/unit/test_dada2_tables.R
# Saved on failure too, since the packages built before a failure are the ones
# the next attempt should reuse. Root writes the cache, so it is made readable first.
- name: Make the renv cache readable
if: always()
run: sudo chmod -R a+rX /opt/renv-cache || true
- name: Save the renv cache
if: always()
uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: /opt/renv-cache
key: renv-${{ runner.os }}-R${{ env.R_APT_VERSION }}-${{ hashFiles('renv.lock') }}-${{ github.run_id }}
- name: Install cutadapt
run: pip install "$CUTADAPT_SPEC"
# The one tool CI does not pin. Ubuntu ships no versioned cd-hit revision worth
# naming, and 24.04 is frozen, so its cd-hit does not move under us; the version
# that arrives is recorded at preflight, where a discrepancy is visible.
- name: Install cd-hit
run: sudo apt-get install -y cd-hit
# Downloaded against the pinned URL and refused unless it hashes to the pinned
# SHA256, so CI runs the same bytes install.jl puts on a developer's machine.
- name: Install vsearch
run: |
source scripts/ci/fetch_pinned.sh
fetch_pinned "$VSEARCH_URL" vsearch.tar.gz
echo "$VSEARCH_SHA256 vsearch.tar.gz" | sha256sum -c -
tar xzf vsearch.tar.gz --warning=no-unknown-keyword
sudo mv "vsearch-$VSEARCH_VERSION-linux-x86_64/bin/vsearch" /usr/local/bin/vsearch
rm -rf vsearch.tar.gz "vsearch-$VSEARCH_VERSION-linux-x86_64"
vsearch --version
- name: Install swarm
run: |
source scripts/ci/fetch_pinned.sh
fetch_pinned "$SWARM_URL" swarm.tar.gz
echo "$SWARM_SHA256 swarm.tar.gz" | sha256sum -c -
tar xzf swarm.tar.gz --warning=no-unknown-keyword
sudo mv "swarm-$SWARM_VERSION-linux-x86_64/bin/swarm" /usr/local/bin/swarm
rm -rf swarm.tar.gz "swarm-$SWARM_VERSION-linux-x86_64"
swarm --version
- name: Install multiqc
run: pip install "$MULTIQC_SPEC"
# FastQC ships as a Java launcher script; `fastqc --version` checks that Java
# is on PATH as well.
- name: Install fastqc
run: |
source scripts/ci/fetch_pinned.sh
fetch_pinned "$FASTQC_URL" fastqc.zip
echo "$FASTQC_SHA256 fastqc.zip" | sha256sum -c -
sudo unzip -q -d /opt fastqc.zip
rm -f fastqc.zip
sudo chmod +x /opt/FastQC/fastqc
sudo ln -sf /opt/FastQC/fastqc /usr/local/bin/fastqc
fastqc --version
- name: Instantiate Julia project
run: julia --project=. -e 'import Pkg; Pkg.instantiate()'
- name: Set up Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version: ${{ env.BUN_VERSION }}
- name: Build frontend
working-directory: frontend
run: |
bun install --frozen-lockfile --ignore-scripts
bun run build
- name: Frontend unit tests
working-directory: frontend
run: bun run test
- name: Download PR2 databases
run: julia --project=. config/ci/download_databases.jl
- name: Rebuild RCall against installed R
run: julia --project=. -e 'import Pkg; Pkg.build("RCall")'
- name: Verify R packages visible from RCall
run: |
julia --project=. -e '
using RCall
R"cat(.libPaths(), sep=\"\n\")"
for pkg in ["dada2", "vegan", "tibble", "MASS", "zCompositions", "glmGamPoi"]
R"if (!require($pkg, character.only=TRUE, quietly=TRUE)) stop($pkg, \" not found\")"
end
'
- name: Run tests
env:
OCCUPANCY_RESULTS: ${{ runner.temp }}/occupancy-results.yml
CI_SKIP_TAXONOMY: "1"
R_LIBS_SITE: /usr/local/lib/R/site-library:/usr/lib/R/site-library:/usr/lib/R/library
# A failure also posts annotations, which the checks API serves without the full
# log. Annotations are capped at 4096 bytes, so each failing assertion gets its own
# (up to 8), followed by the summary rows that report a failure or an error.
shell: bash
run: |
command -v vsearch # The full suite must run the occupancy experiment, not skip it.
set +e
julia --project=. -t 2 --code-coverage=user --compiled-modules=no test/runtests.jl --integration --server 2>&1 | tee /tmp/julia-tests.log
status="${PIPESTATUS[0]}"
if [ "$status" -ne 0 ]; then
enc() { head -c 3500 | sed -e 's/%/%25/g' -e 's/\r/%0D/g' | awk '{printf "%s%%0A", $0}'; }
awk '
/Test Summary:/ { left = 0 }
/(Test Failed at|Error During Test at)/ || (left == 0 && /Got exception outside of a @test/) { n++; left = 60 }
left > 0 { print > ("/tmp/julia-failure-" n ".txt"); left-- }
' /tmp/julia-tests.log
shown=0
for f in $(ls /tmp/julia-failure-*.txt 2>/dev/null | sort -t- -k3 -n); do
shown=$((shown + 1))
[ "$shown" -gt 8 ] && break
echo "::error title=Julia test failure ${shown}::$(enc < "$f")"
done
awk '
/Test Summary:/ { print; p = index($0, "Pass"); pe = (p ? p + 3 : 0); t = index($0, "Total"); next }
pe && t && index($0, "|") && substr($0, pe + 1, t - pe - 1) ~ /[0-9]/ { print; next }
/ERROR:|Some tests did not pass/ { print }
' /tmp/julia-tests.log | tail -n 60 > /tmp/julia-tests-summary.txt
echo "::error title=Julia tests failed (summary)::$(enc < /tmp/julia-tests-summary.txt)"
if [ "$shown" -eq 0 ]; then
echo "::error title=Julia tests failed (tail)::$(tail -c 3500 /tmp/julia-tests.log | enc)"
fi
fi
exit "$status"
- name: Validate measured occupancy grades
run: |
julia --project=. scripts/check-occupancy-grades.jl "$RUNNER_TEMP/occupancy-results.yml"
bash scripts/lint-occupancy-readings.sh
- name: Process coverage
run: julia scripts/process-ci-coverage.jl
- name: Upload coverage to Codecov
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v6.0.2
with:
files: lcov.info
token: ${{ secrets.CODECOV_TOKEN }}
slug: JoshuaJewell/MetaManifold-WebUI
fail_ci_if_error: false
# Machine-checked proofs of the Benjamini-Hochberg adjustment and the TSS/RLE
# scaling algebra (proofs/agda, proofs/agda/README.md).
# Agda publishes no 2.6.4.3 binary, and agda-stdlib 2.1 requires Agda 2.6.4.x,
# so the pair comes from Debian trixie, which packages exactly that pair with
# pre-built library interfaces. The image is pinned by digest and the two
# packages by revision, so a rebuild in the archive fails loudly here instead
# of silently changing the checker. These pins live here rather than in
# config/defaults/tool_versions.yml because no other job and no installer
# reads them.
agda:
name: Agda proofs
needs: gate
if: needs.gate.outputs.relevant == 'true'
runs-on: ubuntu-24.04
container: debian:trixie-20250929-slim@sha256:1caf1c703c8f7e15dcf2e7769b35000c764e6f50e4d7401c355fb0248f3ddfdb
timeout-minutes: 30
env:
DEBIAN_FRONTEND: noninteractive
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- name: Install Agda and the standard library
run: |
apt-get update -qq
apt-get install -y --no-install-recommends agda-bin=2.6.4.3-1+b2 agda-stdlib=2.1-4
agda --version | grep -qx 'Agda version 2.6.4.3'
grep -qx 'name: standard-library-2.1' /usr/share/agda-stdlib/standard-library.agda-lib
# Runs before the type-check: --safe already refuses postulates, but the
# audit also refuses holes, check-disabling pragmas and flags, and any
# module that MetaManifold/All.agda does not import (an unimported module
# is never type-checked at all).
- name: Axiom audit
run: bash proofs/tests/axiom-audit.sh
- name: Type-check
env:
AGDA_DIR: /tmp/agda-dir
run: |
mkdir -p "$AGDA_DIR"
echo /usr/share/agda-stdlib/standard-library.agda-lib > "$AGDA_DIR/libraries"
cd proofs/agda
agda MetaManifold/All.agda
occupancy:
name: Occupancy typed peak
needs: occupancy_gate
if: needs.occupancy_gate.outputs.relevant == 'true'
runs-on: ubuntu-24.04
container: debian:trixie-20250929-slim@sha256:1caf1c703c8f7e15dcf2e7769b35000c764e6f50e4d7401c355fb0248f3ddfdb
timeout-minutes: 30
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262
- name: Install proof tools
run: |
apt-get update -qq
apt-get install -y --no-install-recommends ca-certificates curl git agda-bin=2.6.4.3-1+b2 agda-stdlib=2.1-4
- name: Set up Julia
run: bash scripts/install-ci-julia.sh
- name: Check Session IR and occupancy proofs
run: bash scripts/check-occupancy.sh
# The full suite already runs the actual two-thread vsearch experiment. This
# required context reports that result, including the fresh grade validation,
# instead of paying for a second Julia/R installation and repeating the run.
disagreement:
name: Occupancy disagreement test
needs: [occupancy_gate, test]
if: always() && needs.occupancy_gate.result == 'success' && needs.occupancy_gate.outputs.relevant == 'true'
runs-on: ubuntu-24.04
timeout-minutes: 5
steps:
- name: Require the executed experiment and grade validation to pass
env:
TEST_RESULT: ${{ needs.test.result }}
run: |
if [ "$TEST_RESULT" != success ]; then
echo "ERROR: full suite, occupancy experiment or measured-grade validation did not pass ($TEST_RESULT)" >&2
exit 1
fi
echo 'The full Julia suite passed, including the two-thread vsearch experiment and fresh empirical grade validation.' >> "$GITHUB_STEP_SUMMARY"