Building the package#
wheelhouse-agent_<version>_<arch>.deb is written by
packaging/build-deb.py, a single Python file that uses
nothing outside the standard library. There is no dpkg-deb, no debhelper and no
build container: a .deb is an ar archive of three members, and tarfile and a
sixty-byte header are enough to write one. That is why the same command produces the same
package in CI, on a laptop and inside the image build, and why the packaging step has no
toolchain of its own to break.
The command#
(cd ui && npm ci && npm run build)
(cd agent && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath \
-ldflags="-s -w -X main.version=1.2.3" -o wheelhouse-agent-amd64 .)
python3 packaging/build-deb.py --version 1.2.3 --arch amd64 \
--binary agent/wheelhouse-agent-amd64 --ui ui/dist --out dist/| Flag | Default | Meaning |
|---|---|---|
--version | required | Goes into control, the file name and the on-box README |
--arch | amd64 | Architecture: and the file name |
--binary | required | The built agent for that architecture |
--ui | required | The built UI. The script exits if it holds no index.html |
--out | . | Output directory, created if missing |
--maintainer | Wheelhouse <releases@rhymelikedi.me> | Maintainer: |
SOURCE_DATE_EPOCH, if set, becomes the modification time on every archive member
instead of the build time — the usual reproducible-build lever, so a rebuild of the same
inputs does not differ only in timestamps.
What the package installs#
| Path | Contents |
|---|---|
/usr/bin/wheelhouse-agent | The agent, mode 0755 |
/usr/share/wheelhouse/ui/ | The built UI — what --ui-dir points at |
/usr/bin/wheelhouse-install | The disk installer |
/usr/bin/wheelhouse-autoinstall | The seed finder |
/usr/bin/wheelhouse-opnsense-import | The OPNsense importer, so a migration can be done on the router itself |
/usr/lib/wheelhouse/firstboot.sh | Mints the router API key, enables REST, pins the API to loopback |
/usr/lib/wheelhouse/wait-for-vyos.sh | Both units wait for VyOS to finish loading its configuration |
/usr/lib/wheelhouse/console-banner.sh | Renders /etc/issue |
/usr/lib/wheelhouse/install-driver.py | Drives VyOS' install image through a pseudo-terminal |
/usr/lib/wheelhouse/seed-to-answers.py | Cloud-config in, installer answers out |
/usr/share/wheelhouse/dialogrc | The installer's colours |
/usr/share/wheelhouse/logo.txt | The product logo in ANSI, for fastfetch --logo-type file-raw and neofetch --ascii |
/lib/systemd/system-generators/wheelhouse-live-installer | Turns getty@tty1 into the installer on a live boot |
/lib/systemd/system/wheelhouse-agent.service | The agent |
/lib/systemd/system/wheelhouse-firstboot.service | First boot |
/lib/systemd/system/wheelhouse-console.service + .timer | The console banner, redrawn on a timer |
/usr/share/doc/wheelhouse-agent/copyright | packaging/copyright — every licence in full |
/usr/share/doc/wheelhouse-agent/README | Generated: what the package is, the VyOS credit, and where the corresponding source lives |
/usr/share/doc/wheelhouse-agent/third-party.md | Generated: the third-party map the web UI's About page sends customers to by name |
Enabled by symlink, not by postinst#
data.symlink(f"./etc/systemd/system/multi-user.target.wants/{unit}",
f"/lib/systemd/system/{unit}")The wants-symlinks ship inside the package rather than being created by systemctl enable
in postinst. That is what lets the package be installed inside a live-build chroot,
where there is no systemd to talk to — and the image build is exactly that.
The console banner carries two of those symlinks. multi-user.target.wants is what
runs it; getty.target.wants is what puts it in the same transaction as the gettys, so
its Before= ordering has something to bite on — ordering only constrains units that are
already in the transaction. Without the second symlink, the first screen of a new box is
the stock banner until someone presses Enter.
control#
Package: wheelhouse-agent
Architecture: amd64
Depends: bash, python3, dialog, curl
Section: net
Priority: optional
Homepage: https://releases.rhymelikedi.meFour dependencies, and each is load-bearing: bash for the installer and first boot,
python3 for the pty driver and the seed reader, dialog for the installer's screens,
curl for the health checks. Installed-Size is computed from the binary plus the UI
tree.
The description names VyOS and points at the on-box README, so a machine with no network and no login to the forge still says what it is built on.
postinst and prerm#
#!/bin/sh
set -e
if [ "$1" = configure ] && [ -d /run/systemd/system ]; then
systemctl daemon-reload || true
systemctl start wheelhouse-console.timer || true
systemctl try-restart wheelhouse-agent.service || true
fi
exit 0Two things about it are deliberate. The -d /run/systemd/system guard is what makes the
package installable in a chroot, where every systemctl call would fail. And
try-restart rather than restart means installing the package on a machine where the
agent was not running does not start it.
prerm stops the agent on remove, under the same guard.
Inspecting a built package#
Nothing exotic is needed, because nothing exotic went in:
ar t dist/wheelhouse-agent_1.2.3_amd64.deb # debian-binary control.tar.gz data.tar.gz
ar p dist/wheelhouse-agent_1.2.3_amd64.deb control.tar.gz | tar tzvf -
ar p dist/wheelhouse-agent_1.2.3_amd64.deb data.tar.gz | tar tzvf - | head -40
# Or, with dpkg available:
dpkg-deb -I dist/wheelhouse-agent_1.2.3_amd64.deb
dpkg-deb -c dist/wheelhouse-agent_1.2.3_amd64.debWhere the package goes next#
Two places, and they are different jobs:
- The release. CI builds
amd64andarm64, packages both, and attaches them to the tag's release and the public download host — Making a release. - The image. The ISO job builds the
amd64package from its own checkout and drops it intovyos-build/packages/, where live-build installs every package it finds — Building the image.
The arm64 package exists so the agent can run off-router against an arm64 router. There is no arm64 image.
See also#
- Building the agent · Building the web UI — the two inputs.
- Building the image — where the package is consumed.
- Making a release — how it is published and checksummed.
- The repository, directory by directory — the rest of
packaging/. - The
.deb· systemd units · Files and directories — what the installed package looks like on a running router. - What first boot does · The console banner — two of the things this package installs, documented for an operator.
- Upgrading the agent — installing this package on a router that is already running one.
Checked against#
packaging/build-deb.py ·
packaging/README.md ·
packaging/copyright ·
packaging/wheelhouse-agent.service ·
packaging/wheelhouse-firstboot.service ·
packaging/wheelhouse-console.service ·
packaging/wheelhouse-console.timer ·
packaging/wheelhouse-live-installer ·
.forgejo/workflows/ci.yml ·
.forgejo/workflows/iso.yml