From 0fc1d82365db0c1470d21a0570da50892769e8b6 Mon Sep 17 00:00:00 2001 From: Emil Simeonov Date: Sat, 19 Sep 2026 04:18:36 +0200 Subject: [PATCH] feat(ci): add --container-tag to decouple image tag from release version --- src/devx/ci/create_dependency_pr.py | 17 ++++++++++--- src/devx/translations.json | 8 +++++++ tests/unit/test_create_dependency_pr.py | 32 +++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 3 deletions(-) diff --git a/src/devx/ci/create_dependency_pr.py b/src/devx/ci/create_dependency_pr.py index a3e83d2..1953482 100644 --- a/src/devx/ci/create_dependency_pr.py +++ b/src/devx/ci/create_dependency_pr.py @@ -233,6 +233,15 @@ def create_vikunja_task(title: str, description: str) -> str | None: default="", help=_("Git ref of the producer release (default: v), recorded in the manifest."), ) +@click.option( + "--container-tag", + default="", + help=_( + "Container tag to verify with --verify-container (default: --new-version). " + "Use when the image tag differs from the release version, e.g. a package " + "__version__ tag vs a release git tag." + ), +) @click.option("--dry-run", is_flag=True, default=False, help=_("Show what would be done without creating PR")) def cli( repo: str, @@ -243,6 +252,7 @@ def cli( manifest_path: str, verify_container: str, source_ref: str, + container_tag: str, dry_run: bool, ) -> None: """Create an infra PR to bump a pinned dependency version.""" @@ -261,12 +271,13 @@ def cli( _("Invalid container format: {container} (expected owner/name)", container=verify_container) ) c_owner, c_name = verify_container.split("/", 1) - image_digest = resolve_container_digest(GITEA_API_URL, c_owner, c_name, new_version, token) + image_tag = container_tag or new_version + image_digest = resolve_container_digest(GITEA_API_URL, c_owner, c_name, image_tag, token) click.echo( _( "[dep-pr] Verified {container}:{version} -> {digest}", container=verify_container, - version=new_version, + version=image_tag, digest=image_digest, ) ) @@ -341,7 +352,7 @@ def cli( fields = { "version": new_version, "git_ref": source_ref or f"v{new_version}", - "image_tag": new_version, + "image_tag": container_tag or new_version, "updated_at": datetime.now(UTC).strftime("%Y-%m-%dT%H:%M:%SZ"), } if image_digest: diff --git a/src/devx/translations.json b/src/devx/translations.json index 6e31c4f..ef1b95d 100644 --- a/src/devx/translations.json +++ b/src/devx/translations.json @@ -1351,6 +1351,14 @@ "ru": "Артефакт контейнера {owner}/{name}:{tag} не найден или недоступен для чтения (HTTP {status}). Отказ открывать PR зависимости для неопубликованного артефакта.", "zh": "容器构件 {owner}/{name}:{tag} 未找到或不可读 (HTTP {status})。拒绝为未发布的构件创建依赖 PR。" }, + "Container tag to verify with --verify-container (default: --new-version). Use when the image tag differs from the release version, e.g. a package __version__ tag vs a release git tag.": { + "bg": "Таг на контейнер за проверка с --verify-container (по подразбиране: --new-version). Използвайте, когато тагът на изображението се различава от версията на изданието, напр. таг на __version__ на пакет срещу git таг на издание.", + "de": "Container-Tag, der mit --verify-container geprüft wird (Standard: --new-version). Zu verwenden, wenn sich das Image-Tag von der Release-Version unterscheidet, z. B. ein __version__-Tag eines Pakets vs. ein Release-Git-Tag.", + "en": "Container tag to verify with --verify-container (default: --new-version). Use when the image tag differs from the release version, e.g. a package __version__ tag vs a release git tag.", + "pl": "Tag kontenera do weryfikacji z --verify-container (domyślnie: --new-version). Użyj, gdy tag obrazu różni się od wersji wydania, np. tag __version__ pakietu a tag git wydania.", + "ru": "Тег контейнера для проверки с --verify-container (по умолчанию: --new-version). Используйте, когда тег образа отличается от версии релиза, например тег __version__ пакета против git-тега релиза.", + "zh": "用 --verify-container 验证的容器标签(默认:--new-version)。当镜像标签与发布版本不同时使用,例如包的 __version__ 标签与发布 git 标签。" + }, "Container to verify before opening the PR (owner/name). Resolves the OCI digest of the tag matching --new-version and records it in the manifest.": { "bg": "Контейнер за проверка преди отваряне на PR (owner/name). Разрешава OCI дайджеста на тага, съвпадащ с --new-version, и го записва в манифеста.", "de": "Container, der vor dem Öffnen des PR verifiziert wird (owner/name). Ermittelt den OCI-Digest des zu --new-version passenden Tags und trägt ihn ins Manifest ein.", diff --git a/tests/unit/test_create_dependency_pr.py b/tests/unit/test_create_dependency_pr.py index b1edb62..46f2745 100644 --- a/tests/unit/test_create_dependency_pr.py +++ b/tests/unit/test_create_dependency_pr.py @@ -421,3 +421,35 @@ class TestCliManifestMode: ], ) assert result.exit_code != 0 + + @patch("devx.ci.create_dependency_pr.resolve_container_digest") + @patch("devx.ci.create_dependency_pr.read_manifest_version") + @patch("devx.ci.create_dependency_pr.get_ci_token") + def test_container_tag_overrides_new_version( + self, mock_token: MagicMock, mock_read: MagicMock, mock_digest: MagicMock + ) -> None: + """--container-tag selects the image tag when it differs from version.""" + mock_token.return_value = "fake-token" + mock_read.return_value = "0.9.1" + mock_digest.return_value = "sha256:abc" + runner = CliRunner() + result = runner.invoke( + cli, + [ + "--package", + "sso_bridge", + "--new-version", + "0.9.1", + "--source-repo", + "oblachno/sso-bridge", + "--manifest", + "deploy/m.json", + "--verify-container", + "oblachno/sso-bridge", + "--container-tag", + "0.2.4", + ], + ) + assert result.exit_code == 0 + args = mock_digest.call_args[0] + assert args[1:4] == ("oblachno", "sso-bridge", "0.2.4")