From 0c39e40a57f5d84f4aec8879fbe482c1c1ba44e7 Mon Sep 17 00:00:00 2001 From: Lann Martin Date: Sun, 9 Aug 2026 20:25:28 -0400 Subject: [PATCH] Adopt the gha module: one gha:: recipe per CI job The websocket/webcrypto arrangement: CI job bodies live in .github/justfile (the gha module), each workflow job runs exactly one `just gha::` recipe with per-recipe log groups and failure annotations, and `just ci` is exactly CI. The workflows carry no logic beyond environment setup. - ci.yml's four jobs each collapse to one gha:: step (rust-checks, smoke, conformance-checks, interop). - timing-lab-scheduled moves into the gha module (workflow-only plumbing, the webcrypto placement); timing-lab.yml follows. - `default` lists recipes, so bare `just` no longer runs `check` -- the family convention. - AGENTS.md's checks table gains the `just ci` row. --- .github/justfile | 101 +++++++++++++++++++++++++++++++ .github/workflows/ci.yml | 51 +++++++--------- .github/workflows/timing-lab.yml | 2 +- AGENTS.md | 1 + justfile | 64 +++++--------------- 5 files changed, 142 insertions(+), 77 deletions(-) create mode 100644 .github/justfile diff --git a/.github/justfile b/.github/justfile new file mode 100644 index 0000000..d5d15dd --- /dev/null +++ b/.github/justfile @@ -0,0 +1,101 @@ +# CI job bodies: each workflow job runs exactly one `gha::` recipe, so +# `just ci` is exactly CI and the workflow files carry no logic beyond +# environment setup. + +set working-directory := '..' + +# Wrap one recipe in a GitHub Actions log group (plain passthrough +# locally). +[private] +_step recipe: + #!/usr/bin/env bash + set -euo pipefail + if [ "${GITHUB_ACTIONS:-}" = "true" ]; then + echo "::group::just {{recipe}}" + status=0 + just {{recipe}} || status=$? + echo "::endgroup::" + if [ $status -ne 0 ]; then + echo "::error title=just {{recipe}} failed::exit status $status" + exit $status + fi + else + just {{recipe}} + fi + +# The static checks — fmt, clippy, workspace tests, wasm build — plus the +# release-wasm AES-table audit, which needs the release build and so +# shares this job's cache. +rust-checks: + @just gha::_step fmt-check + @just gha::_step clippy + @just gha::_step test + @just gha::_step build-wasm + @just gha::_step audit + +# The QUIC loopback rig under Wasmtime and both tls-virt deliveries +# against openssl over real TCP. +smoke: + @just gha::_step smoke-quic + @just gha::_step smoke-tls-virt + +# The cross-implementation conformance suite: the shared guest suite +# composed with each delivery, the aggregated matrix diffed against the +# committed one. +conformance-checks: + @just gha::_step conformance-ct::all + @just gha::_step conformance-ct::matrix-check + +# Cross-implementation interop over real transports: the composed +# component against OpenSSL and Go peers over TCP, and the noq leg +# against quic-go over UDP. +interop: + @just gha::_step interop-tls + @just gha::_step interop-quic + +# The timing lab workflow's entry point (timing-lab.yml, schedule-only, +# not a `ci` job): one retry at 4x samples before reporting failure — a +# flake washes out at 4x while a real leak's t grows, so the retry +# separates them; shared runners make the retry mandatory rather than +# optional. Records the environment the verdicts are valid for (verdicts +# hold per runtime version and per microarchitecture; a wasmtime upgrade +# or a runner change invalidates previous quiet readings). Under GitHub +# Actions the report also lands in the job summary. +timing-lab-scheduled: + #!/usr/bin/env bash + set -uo pipefail + samples="${TIMING_LAB_SAMPLES:-2000}" + cpu=$(sed -n 's/^model name[^:]*: //p' /proc/cpuinfo | head -n1) + [ -n "$cpu" ] || cpu="$(sed -n 's/^CPU implementer[^:]*: /implementer /p' /proc/cpuinfo | head -n1)" + environment="$(wasmtime --version), $(uname -m), ${cpu:-unknown CPU}" + echo "timing lab environment: ${environment}" + run() { TIMING_LAB_SAMPLES="$1" just timing-lab 2>&1; } + + report=$(run "$samples"); status=$? + printf '%s\n' "$report" + if [ $status -ne 0 ]; then + samples=$(( samples * 4 )) + echo + echo "timing lab: verdicts diverged; retrying at ${samples} samples/class before reporting failure." + report=$(run "$samples"); status=$? + printf '%s\n' "$report" + fi + + if [ -n "${GITHUB_STEP_SUMMARY:-}" ]; then + { + echo "### timing lab — ${samples} samples/class" + echo + echo "Environment: ${environment}" + echo + # The lab prints its report as a markdown table; lift it verbatim. + printf '%s\n' "$report" | sed -n '/^| surface/,/^$/p' + if [ $status -eq 0 ]; then + echo "All surfaces matched expectations." + else + echo "**Surfaces diverged from expectation, and again on a retry at ${samples} samples/class.**" + echo "A quiet positive control means the harness cannot detect leaks at this" + echo "measurement distance; a LEAK on a real surface warrants investigation." + fi + } >> "$GITHUB_STEP_SUMMARY" + fi + exit $status diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f7abe06..6bc5d53 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,11 +1,9 @@ name: CI -# The pull-request gate: the same recipes a developer runs, split into the -# static checks, the smoke/virtualizer rigs, the conformance matrix, and -# the cross-implementation interop legs. The job bodies live in the -# justfiles; the workflow only provisions the environment. The timing lab -# is deliberately not here — it is statistical and schedule-only -# (timing-lab.yml); `just bench` is non-gating by design. +# The pull-request gate: each job runs exactly one `gha::` job recipe, so +# `just ci` is exactly CI; the workflow only provisions the environment. +# The timing lab is deliberately not here — it is statistical and +# schedule-only (timing-lab.yml); `just bench` is non-gating by design. on: push: branches: [main] @@ -16,10 +14,9 @@ permissions: contents: read jobs: - # `just check` (fmt, clippy all-features, workspace tests, wasm build) - # plus `just audit` (no table-based AES reachable in the release wasm - # artifact — the audit needs the release build, so it shares this job's - # cache). + # `just gha::rust-checks`: fmt, clippy, tests, wasm build, plus the + # AES-table audit, which needs the release build and so shares this + # job's cache. rust-checks: runs-on: ubuntu-latest # Headroom for a cold cargo cache: clippy all-features compiles the @@ -44,12 +41,11 @@ jobs: tool: just env: GITHUB_TOKEN: ${{ github.token }} - - run: just check - - run: just audit + - run: just gha::rust-checks - # The QUIC loopback rig under Wasmtime (`just smoke-quic`) and both - # tls-virt deliveries against openssl over real TCP - # (`just smoke-tls-virt`). openssl and python3 are on the runner image. + # `just gha::smoke`: the QUIC loopback rig under Wasmtime and both + # tls-virt deliveries against openssl over real TCP. openssl and + # python3 are on the runner image. smoke: runs-on: ubuntu-latest # Headroom for a cold cargo cache (the tls-virt-wasmtime leg builds the @@ -75,14 +71,13 @@ jobs: tool: just,wasmtime,wasm-tools,wac-cli env: GITHUB_TOKEN: ${{ github.token }} - - run: just smoke-quic - - run: just smoke-tls-virt + - run: just gha::smoke - # The cross-implementation conformance suite (`just conformance`): the - # shared guest suite composed with each delivery, run under the pinned - # component-test runner and validated against the committed inventory, - # manifest, and matrix. The aggregate action republishes the matrix to - # the job summary; the just gate has already gated. + # `just gha::conformance-checks`: the shared guest suite composed with + # each delivery, run under the pinned component-test runner and + # validated against the committed inventory, manifest, and matrix. The + # aggregate action republishes the matrix to the job summary; the just + # gate has already gated. conformance: runs-on: ubuntu-latest # Headroom for a cold cargo cache plus the ct-tools install. @@ -120,7 +115,7 @@ jobs: tool: just,wasm-tools,wac-cli env: GITHUB_TOKEN: ${{ github.token }} - - run: just conformance + - run: just gha::conformance-checks - name: Publish the conformance matrix if: always() && hashFiles('conformance/driver-ct/results/*.jsonl') != '' uses: polymorph-components/polymorph-test/actions/aggregate@2436bb9fd233ec514f10de613556a7a61bbb8476 @@ -148,10 +143,10 @@ jobs: conformance/driver-ct/results/matrix.md retention-days: 30 - # Cross-implementation interop over real transports (`just interop`): - # the composed component against OpenSSL and Go crypto/tls peers over - # TCP, and the noq leg against quic-go over UDP, both directions, - # under a fresh Ed25519 private PKI per run. + # `just gha::interop`: cross-implementation interop over real + # transports — the composed component against OpenSSL and Go crypto/tls + # peers over TCP, and the noq leg against quic-go over UDP, both + # directions, under a fresh Ed25519 private PKI per run. interop: runs-on: ubuntu-latest # Cap so a hung handshake or stuck peer fails fast. @@ -180,4 +175,4 @@ jobs: tool: just,wasmtime,wasm-tools,wac-cli env: GITHUB_TOKEN: ${{ github.token }} - - run: just interop + - run: just gha::interop diff --git a/.github/workflows/timing-lab.yml b/.github/workflows/timing-lab.yml index 741a38f..4c9a1fd 100644 --- a/.github/workflows/timing-lab.yml +++ b/.github/workflows/timing-lab.yml @@ -57,6 +57,6 @@ jobs: tool: just,wasmtime - name: Run the timing lab - run: just timing-lab-scheduled + run: just gha::timing-lab-scheduled env: TIMING_LAB_SAMPLES: ${{ inputs.samples || '2000' }} diff --git a/AGENTS.md b/AGENTS.md index 20f300d..761d659 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -76,6 +76,7 @@ design record. | Recipe | Verifies | | --- | --- | | `just check` | fmt, clippy (all features), workspace tests (RFC 9001 vectors, profile/provider pinning, class-D key rejection), wasm build | +| `just ci` | every gating CI job's body, exactly as CI runs it — each CI job runs one gha:: job recipe (.github/justfile). The timing lab is schedule-only and excluded | | `just conformance` | the cross-implementation conformance suite (see `conformance/README.md`): the shared guest suite composed with each delivery — the `tls` world's in-guest Ed25519 posture and the `tls-delegated` world with the fixture signer — run under the pinned component-test runner, transpiled under Node 24 JSPI (the jco-node targets), and in headless Chromium (the jco-browser targets; CI or CONFORMANCE_BROWSER=1), with import-satisfaction and signer-reachability gates, validated against the committed case inventory (`tests.lock`) and target manifest, and diffed against the committed matrix. `just conformance-ct::run-webcrypto` (on demand: clones the sibling repo) adds the delegated posture over a real `polymorph:webcrypto` provider | | `just smoke-quic` | QUIC over `wasi:sockets` UDP under Wasmtime | | `just smoke-tls-virt` | both tls-virt deliveries against `openssl s_server` over real TCP (needs openssl + python3): the composed guest virtualizer (handle-address and import-satisfaction gates), and the wasmtime host provider on both sockets generations — wasip3 and `std::net`/0.2 guests — with handle-address and profile-cipher-suite gates plus plain-TCP passthrough-delegation legs | diff --git a/justfile b/justfile index ae1c245..3c7896c 100644 --- a/justfile +++ b/justfile @@ -1,7 +1,21 @@ -# Run all checks. -check: fmt-check clippy test build-wasm +# The orchestration surface: repo-wide recipes plus the GitHub Actions +# gha module, colocated with the workflows it drives. mod conformance-ct "conformance/driver-ct/justfile" +# GitHub Actions plumbing: CI job entry points and workflow-only recipes. +mod gha ".github" + +# List the available recipes. +default: + @just --list + +# The exact set of checks CI runs: each CI job runs exactly one gha:: job +# recipe. The timing lab is schedule-only (timing-lab.yml) and not part +# of ci. +ci: (gha::rust-checks) (gha::smoke) (gha::conformance-checks) (gha::interop) + +# The fast pre-commit checks. +check: fmt-check clippy test build-wasm fmt: cargo fmt --all @@ -83,49 +97,3 @@ timing-lab: [ -n "${TIMING_LAB_SEED:-}" ] && args+=(--env TIMING_LAB_SEED) [ -n "${TIMING_LAB_ISOLATE:-}" ] && args+=(--env TIMING_LAB_ISOLATE) wasmtime run "${args[@]}" target/wasm32-wasip2/release/timing-lab.wasm - -# The timing lab's scheduled wrapper: one retry at 4x samples before -# reporting failure — a flake washes out at 4x while a real leak's t grows, -# so the retry separates them; shared runners make the retry mandatory -# rather than optional. Records the environment the verdicts are valid for -# (verdicts hold per runtime version and per microarchitecture; a wasmtime -# upgrade or a runner change invalidates previous quiet readings). Under -# GitHub Actions the report also lands in the job summary. -timing-lab-scheduled: - #!/usr/bin/env bash - set -uo pipefail - samples="${TIMING_LAB_SAMPLES:-2000}" - cpu=$(sed -n 's/^model name[^:]*: //p' /proc/cpuinfo | head -n1) - [ -n "$cpu" ] || cpu="$(sed -n 's/^CPU implementer[^:]*: /implementer /p' /proc/cpuinfo | head -n1)" - environment="$(wasmtime --version), $(uname -m), ${cpu:-unknown CPU}" - echo "timing lab environment: ${environment}" - run() { TIMING_LAB_SAMPLES="$1" just timing-lab 2>&1; } - - report=$(run "$samples"); status=$? - printf '%s\n' "$report" - if [ $status -ne 0 ]; then - samples=$(( samples * 4 )) - echo - echo "timing lab: verdicts diverged; retrying at ${samples} samples/class before reporting failure." - report=$(run "$samples"); status=$? - printf '%s\n' "$report" - fi - - if [ -n "${GITHUB_STEP_SUMMARY:-}" ]; then - { - echo "### timing lab — ${samples} samples/class" - echo - echo "Environment: ${environment}" - echo - # The lab prints its report as a markdown table; lift it verbatim. - printf '%s\n' "$report" | sed -n '/^| surface/,/^$/p' - if [ $status -eq 0 ]; then - echo "All surfaces matched expectations." - else - echo "**Surfaces diverged from expectation, and again on a retry at ${samples} samples/class.**" - echo "A quiet positive control means the harness cannot detect leaks at this" - echo "measurement distance; a LEAK on a real surface warrants investigation." - fi - } >> "$GITHUB_STEP_SUMMARY" - fi - exit $status