diff --git a/src/commands/ext/package.rs b/src/commands/ext/package.rs index 65a7382..e69f5ac 100644 --- a/src/commands/ext/package.rs +++ b/src/commands/ext/package.rs @@ -560,10 +560,17 @@ impl ExtPackageCommand { format!("/opt/src/{ext_src_dir}") }; - // Create the RPM filename + // RPM forbids `-` in Version (it is the Version/Release separator), so map + // the semver version to its RPM form (`1.0.0-rc.1` -> `1.0.0~rc.1`). Used + // for the spec `Version:` and the built RPM's name; the semver form is kept + // for the avocado.yaml baked into the payload (consumers validate semver). + let rpm_version = crate::utils::version::to_rpm_version(&metadata.version); + + // Create the RPM filename (matches the built RPM's NVR, so it uses the + // RPM-form version, not the semver form). let rpm_filename = format!( "{}-{}-{}.{}.rpm", - metadata.name, metadata.version, metadata.release, metadata.arch + metadata.name, rpm_version, metadata.release, metadata.arch ); // Convert package_files to a space-separated string for the shell script @@ -772,7 +779,7 @@ echo "RPM created successfully: $AVOCADO_PREFIX/output/extensions/{rpm_filename} rm -rf "$TMPDIR" "#, name = metadata.name, - version = metadata.version, + version = rpm_version, release = metadata.release, summary = metadata.summary, license = metadata.license, diff --git a/src/utils/version.rs b/src/utils/version.rs index 1020d69..a8a880f 100644 --- a/src/utils/version.rs +++ b/src/utils/version.rs @@ -71,6 +71,18 @@ pub fn check_cli_requirement(requirement: &str) -> Result<()> { Ok(()) } +/// Convert a semver version string into an RPM-compatible `Version:` value. +/// +/// RPM forbids `-` in the Version field (it is the Version/Release separator), +/// so a semver pre-release like `1.0.0-rc.1` is illegal and `rpmbuild` rejects +/// it. RPM uses `~` for pre-release ordering — `1.0.0~rc.1` sorts *before* +/// `1.0.0`, matching semver pre-release precedence — and `^` for post-release, +/// so map the pre-release `-` to `~` and the build-metadata `+` to `^`. A plain +/// release version (no `-`/`+`) is returned unchanged. +pub fn to_rpm_version(version: &str) -> String { + version.replace('-', "~").replace('+', "^") +} + #[cfg(test)] mod tests { use super::*; @@ -127,6 +139,18 @@ mod tests { assert!(check_cli_requirement("*").is_ok()); } + #[test] + fn test_to_rpm_version() { + // Plain release versions are unchanged. + assert_eq!(to_rpm_version("1.0.0"), "1.0.0"); + assert_eq!(to_rpm_version("2.1.3"), "2.1.3"); + // Pre-release `-` becomes `~` (sorts before the release in RPM). + assert_eq!(to_rpm_version("1.0.0-rc.1"), "1.0.0~rc.1"); + assert_eq!(to_rpm_version("1.0.0-alpha.2"), "1.0.0~alpha.2"); + // Build metadata `+` becomes `^`. + assert_eq!(to_rpm_version("1.0.0+build.5"), "1.0.0^build.5"); + } + #[test] fn test_check_cli_requirement_invalid_syntax() { let result = check_cli_requirement("not-a-requirement");