milestone 14: build, package, sign and publish releases

This commit is contained in:
2026-08-08 12:38:29 +02:00
parent 6c507992e4
commit cdacc560b7
48 changed files with 7272 additions and 543 deletions
+164 -44
View File
@@ -4,58 +4,105 @@ Installs nxdns as a system service on a Linux host with systemd, including a
Raspberry Pi 5. At the end the service answers DNS on port 53 and starts on
boot.
The normal path is to download a released tarball, verify it, and install what
is inside it. Building from source is still supported and is the last section
of this page.
For what each flag does, see [the CLI reference](../reference/cli.md); for what
each configuration field means, see
[the configuration reference](../reference/configuration.md).
> Verification: the build steps and `systemd-analyze verify` were run on the
> machine that wrote this page. `nxdns check` and `nxdns run` were run there
> too, but against a scratch `--data-dir` and `--config` on an unprivileged
> port, because that machine is not a deploy target and has no `/etc/nxdns`,
> no `/var/lib/nxdns` and no root. The steps that need root on a target host —
> `install`, `systemd-sysusers`, `systemctl` — were not run; they are marked
> where they appear.
> Verification: `systemd-analyze verify` was run on the machine that wrote this
> page. `nxdns check`, `nxdns import`, `nxdns export` and `nxdns run` were run
> there too, but against a scratch `--data-dir` and `--config` on an
> unprivileged port, because that machine is not a deploy target and has no
> `/etc/nxdns`, no `/var/lib/nxdns` and no root. The steps that need root on a
> target host — `install`, `systemd-sysusers`, `systemctl` — were not run; they
> are marked where they appear.
>
> The download in step 1 could not be run at all: this repository has no tags
> and no published release, so every release URL on this page is a 404 today.
> Its commands are the ones [Verify a release](verify-a-release.md) covers in
> full, and the URL shapes — including the `releases/latest` redirect the
> version is read from — were probed there against `gitea.com`, a public
> instance running the same Gitea series.
>
> The `zig build dist` and `zig build verify-dist` blocks in the last section
> were run here, both to completion and both exiting 0; that section carries
> the detail.
## 1. Build the binary
## 1. Download and verify
Requires Zig 0.16.0 and Node.js. From the repository root:
Two static musl tarballs are published per release, one per architecture. Pick
`x86_64-linux-musl` for a normal PC or server and `aarch64-linux-musl` for a
Raspberry Pi 5.
```sh
(cd web && npm ci && npm run build)
zig build cross -Dweb-dist=web/dist -Doptimize=ReleaseSafe
BASE=https://git.mial.net/mokhtar/nxdns
VERSION=$(curl -fsS -o /dev/null -w '%{redirect_url}' "$BASE/releases/latest" |
sed 's#.*/releases/tag/v##')
mkdir -p ~/nxdns-release && cd ~/nxdns-release
curl -fLO "$BASE/releases/download/v$VERSION/nxdns-$VERSION-x86_64-linux-musl.tar.gz"
curl -fLO "$BASE/releases/download/v$VERSION/SHA256SUMS.txt"
curl -fLO "$BASE/releases/download/v$VERSION/SHA256SUMS.txt.asc"
```
The first command builds the admin interface into `web/dist`; the second
embeds that directory in the binary. Build the interface every time, before the
binary: a stale `web/dist` ships an admin UI that does not match the API it
talks to.
The first line asks the server which release is current instead of hardcoding a
number that goes stale one release later — Gitea redirects `releases/latest` to
the newest published release's tag page. To install a particular version
instead, set `VERSION=<version>` yourself with the one you want; the tarball
filenames carry the version either way, so there is no version-free download
URL for them.
Two static musl binaries come out, one per deploy target:
Verify before you extract. The signature is over `SHA256SUMS.txt`, and
`SHA256SUMS.txt` is over the tarballs:
- `zig-out/cross/x86_64-linux-musl/nxdns`
- `zig-out/cross/aarch64-linux-musl/nxdns`
```sh
gpg --verify SHA256SUMS.txt.asc SHA256SUMS.txt
sha256sum -c --ignore-missing SHA256SUMS.txt
tar -xzf "nxdns-$VERSION-x86_64-linux-musl.tar.gz"
```
Both are statically linked and need nothing installed on the target host.
[Verify a release](verify-a-release.md) has the whole procedure: where the
public key comes from, what fingerprint to expect, what each failure means, and
what the signature does and does not prove. Read it once before your first
install.
The extracted directory `nxdns-$VERSION-x86_64-linux-musl/` holds everything
this page installs:
| File | What it is |
| --- | --- |
| `nxdns` | The static binary, mode 0755 |
| `nxdns.service` | The systemd unit |
| `nxdns.conf` | The sysusers fragment that creates the `nxdns` user |
| `LICENSE` | EUPL-1.2 |
| `THIRD-PARTY-NOTICES` | Licences of everything compiled or bundled in |
| `INSTALL.md` | A short version of this page |
> Not verified on this host: no release exists yet, so none of these commands
> could be run against one — the `releases/latest` lookup returns 404 for this
> repository and leaves `VERSION` empty. The same lookup was run against
> `gitea.com/gitea/tea` on Gitea `1.27.0+dev` and printed `0.15.1`.
## 2. Copy the files to the target
```sh
scp zig-out/cross/x86_64-linux-musl/nxdns target:/tmp/nxdns
scp deploy/systemd/nxdns.service deploy/systemd/sysusers.conf target:/tmp/
cd "nxdns-$VERSION-x86_64-linux-musl"
scp nxdns nxdns.service nxdns.conf target:/tmp/
```
For a Raspberry Pi 5, copy `zig-out/cross/aarch64-linux-musl/nxdns` instead —
see [Raspberry Pi 5](#raspberry-pi-5) below.
For a Raspberry Pi 5, extract the `aarch64-linux-musl` tarball instead — see
[Raspberry Pi 5](#raspberry-pi-5) below.
> Not verified on this host: `target` is a placeholder for your server's
> hostname, and the machine that wrote this page has no second host to copy to.
> What was verified is that both source paths exist after step 1 and that the
> aarch64 file is a statically linked aarch64 ELF executable.
> hostname, and the machine that wrote this page has no second host to copy
> to.
Before copying, you can confirm the unit file parses:
```sh
systemd-analyze verify deploy/systemd/nxdns.service
systemd-analyze verify nxdns.service
```
Off the target host this prints one complaint and exits 1:
@@ -68,6 +115,9 @@ That is the ExecStart path check finding no binary yet. Any other message is a
real problem with the unit. On the target, after step 3, the same command
should print nothing.
> Verified on this host against `deploy/systemd/nxdns.service` in a checkout,
> which is the same file the tarball ships — the path is the only difference.
## 3. Install the binary, the user and the unit
Run as root on the target:
@@ -75,7 +125,7 @@ Run as root on the target:
```sh
install -m 0755 /tmp/nxdns /usr/local/bin/nxdns
install -m 0644 /tmp/sysusers.conf /usr/lib/sysusers.d/nxdns.conf
install -m 0644 /tmp/nxdns.conf /usr/lib/sysusers.d/nxdns.conf
systemd-sysusers
install -m 0644 /tmp/nxdns.service /etc/systemd/system/nxdns.service
@@ -84,6 +134,9 @@ systemctl daemon-reload
mkdir -p -m 0755 /etc/nxdns
```
The sysusers fragment ships under the name it is installed as, so there is no
rename to get wrong.
> Not verified on this host: these commands need root on a target machine. The
> files they install were read at HEAD and the unit was checked with
> `systemd-analyze verify`.
@@ -236,27 +289,94 @@ availability and disk state without a login.
## Raspberry Pi 5
The Pi 5 is aarch64. Nothing about the procedure changes except which binary
you copy — the cross build needs no toolchain on the Pi and no toolchain beyond
Zig on the build machine:
The Pi 5 is aarch64. Nothing about the procedure changes except which tarball
you take:
```sh
(cd web && npm ci && npm run build)
zig build cross -Dweb-dist=web/dist -Doptimize=ReleaseSafe
scp zig-out/cross/aarch64-linux-musl/nxdns pi:/tmp/nxdns
scp deploy/systemd/nxdns.service deploy/systemd/sysusers.conf pi:/tmp/
curl -fLO "$BASE/releases/download/v$VERSION/nxdns-$VERSION-aarch64-linux-musl.tar.gz"
sha256sum -c --ignore-missing SHA256SUMS.txt
tar -xzf "nxdns-$VERSION-aarch64-linux-musl.tar.gz"
cd "nxdns-$VERSION-aarch64-linux-musl"
scp nxdns nxdns.service nxdns.conf pi:/tmp/
```
Then follow steps 3 to 6 on the Pi.
The build was run on the machine that wrote this page and
`zig-out/cross/aarch64-linux-musl/nxdns` is a statically linked aarch64 ELF
executable.
> Not verified on this host: no release exists to download, `pi` is a
> placeholder for your Pi's hostname, and this page was written on an x86_64
> machine with no Pi attached.
> Not verified on this host: the two `scp` lines. `pi` is a placeholder for
> your Pi's hostname, and this page was written on an x86_64 machine with no Pi
> attached. The build steps above it were run; the copy was not.
## Build from source instead
> Not verified on this host: the aarch64 binary was not executed. This host is
> x86_64 and has no `qemu-aarch64` to run it under. Running it needs a
> Raspberry Pi 5 or another aarch64 machine.
You do not need this to install nxdns, and it gets you a binary nobody has
signed. It is here for two cases: you want to run something other than a
tagged release, or you want to build the release yourself and compare it
against the published one. For the second case, follow
[Verify a release](verify-a-release.md) rather than this section — it says what
the comparison is and is not worth.
Requires Zig 0.16.0 and Node.js. From the repository root:
```sh
(cd web && npm ci && npm run build)
VERSION=$(sed -n 's/^[[:space:]]*\.version[[:space:]]*=[[:space:]]*"\([^"]*\)".*/\1/p' build.zig.zon)
zig build dist -Dversion-string="$VERSION" -Dgit-commit="$(git rev-parse HEAD)" \
-Dweb-dist=web/dist -Doptimize=ReleaseSafe
```
The first command builds the admin interface into `web/dist`; the last one
embeds that directory in the binary. Build the interface every time, before the
binary: a stale `web/dist` ships an admin UI that does not match the API it
talks to. `dist` refuses to run against the `web/dist-placeholder` default for
exactly that reason, so there is no way to skip it by accident.
`-Dversion-string` is required and has no default. It is what `nxdns version`
prints. Take it from `build.zig.zon` rather than inventing one: `verify-dist`
asserts that the version under build equals `.version` there, so a made-up
string like `0.0.0-local` builds but then fails verification. `-Dgit-commit`
is what distinguishes your build from the published one of the same version.
What comes out under `zig-out/dist/` is the same set a release publishes,
minus the signature and the image digest:
- `bin/<triple>/nxdns` — the stripped static binary, one per target
- `stage/nxdns-<version>-<triple>/` — the staged payload, one per target
- `nxdns-<version>-<triple>.tar.gz` — one tarball per target
- `SHA256SUMS` — the two tarball hashes. The release publishes this as
`SHA256SUMS.txt`, with a third line for the image digest appended
The two targets are `x86_64-linux-musl` and `aarch64-linux-musl`. Both binaries
are statically linked and need nothing installed on the target host.
Check the result the same way the release pipeline does:
```sh
zig build verify-dist -Dversion-string="$VERSION" -Dgit-commit="$(git rev-parse HEAD)" \
-Dweb-dist=web/dist -Doptimize=ReleaseSafe
```
`verify-dist` extracts each archive and asserts the ELF is static and within
the size budget, that the layout and file modes are exactly what step 1 lists,
and that `nxdns version` prints what was built. It exits non-zero on any
failure.
> Verified on this host: `zig build dist` and `zig build verify-dist` were both
> run to completion with the version taken from `build.zig.zon`. `dist`
> produced the two tarballs, `SHA256SUMS` and the staged payloads described
> above; `verify-dist` exited 0 with every check passing and the aarch64
> `nxdns version` check skipped for want of `-fqemu`. Passing a made-up
> `-Dversion-string=0.0.0-local` was also run: `dist` succeeded and
> `verify-dist` then failed with
> `FAIL zon-version: build.zig.zon says '0.0.1', the build says '0.0.0-local'`,
> which is why this section reads the version out of `build.zig.zon`.
From here, join the page at step 2 with the staged directory in place of the
extracted one:
```sh
cd "zig-out/dist/stage/nxdns-$VERSION-x86_64-linux-musl"
scp nxdns nxdns.service nxdns.conf target:/tmp/
```
The aarch64 binary is built by the same command and needs no toolchain on the
Pi.