diff --git a/docs/notes/85-packaging.md b/docs/notes/85-packaging.md index 0d55ba4..e2b8473 100644 --- a/docs/notes/85-packaging.md +++ b/docs/notes/85-packaging.md @@ -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 diff --git a/pkgs/helixscreen/pkg.conf b/pkgs/helixscreen/pkg.conf index eb74733..7d5b4a9 100644 --- a/pkgs/helixscreen/pkg.conf +++ b/pkgs/helixscreen/pkg.conf @@ -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" diff --git a/pkgs/klipper/pkg.conf b/pkgs/klipper/pkg.conf index 18b2623..784cab7 100644 --- a/pkgs/klipper/pkg.conf +++ b/pkgs/klipper/pkg.conf @@ -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 diff --git a/pkgs/lib.sh b/pkgs/lib.sh index 45cfa17..1fef9da 100644 --- a/pkgs/lib.sh +++ b/pkgs/lib.sh @@ -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 diff --git a/pkgs/moonraker/pkg.conf b/pkgs/moonraker/pkg.conf index 57ffc73..3a891b8 100644 --- a/pkgs/moonraker/pkg.conf +++ b/pkgs/moonraker/pkg.conf @@ -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" diff --git a/qa/static/test_recipe_layout.py b/qa/static/test_recipe_layout.py index 4bc1a39..47967ca 100644 --- a/qa/static/test_recipe_layout.py +++ b/qa/static/test_recipe_layout.py @@ -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))