Gunnar

nordisk/dwarves

dwarves.runa.ron

// dwarves.runa.ron — the FORGE's OWN CI/CD pipeline, authored in the typed `runa`
// format. Dogfood: the constellation's CI/CD engine describes its own build as DATA.
//
// This is the file the dwarves UI **Pipeline** tab renders by default: it is loaded
// through `dwarves_runa::Pipeline::from_ron_str` (which also runs runa's validator — duplicate
// stage / unknown-`needs` / DAG-cycle check), mapped onto facett's engine-agnostic
// `PipelineModel` by `facett_pipeline::RunaPipelineAdapter`, and lit with LIVE per-stage
// status read back from the run history the forge historises (jera-dispatched steps).
// One viewer, many adapters — the SAME `facett-pipeline` DAG viewer knut's Spark
// pipelines and the brokkr build DAG render on.
//
// It is also executable, exactly as authored:
//   dwarves run dwarves.runa.ron
// (`DWARVES_REPO_ROOT=/path/to/dwarves` to point it at another checkout; else the repo
// root defaults to this file's directory). Execution is jera jobs — every step of a
// stage is dispatched concurrently through the shared Scheduler (`crate::runa_exec`),
// and a `Build{variant}` drives `nornir-build-thing::build_variant` IN-PROCESS (a real
// `cargo build`, zero shell) against the variant set dwarves declares in
// `.nornir/build-variants.toml`.
//
// ⛔ SAFETY INVARIANT — a runa pipeline NEVER `--execute`s / publishes to crates.io.
//    The forge's in-process release backend hard-codes `execute: false`, so the
//    `ReleaseDoctor` step below computes the deps-first publish waves and prints the
//    cascade but uploads NOTHING. The real, irreversible publish stays the human-gated
//    `dwarves workspace-release --execute` path, off this file.
Pipeline(
    name: "dwarves-forge",
    // Built on every push to main, and always startable by hand.
    triggers: [
        Push(branch: "main"),
        Manual,
    ],
    stages: [
        // ── lint: the format + clippy gate, both typed steps — `Lint(tool: Fmt)` is
        //    `cargo fmt --check`, clippy runs with `-D warnings`. (This used to be
        //    `Xtask(cmd: "fmt-check")`, a verb the xtask never had: MEASURED on dwarf
        //    2026-10-16, `xtask: unknown verb fmt-check`.)
        Stage(
            name: "lint",
            steps: [
                Lint(tool: Fmt),
                Lint(tool: Clippy, deny_warnings: true),
            ],
        ),
        // ── build: forge EVERY declared deployment variant via build-thing in-process.
        //    `"*"` is the whole-set matrix — it fans out over dwarves' five declared
        //    variants (dwarves · dwarves-thin · dwarves-server · dwarves-ui ·
        //    dwarves-default) as N concurrent jera jobs, one per variant. The
        //    `dwarves-default` arm is the task-#89 regression guard: the LEAN default
        //    feature surface no other variant builds.
        Stage(
            name: "build",
            needs: ["lint"],
            steps: [
                Build(variant: "*"),
            ],
        ),
        // ── test: the fast test matrix, gated behind a green build.
        Stage(
            name: "test",
            needs: ["build"],
            steps: [
                Nextest(all_features: true),
            ],
        ),
        // ── ui: the forge's OWN egui shell, robot-driven headless (L4 — every UI
        //    surface is robot-tested). Drives the `dwarves-ui` member's recorded per-tab
        //    scenario library over the robot seam and gates on the coverage board having
        //    no gap.
        Stage(
            name: "ui",
            needs: ["build"],
            steps: [
                RobotDriveMember(
                    member: "dwarves-ui",
                    egui_scenarios: ["Warehouse", "Forge", "Pipeline", "Release"],
                ),
                CoverageGate(workspace: "dwarves"),
            ],
        ),
        // ── doctor: kick off the release engine in DRY-RUN. `ReleaseDoctor` drives the
        //    in-process dwarves release engine — discover members, plan the deps-first
        //    publish waves, print the cascade. Nothing is uploaded; this is the
        //    pre-publish gate matrix as a pipeline step.
        Stage(
            name: "doctor",
            needs: ["test", "ui"],
            steps: [
                ReleaseDoctor,
            ],
        ),
    ],
)