Define pipelines as code
Write workflows in TypeScript, compile them to a committed artifact.
Written By Dustin
Last updated About 3 hours ago
TOVIO has two co-equal pipeline front-ends: declarative .github/workflows/*.yml, and a config-as-code program using the @tovio/pipeline builder. Both lower to the same job DAG, the same runtime, and the same UI. YAML is permanent and is never degraded; config-as-code is an addition, for when you want types, reuse, loops, and shared constants.
A pipeline program
import { pipeline, run, uses, emit } from "tovio-node-sdk/pipeline";const checkout = uses("actions/checkout@v4");const ci = pipeline("CI") .on("pull_request", "push") .job("lint", { steps: [checkout, run("cargo clippy --all-targets -- -D warnings")], }) .job("test", { needs: ["lint"], matrix: { node: ["20", "22"] }, steps: [checkout, run("cargo test --workspace")], });emit(ci);Trigger names are GitHub Actions event names — pull_request, push, schedule, workflow_dispatch — which TOVIO maps onto its own change events. Matrix axis values must be strings: a bare JavaScript number like 3.10 silently becomes "3.1", so quote your versions.
Compile it
tovio ci compile pipelines/ci.pipeline.mjsThat runs your program under Node, parses the emitted workflow JSON into the same structural model YAML parses to, validates the DAG, content-addresses it with BLAKE3, and writes a *.pipeline.lock.json lockfile. Your program runs only on your machine — the Forge never executes pipeline code, which is why config-as-code needs no server-side sandbox at all.
Two artifacts, one of which actually runs
.github/ is dot-ignored by default, so --fix-ignore adds !.github/ to .tovioignore and the generated workflow becomes tracked. A hand-authored workflow at the target path is never clobbered without --force.
The staleness gate
tovio ci compile pipelines/ci.pipeline.mjs --emit both --checkRecompiles and byte-compares, writing nothing. A stale or missing artifact is a non-zero exit with TVO-CI-001. Because the content address is a deterministic function of the compiled model, this is a real guard, not a heuristic. Run it as a required check from a hand-authored workflow — hand-authored so it can gate the generated workflows without gating itself.
Stricter than YAML on purpose
An empty or duplicate job id, a needs pointing at a job that does not exist, a cycle, a matrix with no axes, an axis with no values — all fail loudly at compile time. YAML would strand or silently drop the job at run time. Every one of these surfaces as TVO-CI-001 with a specific remediation.
What is deferred
Config-as-code produces a static DAG: the full node set is known at compile time. Tenant-authored code that runs on the platform and grows the DAG dynamically is not available. If you need a runtime-dynamic DAG today, this is not it.