flowchart LR
push["push / PR"] --> chk["flake-check workflow<br/>nix flake check --all-systems --no-build<br/>+ image closure dry-run"]
tag["push tag v*"] --> rel["release-images workflow (ubuntu)"]
rel --> build["nix build .#image-<svc>"]
build --> pushghcr["docker load + tag + push<br/>ghcr.io/shsingh/opensase-<svc><br/>:latest + :version"]
pushghcr --> sbom["SPDX SBOM per image (anchore)"]
CI & Release
Everything released is built by Nix. There is no docker build anywhere in the repo’s release path, so nothing floats: CI and your machine produce byte-identical images from the same lockfile.
Workflows
| Workflow | Trigger | What it does |
|---|---|---|
.github/workflows/flake-check.yml |
PRs, pushes to master | nix flake check --all-systems --no-build + --dry-run builds of all image closures |
.github/workflows/release-images.yml |
tags v*, manual |
matrix-builds the four images on Linux runners, publishes to GHCR, SBOMs each |
Images
Built with pkgs.dockerTools.buildLayeredImage — see nix/images.nix. Linux-only outputs (the services are linux-first); darwin systems skip image outputs.
nix run .#load-images # build all four locally + docker load (linux host)Publishing a release
Releases are GPG-signed tags — the workflow verifies the tag (git tag -v) and fails before publishing anything unsigned:
git tag -s v0.1.0 -m "OpenSASE v0.1.0: initial Nix-built container release"
git push origin v0.1.0The workflow matrix-builds the four images, pushes to GHCR (:latest + the version), and opens a draft release whose notes are generated on the runner:
- digest table for all four images
- commit changelog since the previous tag
- SPDX SBOMs attached as release assets
- 60-second compose deploy snippet
Review and publish the draft to ship. Consumers need nothing but an OCI runtime.
Docs site
.github/workflows/pages.yml renders this site with Quarto and deploys it to GitHub Pages on pushes to master touching _quarto.yml, docs/**, index.qmd, or the README.