Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions .github/scripts/current_latest_version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#!/usr/bin/env python3
# SPDX-FileCopyrightText: 2025-present Amazon.com, Inc. or its affiliates.
#
# SPDX-License-Identifier: Apache-2.0
from __future__ import annotations

import argparse
import re

from packaging.version import InvalidVersion, Version

# Exactly vX.Y.Z with an optional PEP 440 suffix (rc1, -beta, .post1, +local),
# matching what this workflow publishes. A fourth numeric component (v1.2.3.4)
# is rejected: the suffix, if any, must not start with a dot followed by a digit.
TAG_RE = re.compile(r"^v[0-9]+\.[0-9]+\.[0-9]+(?![.0-9])")


def current_latest_version(tags_on_latest: list[str]) -> str:
"""Return the highest vX.Y.Z tag sharing the latest digest, or empty.

Empty means latest does not resolve to a version we recognize, whether it
carries no version tag or only off-grammar ones (a scheme change or a
hand-pushed tag). The caller treats empty as invalid and refuses to move
latest, since it cannot prove it is newer. If one digest carries several
version tags the max is returned, keeping the monotonic guard conservative.
"""
highest: Version | None = None
highest_tag = ""
for tag in tags_on_latest:
if not TAG_RE.match(tag):
continue
try:
version = Version(tag[1:])
except InvalidVersion:
continue
if highest is None or version > highest:
highest = version
highest_tag = tag
return highest_tag


def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(
description="Print the version tag latest currently resolves to."
)
parser.add_argument(
"tags", nargs="*", help="Tags carried by the latest digest, e.g. v1.2.1 latest"
)
args = parser.parse_args(argv)

print(current_latest_version(args.tags))
return 0


if __name__ == "__main__":
raise SystemExit(main())
38 changes: 38 additions & 0 deletions .github/scripts/parse_testing_version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/usr/bin/env python3
# SPDX-FileCopyrightText: 2025-present Amazon.com, Inc. or its affiliates.
#
# SPDX-License-Identifier: Apache-2.0
from __future__ import annotations

import os
import re

# A release-tag component naming the testing package: testing-v<x.y.z> with an
# optional pre-release suffix. Anchored and matched against a single comma-split
# component so prefixes like "mytesting-v1.2.1" are rejected.
_COMPONENT = re.compile(r"testing-v([0-9]+\.[0-9]+\.[0-9]+[0-9A-Za-z.-]*)\Z")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The two scripts accept different tag grammars. parse_testing_version.py accepts testing-v1.2.3.4 and testing-v1.2.3-x86_64. current_latest_version.py rejects both.

  • A v1.2.3.4 release would move latest to a tag that current_latest_version.py cannot read. Every later release would then refuse to update latest.
  • A testing-v1.2.3-x86_64 tag would publish a version tag with the same name as an existing per-architecture image tag.

Please could both scripts share one regex, and could you add a testing-v1.2.3.4 test case?



def parse_testing_version(release_tag: str) -> str:
"""Return the testing version named by the release tag, or empty if none."""
for part in release_tag.split(","):
match = _COMPONENT.fullmatch(part.strip())
if match:
return match.group(1)
return ""


def main():
release_tag = os.environ.get("RELEASE_TAG", "")
tag_version = parse_testing_version(release_tag)

github_output = os.environ.get("GITHUB_OUTPUT")
if github_output:
with open(github_output, "a", encoding="utf-8") as f:
f.write(f"tag_version={tag_version}\n")

print(tag_version)


if __name__ == "__main__":
main()
68 changes: 68 additions & 0 deletions .github/scripts/tests/test_current_latest_version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#!/usr/bin/env python3
# SPDX-FileCopyrightText: 2025-present Amazon.com, Inc. or its affiliates.
#
# SPDX-License-Identifier: Apache-2.0
from __future__ import annotations

import sys
from pathlib import Path

import pytest

sys.path.insert(0, str(Path(__file__).resolve().parent.parent))

from current_latest_version import current_latest_version

CASES = [
# No tags, or no recognized version on the digest -> empty
([], ""),
(["latest"], ""),
(["latest", "some-branch"], ""),
(["nightly"], ""),
# Single version tag alongside latest
(["v1.2.3", "latest"], "v1.2.3"),
# Highest wins when several version tags share the digest
(["v1.2.3", "v1.3.0", "latest"], "v1.3.0"),
# Pre-release orders below its release
(["v2.0.0rc1", "v2.0.0"], "v2.0.0"),
(["v2.0.0rc1"], "v2.0.0rc1"),
# Original tag text preserved, not normalized
(["v2.0.0-beta"], "v2.0.0-beta"),
# Off-grammar tags are not recognized -> empty (caller fails closed)
(["v1"], ""),
(["v1.2"], ""),
(["v999"], ""),
(["v2025.09"], ""),
# A fourth numeric component is not our grammar -> empty
(["v1.2.3.4"], ""),
(["v999.0.0.1"], ""),
(["v1.2.3-x86_64", "v1.2.3-arm64"], ""),
# A good tag alongside a junk one still resolves cleanly
(["v1", "v1.2.3"], "v1.2.3"),
(["v1.2.3.4", "v1.2.3"], "v1.2.3"),
]


@pytest.mark.parametrize("tags,expected", CASES)
def test_current_latest_version(tags, expected):
assert current_latest_version(tags) == expected


def _run_standalone() -> int:
failures = 0
for tags, expected in CASES:
got = current_latest_version(tags)
if got != expected:
failures += 1
print(
f"FAIL: current_latest_version({tags!r}) = {got!r}, expected {expected!r}"
)
if failures:
print(f"{failures} failing case(s)")
return 1
print(f"all {len(CASES)} cases passed")
return 0


if __name__ == "__main__":
raise SystemExit(_run_standalone())
48 changes: 48 additions & 0 deletions .github/scripts/tests/test_parse_testing_version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#!/usr/bin/env python3

import os
import sys

sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))

from parse_testing_version import parse_testing_version


def test_parse_testing_version():

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this use @pytest.mark.parametrize, like test_current_latest_version.py? The _run_standalone runner in that file can also go, since test-parser.yml runs pytest.

test_cases = [
# Testing-only tag enables the job
("testing-v2.0.0", "2.0.0"),
("testing-v1.2.1", "1.2.1"),
# Combined comma-separated tags resolve to the testing version
("sdk-v2.0.0,testing-v2.0.0", "2.0.0"),
("testing-v2.0.0,sdk-v2.1.0", "2.0.0"),
("otel-v1.0.0,testing-v1.2.1,sdk-v2.0.0", "1.2.1"),
# SDK-only or OTel-only tags do not enable the job
("sdk-v2.1.0", ""),
("otel-v1.0.0", ""),
("sdk-v2.0.0,otel-v1.0.0", ""),
# Malformed or unrelated prefixes must not match
("not-testing-v1.2.1", ""),
("sdk-v2.0.0,mytesting-v1.2.1", ""),
("testing-v1.2", ""),
("testing-version-1.2.1", ""),
# No release tag
("", ""),
("v2.0.0", ""),
("random-text", ""),
# Pre-release suffix is kept
("testing-v2.0.0rc1", "2.0.0rc1"),
("testing-v2.0.0-beta,sdk-v1.0.0", "2.0.0-beta"),
]

for input_text, expected in test_cases:
result = parse_testing_version(input_text)
# Assert is expected in test functions
assert result == expected, ( # noqa: S101
f"Expected '{expected}' but got '{result}' for input: {input_text}"
)


if __name__ == "__main__":
test_parse_testing_version()
sys.exit(0)
Loading
Loading