Gunnar

nordisk/facett

facett.runa.ron

// 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"),
            ],
        ),
    ],
)