Skip to content

Commit 24dae84

Browse files
committed
Fix version-aware image tag selection
1 parent af463d9 commit 24dae84

2 files changed

Lines changed: 53 additions & 12 deletions

File tree

llmdbenchmark/parser/version_resolver.py

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Resolve ``"auto"`` image tags and chart versions via skopeo/helm."""
22

33
import json
4+
import re
45
import subprocess
56
from copy import deepcopy
67

@@ -20,6 +21,19 @@ class VersionResolver:
2021
def __init__(self, logger, dry_run: bool = False):
2122
self.logger = logger
2223

24+
@staticmethod
25+
def _latest_version_tag(tags: list[str]) -> str | None:
26+
"""Select the greatest tag using natural version-aware ordering."""
27+
28+
def version_key(tag: str) -> tuple:
29+
return tuple(
30+
(1, int(part), len(part)) if part.isdigit() else (0, part.casefold())
31+
for part in re.split(r"(\d+)", tag)
32+
if part
33+
)
34+
35+
return max(tags, key=version_key) if tags else None
36+
2337
def resolve_image_tag(self, registry: str, repository: str) -> str:
2438
"""Resolve the latest tag for an image via skopeo, falling back to podman."""
2539
image_ref = repository
@@ -61,23 +75,24 @@ def _resolve_via_crane(self, image_ref: str) -> str | None:
6175
for line in result.stdout.strip().split("\n")
6276
if line.strip()
6377
]
64-
if lines:
65-
return lines[-1]
78+
return self._latest_version_tag(lines)
6679
except FileNotFoundError:
6780
pass
6881
return None
6982

7083
def _resolve_via_skopeo(self, image_ref: str) -> str | None:
7184
"""Resolve latest tag using skopeo list-tags."""
72-
cmd = f"skopeo list-tags docker://{image_ref} | jq -r '.Tags[]' | sort -V"
85+
cmd = ["skopeo", "list-tags", f"docker://{image_ref}"]
7386
try:
7487
result = subprocess.run(
75-
cmd.split(), capture_output=True, text=True, check=True
88+
cmd, capture_output=True, text=True, check=True
7689
)
7790
tags_data = json.loads(result.stdout)
7891
tags = tags_data.get("Tags", [])
79-
if tags:
80-
return tags[-1]
92+
if isinstance(tags, list):
93+
return self._latest_version_tag(
94+
[tag for tag in tags if isinstance(tag, str) and tag]
95+
)
8196
except (subprocess.CalledProcessError, json.JSONDecodeError, FileNotFoundError):
8297
pass
8398
return None
@@ -90,12 +105,12 @@ def _resolve_via_podman(self, image_ref: str) -> str | None:
90105
cmd.split(), capture_output=True, text=True, check=False
91106
)
92107
if result.returncode == 0:
93-
lines = result.stdout.strip().split("\n")
94-
if lines:
95-
last_line = lines[-1]
96-
parts = last_line.split()
97-
if len(parts) >= 2:
98-
return parts[1]
108+
tags = []
109+
for line in result.stdout.strip().split("\n"):
110+
parts = line.split()
111+
if len(parts) >= 2 and parts[1].casefold() != "tag":
112+
tags.append(parts[1])
113+
return self._latest_version_tag(tags)
99114
except FileNotFoundError:
100115
pass
101116
return None

tests/test_version_resolver.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
from __future__ import annotations
88

9+
import json
10+
from subprocess import CompletedProcess
911
from typing import Any
1012

1113
import pytest
@@ -75,6 +77,30 @@ def _images() -> dict:
7577
}
7678

7779

80+
# ---------------------------------------------------------------------------
81+
# Registry tag ordering
82+
# ---------------------------------------------------------------------------
83+
84+
85+
class TestRegistryTagOrdering:
86+
def test_skopeo_selects_latest_tag_by_version(
87+
self, monkeypatch: pytest.MonkeyPatch
88+
) -> None:
89+
tags = ["v0.20.1", "v0.9.2", "v0.10.0"]
90+
91+
def _run(*args: Any, **kwargs: Any) -> CompletedProcess[str]:
92+
return CompletedProcess(args[0], 0, stdout=json.dumps({"Tags": tags}))
93+
94+
monkeypatch.setattr(
95+
"llmdbenchmark.parser.version_resolver.subprocess.run", _run
96+
)
97+
resolver = VersionResolver(_StubLogger())
98+
99+
assert resolver._resolve_via_skopeo("docker.io/vllm/vllm-openai") == (
100+
"v0.20.1"
101+
)
102+
103+
78104
# ---------------------------------------------------------------------------
79105
# imageKey expansion
80106
# ---------------------------------------------------------------------------

0 commit comments

Comments
 (0)