milestone 14: build, package, sign and publish releases
This commit is contained in:
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user