Add a Debian package and bash completion

make deb builds dist/jarl_<version>_<arch>.deb with dpkg-deb alone (no
root), for the host or another architecture (DEB_ARCH=arm64). The package
creates the jarl user and directories and an example jarl.yaml on the
first install, restarts a running jarld on upgrades, never touches
/etc/jarl/jarl.yaml, stops jarld on removal, and keeps Jarl's data even
on purge.

make install and the package install bash completion for jarl and jarld.
Besides commands and flags it completes server and group names from the
CLI configuration, zone, view, ACL, TSIG key and user names from the
server, and record types. make dist includes the completion scripts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
abrown38andClaude Opus 5.5 committed 2026-10-08 15:10:57 +00:00
1 parent 0f2c318aaf
commit 352c9cb394
13 files changed
+555 -30

No files matched your search

+20 -1
View File
@@ -2,7 +2,26 @@
`jarl` talks to jarld's REST API with an API token. It runs on the DNS server
or on any machine that can reach jarld. Every command has `--help` with
examples, and `jarl completion bash|zsh|fish` prints shell completion.
examples.
## Shell completion
`make install` and the Debian package install bash completion for `jarl`
and `jarld`; it works in new shells (it needs the `bash-completion`
package, which Debian and Ubuntu install by default). Elsewhere, or for
zsh and fish, `jarl completion bash|zsh|fish` prints the script; for
example `jarl completion bash > ~/.local/share/bash-completion/completions/jarl`.
Besides commands and flags, Tab completes:
* `-s/--server` and the `config` commands: the server and group names in
your configuration file (and `all`; after a comma, the next name);
* `ZONE`, `VIEW`, `ACL`, `USER` and TSIG `KEY` arguments, and the `--view`,
`--zone` and `--user` flags: names from the server, asked with your
token. A zone in several views is offered once, with its views shown.
If the server can't be reached in 3 seconds, nothing is offered;
* record `TYPE`s, with a short description of each;
* `-o/--output`: `table`, `json`, `yaml`.
## Setup
+36 -1
View File
@@ -26,9 +26,11 @@ internal/
acme/ ACME DNS-01 challenge records, shared by jarl acme and the httpreq endpoint
config/ jarld's YAML configuration
deploy/ systemd unit, sysusers.d and tmpfiles.d files
deploy/debian/ the Debian package's control template and postinst/prerm/postrm
contrib/acme.sh/ acme.sh DNS API hook
contrib/lego/ program for lego's exec DNS provider
scripts/e2e.sh end-to-end test against a real named
scripts/mkdeb.sh builds the Debian package (make deb)
```
The main flow is in `internal/engine/engine.go` (`applyOps`):
@@ -38,11 +40,44 @@ record. [concepts.md](concepts.md) describes it from the outside.
## Building and testing
```sh
make # bin/jarld, bin/jarl (CGO_ENABLED=0, version from ./VERSION)
make # bin/jarld, bin/jarl (CGO_ENABLED=0, version from ./VERSION),
# and bin/completion/{jarl,jarld} (bash completion)
make test # go vet, gofmt check, go test ./...
make e2e # scripts/e2e.sh
make deb # dist/jarl_<version>_<arch>.deb (DEB_ARCH=arm64 etc. cross-builds)
```
### The Debian package
`make deb` runs `scripts/mkdeb.sh`: it builds the binaries for
`DEB_ARCH`, installs them with `make install-files` into a staging tree
with Debian's paths (`PREFIX=/usr`, the unit in `/usr/lib/systemd/system`),
adds `deploy/debian/{postinst,prerm,postrm}` and a `control` file filled in
from `deploy/debian/control.in`, and packs it with `dpkg-deb
--root-owner-group`, so no root is needed. The version comes from
`./VERSION` (a `-` becomes `~`, so `0.2.0-rc1` sorts before `0.2.0`;
override with `DEB_VERSION`), and the maintainer from your git name and
email (override with `DEB_MAINTAINER`). The package ships no files in
`/etc`: postinst writes `/etc/jarl/jarl.yaml` once, so upgrades never touch
it.
To try the package without installing it on your machine:
```sh
docker run --rm -it -v "$PWD/dist:/pkg:ro" debian:trixie \
sh -c 'apt-get update && apt-get install -y /pkg/jarl_*_amd64.deb && bash'
```
### Shell completion
The completion scripts are generated by cobra (`jarl completion bash`).
`cmd/jarl/complete.go` adds value completion: it reads each command's usage
line (`add ZONE NAME TYPE DATA...`) and completes the placeholders it knows
(`ZONE`, `VIEW`, `ACL`, `USER`, TSIG `KEY`, `TYPE`, server and group
names), so a new command whose usage line uses these names gets completion
without extra code. Try it without a shell:
`jarl __complete record add ''`.
### Tests against real BIND
The render, engine and adopt tests run the real `named-checkconf`,
+41 -1
View File
@@ -22,10 +22,49 @@ make dist # optional: dist/jarl-<version>-linux-{amd64,arm64}.tar.gz
```
If you build on another machine, copy the binaries plus the `deploy/`,
`docs/` and `contrib/` directories to the DNS server.
`docs/` and `contrib/` directories to the DNS server, or build a Debian
package (below) and copy that.
## 2. Install the files
There are two ways: a Debian package, which is easiest to upgrade and
remove, or `make install`.
### With the Debian package
```sh
make deb # dist/jarl_0.1.0_amd64.deb
make deb DEB_ARCH=arm64 # for another architecture (also armhf, i386, ...)
sudo apt install ./dist/jarl_0.1.0_amd64.deb
```
`make deb` needs only `dpkg-deb` (no root, no debhelper) and cross-builds
for any architecture Go supports. apt also installs `bind9` (recommended)
if it's missing. Installing the package:
* puts the binaries in `/usr/bin`, bash completion in
`/usr/share/bash-completion/completions`, the systemd unit in
`/usr/lib/systemd/system/jarl.service`, the sysusers and tmpfiles files
in `/usr/lib`, and the docs and contrib files in `/usr/share/doc/jarl`;
* creates the `jarl` user (in group `bind`) and Jarl's directories, and
writes `/etc/jarl/jarl.yaml` from the example on the first install. No
file in `/etc` belongs to the package, so your configuration is never
replaced or asked about on upgrades;
* doesn't start jarld: continue with [3. Configure](#3-configure), then
`systemctl enable --now jarl` in step 8.
**Upgrading**: build and install the newer package the same way. A running
jarld is restarted, and upgrades its database as it starts.
**Removing** (`sudo apt remove jarl`) stops jarld and removes the program,
but named keeps running with the configuration Jarl last wrote. Even
`apt purge jarl` keeps `/etc/jarl`, `/var/lib/jarl` (the database) and the
files in `/etc/bind/jarl`, `/var/lib/bind/jarl` and `/var/cache/bind/jarl`,
since named.conf may still include them. Remove them by hand once named no
longer uses them.
### With make install
On the DNS server, as root:
```sh
@@ -37,6 +76,7 @@ This installs:
| File | Purpose |
|------|---------|
| `/usr/local/bin/jarld`, `/usr/local/bin/jarl` | daemon and CLI |
| `/usr/local/share/bash-completion/completions/jarl`, `.../jarld` | bash completion (loaded automatically by the `bash-completion` package) |
| `/etc/systemd/system/jarl.service` | systemd unit (`Type=notify`, sandboxed) |
| `/usr/lib/sysusers.d/jarl.conf` | creates the `jarl` system user and adds it to group `bind` |
| `/usr/lib/tmpfiles.d/jarl.conf` | creates `/etc/jarl`, `/var/lib/jarl`, `/etc/bind/jarl`, `/var/lib/bind/jarl`, `/var/cache/bind/jarl` |