Version the pinned recipes on the release, not on memory (#21)

apk decides whether to upgrade from the version string alone. Three recipes
pin an upstream version -- anvil-moonraker on ${MOONRAKER_VERSION#v},
anvil-klipper on the pinned commit, anvil-helixscreen on its tag -- and each
ships a payload/ of ours beside that upstream tree. Our half changes without
the string moving, so the feed serves new bytes under the name every printer
already holds and `apk upgrade` finds nothing to do. #16 did exactly that to
moonraker.conf, the file that moves Moonraker's config to
/usr/data/anvil-data/config.

PKG_RELEASE is that string's other half, and it is the release date now:
pkg_release_stamp, monotone, with the same-day letter as a trailing digit so
20260827b sorts between 20260827 and 20260828. Nobody has to notice that a
payload changed, which is the only version of this that holds.

PKG_STAMP_EXTRA="$(pkg_payload_hash)" is not this and reads as though it were.
It hashes payload/ so the BUILD rebuilds; it says nothing to a printer. All
three recipes had it, and all three would have shipped stale.

test_recipe_layout.py makes it the rule for recipes added later: a pinned
recipe with a payload/ must take pkg_release_stamp, and the failure names the
recipe and the line to write.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
MonstrofilandClaude Opus 5 authored and GitHub committed 2026-09-08 20:56:59 +03:00
1 parent 5316694795
commit 5f237eff62
6 files changed
+93 -3

No files matched your search

+10
View File
@@ -889,6 +889,16 @@ yesterday would resolve and install yesterday's `anvil-core` without
complaint. `patch.sh` checks for the expected filename before installing and
names `./bin/build-packages.sh` if it is absent.
The recipes whose version is a **pin** have the opposite problem.
`anvil-moonraker`, `anvil-klipper`, `anvil-helixscreen` and `anvil-timelapse`
ship files of ours beside an upstream tree, and apk decides whether to upgrade
from the version string alone: a change under `payload/` is new bytes under an
old name, so a printer already holding that version keeps what it has.
`PKG_RELEASE` is the field that moves — raise it when `payload/` changes, and
back to 1 when the pin itself moves. `PKG_STAMP_EXTRA="$(pkg_payload_hash)"`
is a different mechanism for a different reader: it makes *this build* notice
the edit and rebuild. It does not make a printer notice it.
The model is the one fact opkg cannot work out for itself: both chamber
configs are built every time, they own the same `config/printer.chamber.cfg`,
and they `Conflict`. opkg refuses the pair with exit 255 — measured — so
+7 -1
View File
@@ -13,7 +13,13 @@ PKG_NAME=anvil-helixscreen
# this reads it. Both live there because both are the pin, and a bump has to
# move them together.
PKG_VERSION="$HELIX_PKG_VERSION"
PKG_RELEASE=1
# THE REVISION IS THE RELEASE DATE, because the version above is a pin and does
# not move when our half of this package changes. apk upgrades on the version
# string alone, so a hand-maintained number here is one somebody has to
# remember; pkg_release_stamp is the release, and every printer on an earlier
# one sees a higher -r. qa/static/test_recipe_layout.py holds recipes with a
# payload/ to this.
PKG_RELEASE="$(pkg_release_stamp)"
PKG_EXCLUDE=".version"
+7 -1
View File
@@ -19,7 +19,13 @@ PKG_NAME=anvil-klipper
# that way -- `+git` parsed fine under the old packager for a year, which is
# why nothing noticed until the format changed.
PKG_VERSION="0.13.0~$(printf '%.8s' "$KLIPPER_VERSION")"
PKG_RELEASE=1
# THE REVISION IS THE RELEASE DATE, because the version above is a pin and does
# not move when our half of this package changes. apk upgrades on the version
# string alone, so a hand-maintained number here is one somebody has to
# remember; pkg_release_stamp is the release, and every printer on an earlier
# one sees a higher -r. qa/static/test_recipe_layout.py holds recipes with a
# payload/ to this.
PKG_RELEASE="$(pkg_release_stamp)"
PKG_EXCLUDE=".version"
# THE INTERPRETER IS A DEPENDS: etc/s6-rc/source/klipper/run execs $FF_PYTHON
+22
View File
@@ -50,6 +50,28 @@ pkg_payload_hash() {
| sha256sum | cut -c1-16
}
# pkg_release_stamp -> the packaging revision for a recipe whose VERSION is an
# upstream pin but whose contents are partly this repo's.
#
# apk decides whether to upgrade from the version string alone, and for those
# recipes the string does not move when our files change: anvil-moonraker is
# `0.11.0` whatever we ship beside upstream's tree, anvil-klipper is the
# pinned commit whatever our klippy extras say. The revision is what moves, so
# it is the release date rather than a number somebody has to remember to
# raise -- a printer on any earlier release sees a higher `-r` and upgrades.
#
# THE TRAILING LETTER IS A SAME-DAY RE-RELEASE (20260827b, and there were four
# that day). apk compares `-r` numerically, so the letter becomes a digit
# after the date rather than beside it: 20260827 -> 202608270, `b` ->
# 202608272, and the next day's 202608280 is still larger. Appending the
# letter's index without the extra place would order 20260827b above 20260828.
pkg_release_stamp() {
_d=${MOD_VER%%[a-z]}
_l=${MOD_VER#"$_d"}
printf '%s%s' "$_d" "$(awk -v l="$_l" \
'BEGIN { print (l == "") ? 0 : index("abcdefghijklmnopqrstuvwxyz", l) }')"
}
# pkg_signkey_hash -> a cache key for the feed's public key, or `unsigned`.
#
# anvil-core ships that key, and it lives OUTSIDE the repo, so
+7 -1
View File
@@ -13,7 +13,13 @@ PKG_ARCH=all
# sqlite the releases after it wanted. It builds its own CPython now, so it
# can. apk orders 0.11.0 after every 0.8.0 string that shipped.
PKG_VERSION="${MOONRAKER_VERSION#v}"
PKG_RELEASE=1
# THE REVISION IS THE RELEASE DATE, because the version above is a pin and does
# not move when our half of this package changes. apk upgrades on the version
# string alone, so a hand-maintained number here is one somebody has to
# remember; pkg_release_stamp is the release, and every printer on an earlier
# one sees a higher -r. qa/static/test_recipe_layout.py holds recipes with a
# payload/ to this.
PKG_RELEASE="$(pkg_release_stamp)"
PKG_EXCLUDE=".version"
+40
View File
@@ -19,6 +19,7 @@ legitimately changed, which is the opposite of what a test is for.
What is left is the two that bite:
"""
import os
import re
import pytest
@@ -126,3 +127,42 @@ def test_klipper_still_depends_on_numpy():
"lost feature -- klippy loads [stepper_resonance_tester] from "
"FlashForge's printer.vibration.cfg and dies on the ImportError, so "
"the printer never reaches ready.")
def test_a_pinned_recipe_that_ships_our_files_versions_on_the_release():
"""A recipe apk can never see a change in is a recipe printers never update.
Two kinds of recipe live under pkgs/. One versions on `$MOD_VER`, the
release date, so every build says something new. The other pins an
upstream version -- `${MOONRAKER_VERSION#v}`, the Klipper commit, the
HelixScreen tag -- and that string stays put however much of OUR half of
the package changes, because `payload/` is ours. apk compares the version
and nothing else: same string, no upgrade, whatever the bytes say.
`PKG_STAMP_EXTRA="$(pkg_payload_hash)"` does not help here and reads as
though it should. It hashes `payload/` so the BUILD rebuilds the package;
it says nothing to a printer.
So a pinned recipe with a `payload/` directory takes its revision from
`pkg_release_stamp` -- the release date, monotone, and nobody's to
remember. This test is what makes that the rule rather than a habit.
"""
missing = []
for name, d in recipes():
if not os.path.isdir(os.path.join(d, "payload")):
continue # ships nothing of ours to go stale
conf = open(os.path.join(d, "pkg.conf")).read()
version = re.search(r"^PKG_VERSION=(.*)$", conf, re.M)
release = re.search(r"^PKG_RELEASE=(.*)$", conf, re.M)
if version and "MOD_VER" in version.group(1):
continue # the whole version is the release
if not release or "pkg_release_stamp" not in release.group(1):
missing.append("%s (PKG_VERSION=%s, PKG_RELEASE=%s)" % (
name,
version.group(1) if version else "?",
release.group(1) if release else "1 (default)"))
assert not missing, (
"these recipes pin an upstream version and ship a payload/ of ours, so "
"a change to our half leaves the version they had -- and no printer "
"upgrades: %s. Set PKG_RELEASE=\"$(pkg_release_stamp)\"."
% ", ".join(missing))