Commit ab18152
ci: notice when a new Debian stable leaves the pinned suite behind (#733)
* feat(net): resolve device .local names over mDNS
Go never resolves ".local" itself. net/conf.go routes those names to libc
only when cgo is available, and every FTW build sets CGO_ENABLED=0, so the
pure Go resolver is always selected: a configured "zap.local" became a
unicast DNS query to the site router and failed. That holds on every base
image and every libc, musl and glibc alike, so the container's distro was
never the variable here.
Add internal/mdnsresolve, which answers those names over multicast DNS, and
route every driver transport through it: Modbus TCP, MQTT (driver and Home
Assistant bridge), HTTP including the TLS-pinned client, WebSocket and raw
TCP. Only ".local" names take the new path; literal IPs and ordinary DNS
names dial exactly as before.
Resolution runs per dial rather than once at startup, so a device that moves
to a new DHCP lease is found again on the next reconnect with no config edit.
Answers are cached for the record TTL clamped to 30-120s so reconnect loops
cannot flood the LAN, and failures are cached for 5s so a still-booting
device is retried soon. Failures log "mDNS resolution failed" and name the
mechanism rather than surfacing as a generic dial error.
The scanner's reverse PTR lookup moves into the same package so there is one
mDNS implementation instead of two.
Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com>
* build: run one base across the whole stack (debian:bookworm-slim)
Core was alpine:3.22 and the updater sidecar was docker:27-cli (also
alpine), while the optimizer was already python:3.12-slim-bookworm. That
is two libcs and two security streams in one deployment, and two base
rootfs blobs pulled per host. Put all three on debian:bookworm-slim so the
layer is pulled once and tracked once.
glibc additionally lets the image run ordinary prebuilt vendor binaries,
which musl cannot, and a full userland makes on-site docker exec debugging
practical. libnss-mdns is installed and wired into nsswitch.conf so .local
resolves for glibc tools inside the container when an avahi socket is
mounted; the FTW process does not depend on that, because a CGO_ENABLED=0
binary bypasses NSS entirely and resolves .local in Go instead.
The binary stays fully static and still cross-compiles on the build
platform, so only the small runtime layer is emulated for arm64. Measured:
79s for a full arm64 build, image 148MB (amd64 128MB, up from 53MB).
wget is now installed explicitly and asserted by the boundary test. It is
contractual rather than incidental: ftw-updater docker-execs it inside the
core image to decide whether an update commits, and updaters already in
the field will keep doing so, so the core image cannot stop shipping it.
The boundary test's `^FROM alpine:` line is replaced by the invariant it
was actually standing in for -- core must not build on a Python base --
and given a real error message.
Also embed tzdata in the binary as a fallback. Production code reads
time.Local, which silently degrades to UTC when zoneinfo is missing, and
the tzdata tests skip rather than fail, so that regression would ship
green. Verified uid 100 / gid 101 have no passwd entry on this base, and
corrected the two comments that attributed them to an alpine adduser.
Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com>
* build: pin the stack to Debian 13 trixie, the current stable
bookworm is oldstable. Move all three images to trixie so the deployment
tracks the suite that is actually receiving full security support, and so
the base matches the Raspberry Pi OS release the SD image is built from
(deploy/pi-gen/config: RELEASE=trixie).
This also brings the optimizer along. Leaving it on a bookworm-derived
python image would have split the shared base layer, which is the whole
reason the images were aligned -- verified after the move that core,
updater and optimizer resolve to one identical base layer digest.
Verified on trixie: CVXPY 1.9.2 solves and highspy imports under Python
3.12.13; the updater's copied docker CLI 27.5.1 and compose plugin v2.33.0
both run; core has wget, the CA bundle, zoneinfo and an nsswitch.conf
wired for mdns, and runs as uid 100 / gid 101.
Pinned to the codename rather than a stable alias, so a major-version jump
can never arrive silently on a rebuild.
Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com>
* ci: notice when the container base falls behind Debian stable
The images pin a Debian codename rather than a suite alias, so a major
version jump can never arrive silently on a rebuild. The cost of pinning
is that nothing notices when a new stable ships, and a suite quietly stops
receiving full security support once it becomes oldstable -- which is
exactly the state bookworm was already in.
Truth comes from Debian's own Release file for the stable suite, not from
registry tag listings: tags are noisy, rate-limited, and say nothing about
which suite Debian considers stable.
The check reads the pin out of the three Dockerfiles rather than hard-
coding it, so it cannot drift from what actually ships, and it fails the
run rather than reporting "current" if it could not reach Debian. It also
flags the images having drifted apart, since a split pin silently costs
the shared base layer they were aligned for.
When a new stable is found it reports whether debian:<suite>-slim and the
matching python:<ver>-slim-<suite> are actually published yet, because the
python image can lag by days and moving core alone would split the pin.
Follows the bundled-driver-pin workflow: one issue, rewritten each run and
closed automatically once the pin is current, so a suite left alone for a
year costs one thread rather than fifty-two.
Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com>
* build: bump the optimizer to 1.4.0 for the new base
The optimizer releases independently of core, and optimizer-release.yml
skips the build entirely when an image already exists for the requested
version, then asserts that the published image's
org.opencontainers.image.revision label matches the commit being released.
Changing Dockerfile.optimizer without moving the version therefore does not
merely produce two different artifacts under one number -- it cannot ship
at all. Releasing 1.3.2 from this commit would either reuse the bookworm
image or fail metadata verification.
Minor rather than patch: the Python surface is untouched, but the runtime
underneath it moves a Debian major version, so consumers are getting a
materially different artifact.
Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com>
* build: leave .local name resolution to its own change
@
The base move and the resolver were stacked in one branch, which made the
resolver look like a consequence of choosing Debian. It is not: the two are
independent, and the resolver is the one with the device-facing risk.
Take the resolver back out so this branch is only the base move, and restate
what libnss-mdns actually buys here — tools inside the image, not the FTW
process, which is CGO_ENABLED=0 and never consults NSS.
Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com>
@
* chore(release): add a changeset for the base-currency check
The `no-changeset` label is the right escape hatch here, but applying a label needs write access to srcfl/ftw. A patch entry saying no runtime behaviour changes keeps the gate honest without one.
Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com>
* ci: keep the debian base schedule from running on forks
Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Fredrik Ahlgren <fredrik@sourceful-labs.com>1 parent fed482a commit ab18152
4 files changed
Lines changed: 209 additions & 1 deletion
File tree
- .changeset
- .github/workflows
- scripts
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
100 | 100 | | |
101 | 101 | | |
102 | 102 | | |
103 | | - | |
| 103 | + | |
104 | 104 | | |
105 | 105 | | |
106 | 106 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
0 commit comments