Gunnar

nordisk/Skidbladnir

brokkr.toml

# Skidbladnir CI/build pipeline — driven by the dwarves forge (brokkr descriptor +
# sindri scheduler). `dwarves validate` / `dwarves plan` / `dwarves run [--real]`.
#
# WHY THIS FILE EXISTS (MEASURED 2026-08-25): Skidbladnir had none. Across the whole
# constellation — 38 repos under ~/git — `nornir/brokkr.toml` was the ONLY pipeline
# declaration, so the forge had no job for this repo at all and nothing was built or
# gated except by a human at a terminal. The cost was paid the same day:
# `vmsuite::judge_active` graded systemd's transitional `deactivating` as **stopped**
# (fixed in `4be0017`), and the 23 pure judge tests that would have caught it live
# behind `--features matrix`, which nothing declared a run of.
#
# Every `run = { variant = … }` below names a row in `.nornir/build-variants.toml`;
# `tests/gated_runs_are_declared_guard.rs` fails this repo's own `cargo test` if a
# feature that gates a test is enabled by no variant, or by a variant no step here
# names. The two files cannot drift apart silently any more.
#
# What a `variant` step IS: `nornir_build_thing::build_variant` — a real cargo build
# under `--real` (`dwarves/crates/dwarves/src/runner.rs:190`). It is NOT a test run;
# `RunKind::Container`/`RunKind::Vm` fall through to a fallback runner that is
# unwired today, so declaring a `cargo test` container step here would be a
# declaration the forge cannot honour. The gated rows are therefore compiled, not
# executed — which kills bit-rot in every dark surface and is honestly less than
# running them.

[pipeline]
name = "skidbladnir-ci"

[[pipeline.trigger]]              # fire when the poll sweep sees HEAD move
kind = "poll"
events = ["push"]

[[pipeline.trigger]]              # …and nightly at 03:20 (off nornir's 03:00)
kind = "cron"
cron = "20 3 * * *"

# ── the shipping artifacts ───────────────────────────────────────────────────
[[pipeline.stage]]
name = "build"

[[pipeline.stage.step]]
name = "cli"
run = { variant = "skidbladnir" }        # the lean offline core (default `pki`)

[[pipeline.stage.step]]
name = "cli-znippy"
needs = ["cli"]
run = { variant = "skidbladnir-znippy" } # the real znippy archive engine linked in

[[pipeline.stage.step]]
name = "probe"
needs = ["cli"]
run = { variant = "skidbladnir-probe" }

[[pipeline.stage.step]]
name = "testsvc"
needs = ["cli"]
run = { variant = "skidbladnir-testsvc" }

# ── the surfaces that only exist behind a feature gate ───────────────────────
# One step per dependency stack, so a red names the stack that broke. Each needs
# `cli` only — they are independent of one another and of each other's failures.
[[pipeline.stage]]
name = "gated-surfaces"

[[pipeline.stage.step]]
name = "matrix"                          # KVM install matrix + vm-systemd suite + korpprov
needs = ["cli"]
run = { variant = "skidbladnir-matrix" }

[[pipeline.stage.step]]
name = "promote"
needs = ["cli"]
run = { variant = "skidbladnir-promote" }

[[pipeline.stage.step]]
name = "service-drive"
needs = ["cli"]
run = { variant = "skidbladnir-service-drive" }

[[pipeline.stage.step]]
name = "connected"                       # reqwest: vault + openbao + download
needs = ["cli"]
run = { variant = "skidbladnir-connected" }

[[pipeline.stage.step]]
name = "federation"                      # ureq: the harbour clients
needs = ["cli"]
run = { variant = "skidbladnir-federation" }

[[pipeline.stage.step]]
name = "cluster"                         # kube + talos apid
needs = ["cli"]
run = { variant = "skidbladnir-cluster" }

[[pipeline.stage.step]]
name = "sockets"
needs = ["cli"]
run = { variant = "skidbladnir-sockets" }