dpkg-deb prints a BARE VALUE when one control field is requested and
"<Field>: <value>" when several are. Every reader was written for one shape and
broke on the other, so no pool could be read and no pin could be matched against
real files:
- build-index.sh: kept the label, so "Package: curl" failed the name check and
the pool came out empty -- the build refused with "no .deb files";
- add-packages.sh (read_deb): rejected every genuine .deb as an invalid name;
(scan_existing): produced empty keys, so a version conflict went undetected
and the same package was copied twice;
- build-offline-repo.sh: compared a labelled value with a "package=version" pin,
which can never match.
A first attempt with `sed 's/^Field: //'` was wrong in the other direction: sed
-v prints nothing when the pattern does not match, which emptied the single-field
reads. The label is now dropped only when it is present
(awk: if the first word ends with a colon), which leaves a bare value and a
version like "1:2.3-4" untouched.
Also fixed in build-index.sh: the foreign-file guard did not list
repo-manifest.yaml, the record this script writes into the bundle itself, so
every second build stopped on its own previous output. Accepting it is what
makes a rebuild possible at all.
Tests
- The dpkg-deb stub now reproduces both shapes, the single- and the multi-field
one. It emitted only one, which is exactly why this class stayed invisible
while the suite was green.
- The rebuild case mirrors the production layout (record inside the output
directory) and is proven by mutation: removing repo-manifest.yaml from the
guard's allow-list turns it red.
The package pool was committed but the installer still stopped at "no local
package repository": it requires the compiled bundle (Packages, Packages.gz,
packages.lock.tsv, SHA256SUMS), and building that had never run.
The bundle
- 1321 packages, 884 MiB, Ubuntu 26.04 amd64, built with apt-ftparchive on the
fleet's own release so the closure matches the devices.
- built/pool/ is gitignored: those .deb files are already tracked once under
provisioning/offline-repo/packages/, and the same ~890 MB does not need a
second path. What the installer reads from the bundle is the index.
- The output directory is chmod 0755: mktemp -d created it 0700 and left it
so, which made a valid bundle unreadable to the installer's probe.
dpkg-deb output was parsed wrong in four places
- `dpkg-deb -f <file> Package Version` prints "<Field>: <value>", and the
callers kept the whole line, so the value carried its label. build-index.sh
therefore accepted no package at all and reported an empty pool;
add-packages.sh rejected every real .deb as an invalid name, missed version
conflicts, and copied a duplicate; build-offline-repo.sh never matched a pin.
- The test suite could not see any of this: its dpkg-deb stub emitted bare
values. The stub now prints the labels exactly as the real tool does, which
is what surfaced the remaining three sites.
Tests
- The secret scan covers the files we author, not apt-ftparchive's index over
third-party packages: polkit's own description reads "...asked for their
password:", which is prose in the packages, not a leak. Any secret reaching a
manifest, README or tool is still caught.
- The "test build must not touch the bundle" assertion now compares the
directory before and after instead of demanding it be empty, so a
maintainer's real build is not reported as test spill.