Probe the RPM filename nfpm actually publishes (#175)
ci/woodpecker/push/deploy-fedora43 Pipeline was successful
ci/woodpecker/push/deploy-fedora44 Pipeline was successful
ci/woodpecker/push/deploy-fedora42 Pipeline was successful
ci/woodpecker/push/deploy-almalinux8 Pipeline was successful
ci/woodpecker/push/deploy-almalinux9 Pipeline was successful

nfpm re-renders semver versions into the RPM filename, so `2025.08.03` ships as `2025.8.3`. The existence probe asked artifactapi for the raw metadata version, always got a 404, and rebuilt and republished `nzbget_exporter` and `unkin-ca-certificates` on every pipeline run.

- add `nfpm_rpm_version`/`rpm_file_name` mirroring nfpm rendering
- build the probe filename through `rpm_file_name`
- drop dead `normalize_version` and `get_package_full_name`
- cover the nfpm rendering table and the probed URL in tests

Reviewed-on: #175
Co-authored-by: unkin-agent <unkin-agent@unkin.net>
Co-committed-by: unkin-agent <unkin-agent@unkin.net>
This commit was merged in pull request #175.
This commit is contained in:
2026-09-28 22:22:42 +10:00
committed by BenVincent
parent 1c316e875a
commit df7490388e
3 changed files with 114 additions and 39 deletions
+33 -39
View File
@@ -517,37 +517,38 @@ def get_github_token() -> str:
# ==================== GITEA API FUNCTIONS ====================
def normalize_version(version: str) -> str:
def nfpm_rpm_version(version: str) -> str:
"""
Normalize version string by removing leading zeros from numeric components.
Gitea automatically does this normalization.
Render a metadata version the way nfpm renders it into an RPM filename.
nfpm re-renders any semver-parseable version: leading zeros are dropped,
missing components padded to three, and the prerelease joined with '~'.
Anything semver cannot parse (four or more components, non-numeric
components, a numeric prerelease identifier with a leading zero) is used
verbatim.
Examples:
"2025.08.03" -> "2025.8.3"
"1.05.0" -> "1.5.0"
"0.6.1" -> "0.6.1" (no change needed)
Args:
version: Original version string
Returns:
Normalized version string
"1.05" -> "1.5.0"
"1.2.3-rc1" -> "1.2.3~rc1"
"1.02.3.4" -> "1.02.3.4" (not semver, left alone)
"""
import re
core, sep, prerelease = version.partition('-')
if sep and not prerelease:
return version
# Split by common separators and normalize each numeric part
parts = re.split(r'([.\-_])', version)
normalized_parts = []
components = core.split('.')
if len(components) > 3 or not all(c.isdigit() for c in components):
return version
for part in parts:
# If this part is purely numeric and has leading zeros, remove them
if part.isdigit() and len(part) > 1 and part.startswith('0'):
# Remove leading zeros but keep at least one digit
normalized_parts.append(str(int(part)))
else:
normalized_parts.append(part)
# semver rejects numeric prerelease identifiers with a leading zero
if any(i.isdigit() and len(i) > 1 and i.startswith('0')
for i in prerelease.split('.')):
return version
return ''.join(normalized_parts)
components += ['0'] * (3 - len(components))
rendered = '.'.join(str(int(c)) for c in components)
return f"{rendered}~{prerelease}" if prerelease else rendered
def get_rpm_dist_tag(distro: str) -> str:
@@ -598,6 +599,14 @@ def get_rpm_arch(arch: str) -> str:
return {'amd64': 'x86_64', 'arm64': 'aarch64'}.get(arch, arch)
def rpm_file_name(package_name: str, version: str, release: str, arch: str) -> str:
"""Filename nfpm produces for this package, and therefore the published name."""
return (
f"{package_name}-{nfpm_rpm_version(version)}-{release}"
f".{get_rpm_arch(arch)}.rpm"
)
def check_package_exists(
package_name: str,
version: str,
@@ -627,7 +636,7 @@ def check_package_exists(
base_url = os.getenv('ARTIFACTAPI_URL', 'https://artifactapi.k8s.syd1.au.unkin.net')
repo = get_vendor_repo(distro)
rpm_file = f"{package_name}-{version}-{release}.{get_rpm_arch(arch)}.rpm"
rpm_file = rpm_file_name(package_name, version, release, arch)
url = f"{base_url}/api/v2/remotes/{repo}/files/Packages/{rpm_file}"
try:
@@ -651,21 +660,6 @@ def check_package_exists(
return False
def get_package_full_name(package_name: str, version: str, release: str) -> str:
"""
Generate the full package name as used in the registry.
Args:
package_name: Package name
version: Version string
release: Release number
Returns:
Full package name string
"""
return f"{package_name}-{version}-{release}"
# ==================== DOCKER FUNCTIONS ====================
def check_docker_available() -> bool: