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
13 changes: 10 additions & 3 deletions src/commands/ext/package.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down
24 changes: 24 additions & 0 deletions src/utils/version.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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('+', "^")
}
Comment on lines +82 to +84

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -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");
}
Comment on lines +143 to +152

#[test]
fn test_check_cli_requirement_invalid_syntax() {
let result = check_cli_requirement("not-a-requirement");
Expand Down
Loading