// facett.runa.ron — facett's release chain, authored in the typed `runa` format.
//
// WHAT THIS REPLACES. The chain below was five verbs a human typed in order, from
// memory, from a runbook: `wrap-wasm` → `publish-pip` → `publish-holger` →
// `verify-install` → `test-demo`. Every one of them is real and none of them knew
// about the others. Run out of order they fail in confusing ways (a wheel built
// before the bundle stages files nobody wrote); run halfway, they leave an index
// serving a wheel nothing has proved installable. The order was knowledge, and
// knowledge in a person's head is not a pipeline.
//
// WHAT IT IS NOT. It does not re-implement any of those verbs. Each stage below
// either names a build-thing VARIANT that facett declares (`Build`) or calls the
// xtask verb that already exists (`Xtask`) — the forge resolves the first against
// `.nornir/build-variants.toml` and drives `nornir-build-thing::build_variant`
// in-process; the second is the same binary a human runs. One implementation, two
// doors, which is the same rule `cargo xtask build` follows.
//
// `alias: "ops"` (2026-10-01): publish-holger, verify-install and test-demo build
// nothing, so they left xtask (THE LAW: xtask carries ONE dependency, the recipe) for
// `facett-ops`, reached as `cargo ops <verb>`. The step names that alias and the forge
// runs `cargo ops <cmd>` (dwarves-runa 0.2.4 `Step::Xtask::alias`, dwarves runa_exec).
//
// HELD TRUE BY A TEST. `xtask/tests/the_pipeline_names_things_that_exist.rs` parses
// this file and checks every `Build(variant:)` against facett's declaration and
// every `Xtask(cmd:, alias:)` against the verb list of the dispatcher that alias runs. A pipeline naming a
// variant nobody declares, or a verb nobody has, is a CI file that goes red on the
// forge and green on the desk.
//
// To execute against this checkout:
// DWARVES_REPO_ROOT=/home/rickard/git/facett dwarves run facett.runa.ron
Pipeline(
name: "facett-release",
// On a push to main, and always startable by hand — the release chain is also
// the thing you run when you want to know whether the release chain still works.
triggers: [
Push(branch: "main"),
Manual,
],
stages: [
// ── bundle: the REAL components to wasm32, then wasm-bindgen and the
// component-manifest capture. A declared variant, so the forge runs the
// typed stages of the `[[build.wasm]]` cell rather than a remembered verb.
Stage(
name: "bundle",
steps: [
Build(variant: "facet-wrapped-wasm"),
],
),
// ── wheel: `python -m build` inside the pinned `nordisk-pip-toolchain`, with
// the network off — the two PyPI releases: `facett-wheel` (PyPI `facett`,
// over the files the stage above produced) and `facett-demo-wheel` (PyPI
// `facett-demo`, pure Python). `needs` keeps the bundle first.
Stage(
name: "wheel",
needs: ["bundle"],
steps: [
Build(variant: "facett-wheel"),
Build(variant: "facett-demo-wheel"),
],
),
// ── publish: pack the release set into holger's PEP 503 archive. Not a
// build-thing kind — packing an index is a publish act — so it is the
// xtask verb that already does it, re-reading the wheel it publishes.
Stage(
name: "publish",
needs: ["wheel"],
steps: [
Xtask(cmd: "publish-holger", alias: "ops"),
],
),
// ── prove: a real `pip install` from that index, in a container that has
// never seen this repo, judged in Rust against the wheel that was built.
// This is the stage that turns "the archive is correct" into "a client
// resolved it", and it is why the wheel is never baked into an image.
Stage(
name: "prove",
needs: ["publish"],
steps: [
Xtask(cmd: "verify-install", alias: "ops"),
],
),
// ── demo: the Streamlit app's own suite, in its own container, against the
// INSTALLED wheels (facett + facett-demo from the built wheelhouse; the
// tests are the only thing mounted from the repo, plus the two sibling
// wire files). The live panes skip with `korp-server not reachable` when
// no server is up; the gallery and the offline (no-korp) mode need none.
Stage(
name: "demo",
needs: ["prove"],
steps: [
Xtask(cmd: "test-demo", alias: "ops"),
],
),
],
)