Build from source¶
The CLI¶
You'll need Go 1.27.4 or newer to build nsl. To run the checks, install the golangci-lint version pinned in mise.toml. mise ↗ installs it and verifies it against mise.lock. It also installs the pinned Node, which make verify needs for the template gate.
mise install # the pinned golangci-lint, svu and Node
make build # build/nsl
make check # format, then verify
make verify # tidiness, license notices, vet, formatting, lint, the template gate and tests; changes nothing
make ci # what CI runs: verify, race-enabled tests with coverage, cross builds
You can run the unit tests without root or a VM. They use fake tools and local processes.
Releases and the Homebrew tap¶
make bump tags the next stable version from a clean, verified main.
The release workflow uses GoReleaser Pro to publish the Linux archives and
generate Casks/nsl.rb for
frostyard/homebrew-tap ↗.
GoReleaser commits the cask to a bump/nsl-VERSION branch of the tap and opens
a pull request into the tap's main; the tap serves the new release once a
maintainer merges it. GoReleaser only warns when GitHub refuses the pull request,
so check the tap for it after each release. Snapshots and prerelease tags do not
update the stable cask.
Both workflows pin GoReleaser Pro to one exact version. The
release-config job renders the cask in snapshot mode, and
scripts/check-cask.py refuses a cask that Homebrew's
Cask/ZapNotRequired cop refuses. Bump the exact pin in both
workflows together.
The workflow needs the GORELEASER_KEY and HOMEBREW_TAP_TOKEN secrets.
HOMEBREW_TAP_TOKEN is a fine-grained token for the tap only, with Contents and
Pull requests write access; the workflow's GITHUB_TOKEN only publishes this
repository's release and provenance. Missing tap credentials fail the workflow
before publication.
Archive names carry no version, such as nsl_linux_amd64.tar.gz, because the
install guide downloads
them through releases/latest/download/. Keep them stable.
Run make release-check with GoReleaser Pro when editing .goreleaser.yaml.
The release-config CI job runs the same Pro configuration check.
A Dependabot or fork pull request does not get GORELEASER_KEY, so the job
skips the check there with a notice; the merge-group and main runs check it.
See ADR-0020 ↗
for the publication decision.
The images¶
The image builder runs mkosi inside a disposable Lima VM using pinned nspawn/mkosi-definitions ↗ recipes. Build dependencies stay in that VM. On the host, you'll need Lima 2.2.0, Python 3, Git, Go and flock.
make build
./scripts/bootstrap-poc.sh --waypipe # optional: pinned Lima and Waypipe under build/poc
source build/poc/env.sh
# The VM image.
scripts/build-image.sh --role vm
image=build/image/share/nsl-vm-trixie-x86-64-r11.raw
build/nsl update --image "$image" --digest "sha256:$(sha256sum "$image" | cut -d' ' -f1)"
build/nsl recover
# A machine image, and a machine from it.
scripts/build-image.sh --role machine --distribution debian
machine=build/image/share/nsl-machine-debian-trixie-x86-64-r6.tar.zst
build/nsl create debian --image "$machine" --digest "sha256:$(sha256sum "$machine" | cut -d' ' -f1)"
Each build prints a Built PATH line. Use that path for image or machine if the profile revision differs from these examples.
--distribution also takes ubuntu, fedora, centos, arch, opensuse and azure; --distribution opensuse --release 16.0 builds Leap, and --release also picks Debian testing, Fedora rawhide and Ubuntu noble. The image build notes ↗ describe the layers and the builder.
Accept images in disposable VMs¶
python3 scripts/probe-vm.py --nsl build/nsl --image "$image" --evidence build/image/evidence/probe.json
python3 scripts/probe-machines.py --nsl build/nsl --vm-image "$image" --machine-image "$machine" \
--evidence build/image/evidence/machines.json
The probes boot VMs in private state directories and remove them afterwards.
This site¶
The site lives in site/: pages in site/content/, configuration in site/properdocs.yml. It is built with ProperDocs ↗ and the MaterialX ↗ theme, and published to GitHub Pages from main.
make site-serve # preview at http://127.0.0.1:8000/nsl/
make site # build into site/dist, failing on any warning
Both targets install the pinned tools into build/site-venv first. When you change how nsl behaves, update the relevant pages in the same pull request.
Design documents¶
Decisions, designs, contracts and plans live in docs/ ↗. Start at the documentation index ↗.