diff --git a/python3/BUILD b/python3/BUILD index bbd60e351..1c0da9c3c 100644 --- a/python3/BUILD +++ b/python3/BUILD @@ -2,6 +2,7 @@ load("//private/util:tar.bzl", "tar") load(":config.bzl", "PYTHON_ARCHITECTURES", "PYTHON_DISTROS", "PYTHON_PACKAGES") load(":ldconfig.bzl", "python3_ldconfig") load(":python.bzl", "python3_image", "python3_image_index") +load(":version_test.bzl", "clean_python_version_test") package(default_visibility = ["//visibility:public"]) @@ -42,3 +43,5 @@ python3_ldconfig( architectures = PYTHON_ARCHITECTURES["debian13"], distro = "debian13", ) + +clean_python_version_test(name = "clean_python_version_test") diff --git a/python3/python.bzl b/python3/python.bzl index 022b62ac1..6f2343ab7 100644 --- a/python3/python.bzl +++ b/python3/python.bzl @@ -5,6 +5,7 @@ load("@rules_oci//oci:defs.bzl", "oci_image", "oci_image_index") load("//common:variables.bzl", "DEBUG_MODE", "OS_RELEASE", "USERS") load("//private/util:deb.bzl", "deb") load("//private/util:tar.bzl", "tar") +load(":version.bzl", "clean_python_version") DISTRO_VERSION = { "debian13": "3.13", @@ -50,6 +51,13 @@ def python3_image(distro, arch, packages): """ python_alias(distro) + _python_version = clean_python_version(deb.version( + arch, + distro, + "python" + DISTRO_VERSION[distro] + "-minimal", + "python", + )) + for mode in DEBUG_MODE: for user in USERS: oci_image( @@ -65,7 +73,10 @@ def python3_image(distro, arch, packages): deb.package(arch, distro, pkg, "python") for pkg in packages ] + [":python_aliases_%s" % distro] + ([":ldconfig_cache_" + arch] if distro == "debian13" else []), - annotations = {"org.opencontainers.image.source": OS_RELEASE["HOME_URL"]}, + annotations = { + "org.opencontainers.image.source": OS_RELEASE["HOME_URL"], + "com.google.distroless.python.version": _python_version, + }, ) for user in USERS: diff --git a/python3/version.bzl b/python3/version.bzl new file mode 100644 index 000000000..a639b0c95 --- /dev/null +++ b/python3/version.bzl @@ -0,0 +1,23 @@ +"Python version helper functions" + +def clean_python_version(version_string): + """Clean Debian package version to get upstream Python version. + + Handles epochs (prefix before first ':') and Debian revisions (suffix after last '-'). + """ + + # Handle epoch (split at first ':') + epoch_parts = version_string.partition(":") + if epoch_parts[1]: # separator found + version_string = epoch_parts[2] + else: + version_string = epoch_parts[0] + + # Handle revision (split at last '-') + rev_parts = version_string.rpartition("-") + if rev_parts[1]: # separator found + version_string = rev_parts[0] + else: + version_string = rev_parts[2] + + return version_string diff --git a/python3/version_test.bzl b/python3/version_test.bzl new file mode 100644 index 000000000..ef32b7e1f --- /dev/null +++ b/python3/version_test.bzl @@ -0,0 +1,33 @@ +load("@bazel_skylib//lib:unittest.bzl", "asserts", "unittest") +load(":version.bzl", "clean_python_version") + +def _clean_python_version_test_impl(ctx): + env = unittest.begin(ctx) + + # Standard Debian version: upstream-revision + asserts.equals(env, "3.13.5", clean_python_version("3.13.5-2")) + + # With epoch: epoch:upstream-revision + asserts.equals(env, "3.13.5", clean_python_version("1:3.13.5-2")) + + # With epoch and complex revision (e.g. backport) + asserts.equals(env, "3.13.5", clean_python_version("1:3.13.5-2+deb13u3")) + + # Raw upstream only (no revision, no epoch) + asserts.equals(env, "3.13.5", clean_python_version("3.13.5")) + + # Upstream containing hyphens (should preserve hyphens in upstream, split at last hyphen) + asserts.equals(env, "3.13.5-rc1", clean_python_version("3.13.5-rc1-1")) + + # Upstream containing hyphens and epoch + asserts.equals(env, "3.13.5-rc1", clean_python_version("1:3.13.5-rc1-1")) + + # Upstream containing colons (unlikely but test robustness) + # partition(":")[:] should handle it if we only split at first colon + asserts.equals(env, "3.13:5", clean_python_version("1:3.13:5-1")) + + return unittest.end(env) + +clean_python_version_test = unittest.make( + impl = _clean_python_version_test_impl, +)