feat(update): fail a declared update source whose pin is not a semver

The version-source resolver orders upstream versions by semver. A pin that
is not one, such as `latest` or a branch name, has no depth, so no candidate
ever matches it and the declaration never moves anything. Nothing reported
that: the watched-pins lint only looks at values that already are a semver.

test_version_keys now fails on an `update:` block whose pinned key does not
hold a semver. A monitored addon the resolver does not read is held to the
same rule as soon as it pins a `version`, because the catalog check skips a
non-semver pin just as silently. An addon without a `version` keeps tracking
the app default and is left alone.

The Stalwart README no longer asks for `webui_version` to be bumped by hand
next to the image tag. Its `update:` block names the upstream tags, the
server itself follows `releases/latest` of the bundle, and the resolver
plans v1.0.5 to v1.0.11 for it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
kevinveenbirkenbachandClaude Opus 5.5 committed 2026-10-05 12:48:22 +02:00
1 parent 0ebfc9e58b
commit 4c955d080f
5 files changed
+77 -10

No files matched your search

@@ -220,5 +220,41 @@ class TestAddons(unittest.TestCase):
)
class TestSemverPins(unittest.TestCase):
def test_an_update_block_on_a_moving_tag_is_reported(self) -> None:
root = _repo(SERVICES.replace("app_version: 2.0.0", "app_version: latest"))
self.assertEqual(
module.invalid_declarations(root),
[
(
"web-app-example/app.app_version: 'latest' is not a semver, so "
"no upstream version orders above it and the pin never moves"
),
],
)
def test_a_monitored_addon_on_a_moving_tag_is_reported(self) -> None:
root = _addon_repo(
{
"moving": ADDONS["catalogued"].replace('"1.0.0"', '"master"'),
"pinned": ADDONS["catalogued"],
"unpinned": ADDONS["catalogued"].replace('version: "1.0.0"\n', ""),
"unmonitored": ADDONS["unmonitored"].replace('"1.0.0"', '"master"'),
}
)
self.assertEqual(
module.invalid_declarations(root),
[
(
"web-app-example/addons/moving.version: 'master' is not a "
"semver, so no upstream version orders above it and the pin "
"never moves"
),
],
)
if __name__ == "__main__":
unittest.main()