Write /etc/keel_version beside the compatibility file #5
No reviewers
Labels
No labels
bug
documentation
duplicate
enhancement
good first issue
help wanted
invalid
question
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Keel-Linux/common!5
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/keel-version-file"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
An appliance had no file saying what it is. The console banner reads
/etc/turnkey_versionfor the name and the version because there wasnothing else to read, and its own comment said so.
This adds
/etc/keel_version, the same four fields with thekeel-prefix, written at build time beside the compatibility file. Handbook
decision 0014 (Keel-Linux/handbook#6) settles what each of the two is
for: the TurnKey file is an interface we honour, the Keel file is what we
say we are.
Why here
mk/turnkey.mk, inroot.patched/post, is the only place the versionstring exists, it is shared by every appliance rather than repeated in
each recipe, and it is the last thing that touches the tree, so neither
file can be clobbered by an overlay or a conf script. An appliance conf
script cannot do it: conf scripts run in
root.patched/body, before/etc/turnkey_versionis written, so the value they would derive from isthe parent layer's, or nothing at all on a rootfs layer. The ordering was
read out of the makefiles, not assumed.
A defect this uncovered
turnkey-version.pytakes the name for/etc/turnkey_versionfrom thefirst line of the product changelog. keel-core's changelog became
keel-core-19.0 (1) keelon 2026-09-27, so the next core layer wouldhave written
keel-core-19.0-trixie-amd64into that file, and everyparser of it is prefix sensitive:
sysversion._parse_turnkey_releasematchesturnkey-.*?-(\d.*?)-[^\d],so
get_turnkey_release()returns the empty string and the releasenumber disappears from every version string built from it;
sysversion.AppVerremoves the prefixturnkey-and then splits, sothe app name comes out as
keel-corerather thancore;keel.inspect.app.probe_appliancerequires theturnkey-prefix andreports the appliance as missing otherwise, which costs
keel inspectandkeel diffthe identity of the machine.bin/keel-version-filesnow removes at most one product prefix from thechangelog-derived string and writes
turnkey-<rest>andkeel-<rest>.The published core layer predates the changelog rename, so nothing
shipped is affected; the next build would have been.
Tests
lib/version-files.shholds the grammar and the prefix rules as purefunctions,
bin/keel-version-filesis the thin main that validates andwrites (decision 0004). 33 bats tests, both files at 100 percent
under kcov 43 (15 of 15 and 36 of 36 lines), covering every exit code (1
bad usage or a version string that is not an appliance identity, 2 an
unwritable tree, 3 the library missing) and the errexit trap of
docs/traps.md.tests/coverage.shgrew the per-target loop keel-coreuses so it can measure more than one file;
conf/turnkey.d/postfix-localstays at 100 percent, 17 of 17 lines. The threshold stays at 100.
Test plan
COVERAGE_THRESHOLD=100 tests/coverage.sh: three files, all 100percent
shellcheck -S warningclean on the new filesread from
product.mkandmk/turnkey.mkwithmake -nimage and
keel inspectstill naming the applianceRebased onto 19.x, 2026-10-02 (after common#30 and #31)
mk/turnkey.mkandmk/turnkey-desktop.mk: the identity files are written bybin/keel-version-filesas before, with the-xcheck. The per-appliance apt User-Agent (01turnkey) that this branch's makefiles still wrote is not restored: common#6 replaced it with the overlay's fixed01keel, andconf/turnkey.d/apt-identitydeletes a01turnkey. The recipe ends inmk/turnkey/seal-root(#31) as on 19.x.tests/mk-identity.bats: the harness now links the realseal-rootunder itsFAB_PATHand gives the scratch root a locked root. Three of its tests failed after the rebase without that. Four new tests: the build date is stamped after the identity files, a shipped root password fails the build, and neither makefile writes anything intoapt.conf.d.tests/coverage.sh: thelib/version-files.shandbin/keel-version-filestargets and the unmeasuredmk-identity.batsrun are added beside 19.x's targets and its before-firstboot/pam-unix run.COVERAGE.md: both tables' rows kept, 17/17 and 36/36, 37 bats for the identity files.changes/turnkey.changelog: a bullet for/etc/keel_versionat the top of the unreleasedturnkey-core-19.0 (1)entry, above #31's; the branch had none.keel-<app>-<version>-<codename>-<arch>in/etc/keel_version,turnkey-prefix kept in/etc/turnkey_version.KEEL_APT_TRACK, decision 0047):mk/turnkey.mkno longer calls fab'smake-release-deb.py, so the recipe now writes the two identity files and nothing else beforeseal-root. #32's test that no release meta package is built asserted the oldturnkey_version=... > /etc/turnkey_versiontext; it now asserts thekeel-version-filescall, andtests/mk-identity.batsproves the files are written by running the recipe.The appliance side is Keel-Linux/keel-core#9 and the decision note is Keel-Linux/handbook#6.
Reviewed together with Keel-Linux/keel-core#9 and handbook #6 and #4. Merging this one first is the ordering with no window in which anything is wrong: keel-core#9 reads
/etc/keel_versionand falls back, so it is safe either way, but this is what makes the fallback stop being the only path.What I verified rather than accepted:
/usr/share/fab/turnkey-version.pyget_turnkey_versionreturnsf"{codename}{version_tag}-{dist}-{architecture}"wherecodenameis(\S+)from the changelog's first line, sokeel-core-19.0 (2) keel;with--dist=trixieyieldskeel-core-19.0-trixie-amd64, and the make line this replaces wrote that straight into/etc/turnkey_version. All three parsers named are prefix sensitive as described:sysversion._parse_turnkey_releasematchesr"turnkey-.*?-(\d.*?)-[^\d]"and returns"";AppVer.__init__doesremoveprefix("turnkey-")thenrsplit("-", 3), so the app name comes outkeel-core;keel/inspect/app.py:43isif not text.startswith(VERSION_PREFIX) ... return None, missing("appliance", ...), which is the appliance identity gone fromkeel inspectandkeel diff. Two further readers the decision note lists also check out:inithooks/firstboot.d/29tagidthroughlib/tagid.sh, and/etc/tklbam/hooks.d/maria-db-changes, which doessed -En "s|turnkey-[a-z0-9-]+-([0-9]+)\.[0-9]+[a-z0-9]*-.*|\1|p".Welcome to Core, TurnKey GNU/Linux 19.0, which onlyfmt_sysversionover aturnkey-string produces.kvf_app_versionstrips at most one prefix and only from the front. I ran the library against all ten recipes' changelog names on both architectures: every one is accepted and every compatibility string comes out identical to what those appliances ship today, so the only new content in any image is/etc/keel_version.turnkey-keelson-19.0-trixie-amd64keepskeelson,keel-turnkey-core-19.0-trixie-amd64keepsturnkey-core../etc/turnkey_version, so each will carry./etc/keel_versionwritten from the same string in the same call and the two cannot drift.keelhas no special case for either path in assembly; they are ordinary files and the child's copy wins as it does today. Every published manifest hasparent=coretransitively. Nothing in the other ten recipes breaks.root.patched/postruns afterroot.patched/body, whose last action is the common removelists-final; onlyroot.patched/cleanupfollows and it touches apt caches and CA certificates. Conf scripts do run inroot.patched/body, before either file exists.COVERAGE_THRESHOLD=100 tests/coverage.shgivespostfix-local17/17,version-files.sh15/15,keel-version-files36/36, all 100.00. The rewritten per-target loop works andpostfix-localdid not regress.shellcheck -S warningclean. The assertions are behavioural: the script is run and the files it wrote are read back, including mode, line count and the replace-not-append case.MEDIUM
1. Every appliance build now hard-depends on a script in the common checkout, and the failure will not name itself.
mk/turnkey.mk:56,$(COMMON_BIN_PATH)/keel-version-files $$release_version $O/root.patched || exit 1. The scenario: a build host whosecommoncheckout predates this merge, or abin/that lost its mode bit, and every recipe dies inroot.patched/postafter the whole root has been built, with a bareNo such file or directory. Failing loudly is right; failing legibly costs a[ -x ... ] || { echo ...; exit 1; }beside the call, naming the checkout that is stale.2.
$$release_versionis unquoted. Empty gives one argument and exit 1 with the usage; a value with a space gives three and exit 1 too. Both are the outcome you want, but by luck rather than by construction, and quoting says which.LOW
3. A hyphenated
VERSION_TAGnow fails the build.turnkey-version.pyappends the tag to the name, so--tag=-rc1producescore-19.0-rc1-trixie-amd64, whichkvf_is_app_versionrefuses because the version field is[0-9][^-]*.--tag=rcis fine and is tested. One sentence in the usage saying the tag joins the version field would stop somebody finding this during a release.4.
kvf_app_versionandkvf_is_app_versioneach walk$KVF_PREFIXESto answer the same question with inverted senses. Onekvf_has_prefixwould make the pair read as the rule they encode.Approve
No CRITICAL and no HIGH. The two MEDIUMs are hardening of a path that already fails in the right direction, and the LOWs are documentation. The mechanism it disarms is real, the normalisation is conservative, and the blast radius is one new file per image and no change to any existing one.
MEDIUM 1 and 2 and both LOWs fixed in 8d19fbb and 0e47bdb, plus the gap #8 found:
mk/turnkey-desktop.mknow writes both identity files through the same script.tests/mk-identity.batsruns the recipe of both makefiles under make against fab stubs (6 of 8 failed before). Conflicts with #8 inmk/andtests/coverage.sh; trial-merged, the resolution is mechanical and all five files stay at 100 percent.