Files
computer-playbook/tests/unit/python/utils/update/test_docker_release_groups.py
T
kevinveenbirkenbachandClaude Opus 5 e66005ab07 fix(update): move a role's images as one release or not at all
Seven roles pin several images at one shared version, because those images
are one product: the four Jitsi components, six GitLab CNG images, eight
BigBlueButton services, three Penpot, plus Mastodon, Funkwhale and
Nextcloud Talk. The bump job walks entries one by one and takes each
image's own newest tag, so a run landing between upstream's first and last
push raises the components already there and leaves the rest behind. What
deploys afterwards is a mixed set, and for Jitsi a mixed set breaks video.

The window is real rather than theoretical. Measured on Docker Hub,
'stable-9457' exists for jitsi/prosody, jitsi/jicofo and jitsi/jvb but not
for jitsi/web, one build out of sixty.

Candidates are now grouped by role and current version, and the group is
held to the newest tag the intersection of all their tag lists still
offers. A group with nothing newer in common yields no update at all rather
than a partial one. A lone entry passes through untouched, so nothing
changes for the large majority.

The key is deliberately the role and the version, not the image namespace.
A namespace rule does not survive the data: prom/* carries four components
at four different versions, opentalk/* three, and minio, taigaio and
markoshust each two. Grouping by namespace would hold back pins that are
correctly independent.

Five of the seven roles have carried this exposure all along; the channel
grammar in the previous commit added the sixth by making Jitsi orderable at
all. This closes all of them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-17 17:45:28 +02:00

114 lines
3.6 KiB
Python

"""Several images of one role at one version move together or not at all.
Upstream does not push a release group atomically. Measured on Docker Hub:
``jitsi/prosody``, ``jitsi/jicofo`` and ``jitsi/jvb`` all publish
``stable-9457`` while ``jitsi/web`` does not, so a bump run landing in that
window would raise three components and leave the fourth behind.
"""
from __future__ import annotations
import unittest
from pathlib import Path
from utils.update.docker import (
DockerImageVersionEntry,
DockerImageVersionUpdate,
_held_to_release_groups,
)
JITSI = ("web", "prosody", "jicofo", "jvb")
def _entry(role: str, service: str, image: str, version: str):
return DockerImageVersionEntry(
role=role,
service=service,
image=image,
version=version,
config_path=Path("meta/services.yml"),
)
def _jitsi_updates(version: str, latest: str):
return [
DockerImageVersionUpdate(
entry=_entry("web-app-jitsi", name, f"jitsi/{name}", version),
latest=latest,
)
for name in JITSI
]
class TestReleaseGroupsMoveTogether(unittest.TestCase):
def test_a_partial_publish_holds_the_whole_group_back(self):
tags = {
"jitsi/web": ["stable-9400", "stable-9646"],
"jitsi/prosody": ["stable-9400", "stable-9646", "stable-9700"],
"jitsi/jicofo": ["stable-9400", "stable-9646", "stable-9700"],
"jitsi/jvb": ["stable-9400", "stable-9646", "stable-9700"],
}
updates = _jitsi_updates("stable-9400", "stable-9700")
held = _held_to_release_groups(updates, tags)
self.assertEqual({update.latest for update in held}, {"stable-9646"})
self.assertEqual(len(held), 4)
def test_a_group_all_publish_moves_to_the_head(self):
tags = {
f"jitsi/{name}": ["stable-9400", "stable-9700"] for name in JITSI
}
updates = _jitsi_updates("stable-9400", "stable-9700")
held = _held_to_release_groups(updates, tags)
self.assertEqual({update.latest for update in held}, {"stable-9700"})
def test_a_group_with_nothing_in_common_is_dropped(self):
tags = {
"jitsi/web": ["stable-9400", "stable-9701"],
"jitsi/prosody": ["stable-9400", "stable-9702"],
"jitsi/jicofo": ["stable-9400", "stable-9703"],
"jitsi/jvb": ["stable-9400", "stable-9704"],
}
updates = _jitsi_updates("stable-9400", "stable-9704")
self.assertEqual(_held_to_release_groups(updates, tags), [])
def test_a_lone_entry_is_untouched(self):
update = DockerImageVersionUpdate(
entry=_entry("web-app-fider", "fider", "getfider/fider", "v0.35.0"),
latest="v0.36.0",
)
held = _held_to_release_groups([update], {"getfider/fider": ["v0.36.0"]})
self.assertEqual(held, [update])
def test_two_roles_on_one_version_are_not_one_group(self):
tags = {
"jitsi/web": ["stable-9400", "stable-9700"],
"other/thing": ["stable-9400"],
}
updates = [
DockerImageVersionUpdate(
entry=_entry("web-app-jitsi", "web", "jitsi/web", "stable-9400"),
latest="stable-9700",
),
DockerImageVersionUpdate(
entry=_entry("web-app-other", "thing", "other/thing", "stable-9400"),
latest="stable-9400",
),
]
held = _held_to_release_groups(updates, tags)
self.assertEqual(
[update.latest for update in held], ["stable-9700", "stable-9400"]
)
if __name__ == "__main__":
unittest.main()