Run TOVIO in CI

The CI bridge, and the shape of a pipeline that uses it.

Written By Dustin

Last updated 27 minutes ago

The CI bridge (@tovio/ci-bridge) is a zero-dependency Node wrapper around the TOVIO CLI for GitHub Actions and Spacelift. It owns no storage, sync, Git translation, authentication, policy, or cryptography — it validates a small operation contract and starts tovio with an exact argument vector.

A closed operation set

checkout, sync, git-bridge, health, and status. That is the whole surface. There is no arbitrary-command input, and the child is started with no shell, so a workflow value can never be assembled into a command line.

Two actions, not one

Installing a verified release and running an operation are deliberately separate actions. The operation wrapper never downloads a binary; a sibling setup action does.

- name: Install a verified TOVIO release  id: tovio  uses: Tovio-VCS/tovio/packages/tovio-ci-bridge/setup@<full-commit-sha>  with:    version: 1.0.0  env:    GH_TOKEN: ${{ github.token }}- name: Checkout  uses: Tovio-VCS/tovio/packages/tovio-ci-bridge@<same-full-commit-sha>  with:    operation: checkout    tovio-path: ${{ steps.tovio.outputs.tovio-path }}    remote: https://forge.example.test    repository-id: <64 hex chars>    destination: ${{ github.workspace }}

Pin both to the same full immutable commit SHA. The version is an exact semantic version — never latest and never a mutable channel.

What "verified" means

The setup action bounds both HTTPS downloads, verifies the archive against its .sha256 sidecar, requires gh attestation verify provenance naming the repository's exact release workflow and the matching immutable refs/tags/v<version>, rejects self-hosted attestation signers, accepts only the release packager's exact regular-file member set, extracts without a shell, smokes both binaries, and removes the temporary install in an always-run post step. Any of those failing is TVO-CI-009, and no unverified binary path is ever exposed to a later step. Releases additionally publish a signed SPDX SBOM attestation per artifact.

Bounds you get for free

Every invocation has an explicit timeout — 15 minutes by default, 1 second to 60 minutes allowed — and expiry terminates the exact child process tree rather than leaving the wrapper waiting. While running it emits a heartbeat at least every 30 seconds that contains no arguments, remote URLs, paths, or inherited environment values.

Partial checkouts

For a large repository, sparse, blobless, blob-limit, depth, and no-attachments are exposed as inputs and the wrapper enforces the CLI's mutual-exclusion rules before starting anything. A sparse or blobless checkout is usually much faster and enough for CI.

Gating on the result

health is a diagnostic, not a gate. It reports whether the lane is clean, landable, and how stale — and it exits 0 even when the lane tip carries unresolved conflicts, because a conflict is data rather than an error. Do not wire it up as a pass/fail step and assume it will stop anything.

The gate is tovio build-check: exit 0 if and only if the lane tip has no unresolved conflict objects, non-zero listing the offending paths. It does not compile or test anything — run your own build in its own step.

build-check is not one of the bridge's five operations, so call the installed binary directly:

- name: Conflict-free gate  working-directory: ${{ github.workspace }}  run: ${{ steps.tovio.outputs.tovio-path }} build-check

tovio fsck is the other useful CI gate: it re-hashes every object and verifies references and the audit chain. Add --strict to promote suspected key-material warnings to failures.

Spacelift

The same executable runs from a bounded lifecycle hook:

before_init:  - tovio-ci-bridge sync  - tovio-ci-bridge health

A source-built multi-architecture runner image is published per release tag. Pin its immutable digest rather than a mutable tag. The image contains no worker key, repository session, or cloud credential — your secret store mounts those at runtime.

Status

The bridge is built and is the v1 CI story: you bring your own runner. It is not declared production-complete until signed-binary installation, clean hosted-runner and private-worker exercises, marketplace and registry publication, live OIDC-scoped ephemeral enrollment, revocation and protected-path tests, and an independent supply-chain review are all recorded as evidence. Hosted agent and CI end-to-end remains an open gate.