Skip to content

CI and Releases

github-actions[bot] edited this page Oct 7, 2026 · 5 revisions

CI and releases

CI is .github/workflows/ci.yml. It runs the same commands as the devenv scripts, written out directly, because running jobs through devenv shell was too slow. Keep the two in step by hand.

Nix is installed by .github/actions/setup-nix, which writes substituters to /etc/nix/nix.conf before the daemon starts. (NIX_CONFIG is ignored for untrusted clients, and the build silently compiles from source.)

Jobs

  • clippy and rustfmt (rustfmt checked on pull requests only)
  • Rust tests for both crates
  • flake evaluation
  • package builds
  • installer ISO: built on every push, then booted under OVMF and SeaBIOS by tests/iso-boot.py. A boot passes when the installer sends a DNS query for github.com. On timeout, the job uploads a screenshot.
  • the Nextcloud image: weekly and on request only
  • losos-registrar for the dev machine: a static musl build pushed to GHCR on every push to main and every tag, served by the proxy (below)

Releases

Push a v* tag, or draft a release with a new v* tag in the web UI. The release job:

  1. builds the installer ISO and the demo QCOW2,
  2. attaches the ISO and its .sha256 to the GitHub release,
  3. pushes both to GHCR (the QCOW2 is over GitHub's 2 GiB asset limit).

Your release notes are kept. The download section goes between two marker comments, and a re-run replaces only that section.

Binary cache

CI publishes signed Nix store paths as OCI artifacts at ghcr.io/dasmatus/losos/nix-cache. They are served by a separate deployment of the LosOS Desktop proxy with GHCR_REPOSITORY=dasmatus/losos.

Setup:

  1. nix key generate-secret --key-name losos-1
  2. Store the secret key as repository secret NIX_CACHE_SIGNING_KEY.
  3. Store the public key (nix key convert-secret-to-public) as repository variable NIX_CACHE_PUBLIC_KEY.
  4. Store the proxy's HTTPS URL as repository variable LOSOS_PROXY_URL. That is the domain attached to the losos-cache-proxy Vercel project, https://proxy.losos.dasmat.us, and nothing else (the earlier name, losos-proxy.dasmat.us, now 307-redirects there): a hostname that merely resolves to Vercel answers DEPLOYMENT_NOT_FOUND, nix prints warning: '<url>' does not appear to be a binary cache in every job and builds from source, and the run stays green. setup-nix now checks <url>/nix-cache-info and annotates the run when that happens.
  5. Make the losos/nix-cache package public, and losos/images (below) once the first push to main has created it. GitHub creates a package private; the proxy then answers 502 token: 403 for everything in it.
  6. Keep losos.cache.substituters and losos.cache.trustedPublicKeys in modules/options.nix at the same URL and key. Their defaults are https://proxy.losos.dasmat.us and the losos-1 public key, so a stock appliance and installer medium already pull from the cache; tests/invariants.nix fails if either default is dropped. Rotating the signing key means changing the variable and the default together.

Every job reports what it actually got from the cache. setup-nix puts a nix shim on PATH that counts nix's own copying path '…' from '<url>' and building '…' lines per invocation and writes a Nix binary cache use table to the job's step summary (one row per nix command that copied or built anything: paths from the LosOS cache, from cache.nixos.org, from elsewhere, and built on the runner), plus one LosOS cache: … line in the job log right after the command. A row with the LosOS column at 0 and a non-zero "built here" means this project's own paths were compiled although an earlier run should have pushed them; a job that only evaluates adds no row. The shim changes neither nix's output nor its exit status.

Check <proxy-url>/nix-cache-info and nix copy --from <proxy-url> <store-path> before relying on it. An unreachable cache is not fatal: the appliance waits 5 s and falls back to cache.nixos.org and building. Never put the secret key in the flake or on an appliance.

The dev-machine tool

The key ceremony (Master Proxy → Official edges) runs losos-registrar provision on the operator's own computer, which has no Nix store, so .#losos-registrar-static is the same crate linked statically against musl. The registrar-and-ui job builds it and the publish-tool job pushes it to GHCR as the OCI artifact ghcr.io/dasmatus/losos/images:<channel>-x86_64, one layer per file: losos-registrar and SHA256SUMS. That is the shape the proxy's /updates/<channel>/<arch>/<file> route serves: it picks the layer whose title is the file name and redirects to GHCR's storage (SHA256SUMS it serves inline), the same route LosOS Desktop's sysupdate reads. The channel is main for a push to main, the tag for a release (which also moves stable), and a cleaned-up branch name for a manual run elsewhere; pull requests never publish.

curl -fsSLO "https://proxy.losos.dasmat.us/updates/main/x86_64/{losos-registrar,SHA256SUMS}" && sha256sum -c --ignore-missing SHA256SUMS && chmod +x losos-registrar

The job's step summary carries the artifact reference, the URL and the sum. oras pull ghcr.io/dasmatus/losos/images:main-x86_64 fetches the same files without the proxy.

Clone this wiki locally