From cc5bbd2297b4706f92040a544aac2739f22e36f9 Mon Sep 17 00:00:00 2001 From: Valentin Haudiquet Date: Tue, 22 Sep 2026 13:00:40 +0200 Subject: [PATCH] chlog: drop a trailing buildN before appending ubuntu1 An Ubuntu upload of a package sitting at X-2build1 produced X-2build1ubuntu1: the blind append misrepresents the lineage and, sorting below the proper X-2ubuntu1, could never supersede it. A real change on top of a rebuild replaces the marker instead, so the trailing buildN is now stripped before the ubuntu counter is appended or incremented: X-2build1 becomes X-2ubuntu1, X-2ubuntu1build1 becomes X-2ubuntu2. --- src/changelog.rs | 67 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 2 deletions(-) diff --git a/src/changelog.rs b/src/changelog.rs index 10578a5..604c6cb 100644 --- a/src/changelog.rs +++ b/src/changelog.rs @@ -137,7 +137,9 @@ pub async fn generate_entry( enum Bump { /// Regular upload: increment the trailing number Normal, - /// Ubuntu upload: `1.0-9` becomes `1.0-9ubuntu1` + /// Ubuntu upload: `1.0-9` becomes `1.0-9ubuntu1`; a trailing + /// no-change-rebuild marker is dropped first (`1.0-9build1` becomes + /// `1.0-9ubuntu1`) Ubuntu, /// Non-maintainer upload: `1.0-1` becomes `1.0-1.1`, native `1.0` /// becomes `1.0+nmu1` @@ -158,7 +160,7 @@ fn compute_new_version( bump: Bump, ) -> Result> { match bump { - Bump::Ubuntu => increment_suffix(old_version, "ubuntu"), + Bump::Ubuntu => increment_suffix(strip_build_suffix(old_version), "ubuntu"), Bump::Rebuild => increment_suffix(old_version, "build"), Bump::Nmu => { if old_version.contains('-') { @@ -177,6 +179,19 @@ fn compute_new_version( } } +/// The version an Ubuntu upload is numbered from: a trailing +/// no-change-rebuild marker is dropped, because a real change on top of a +/// rebuild replaces the marker rather than appending to it — `X-2build1` +/// becomes `X-2ubuntu1`, where an appended `X-2build1ubuntu1` would +/// misrepresent the lineage and sort below `X-2ubuntu1` +fn strip_build_suffix(version: &str) -> &str { + let stem = version.trim_end_matches(|c: char| c.is_ascii_digit()); + match stem.strip_suffix("build") { + Some(base) if stem.len() < version.len() => &version[..base.len()], + _ => version, + } +} + /// The version bump a regular ([`EntryKind::Normal`]) upload gets, derived /// from the vendor of the target series: Ubuntu series number their uploads /// the Ubuntu way (`1.0-1` becomes `1.0-1ubuntu1`), everything else — @@ -1139,6 +1154,22 @@ mod tests { compute_new_version("15.2.0-9ubuntu1", Bump::Ubuntu).unwrap(), "15.2.0-9ubuntu2" ); + // Ubuntu upload on top of a rebuild drops the buildN marker: + // appending would give 15.2.0-9build1ubuntu1, which sorts below + // the proper 15.2.0-9ubuntu1 + assert_eq!( + compute_new_version("15.2.0-9build1", Bump::Ubuntu).unwrap(), + "15.2.0-9ubuntu1" + ); + assert_eq!( + compute_new_version("15.2.0-9ubuntu1build1", Bump::Ubuntu).unwrap(), + "15.2.0-9ubuntu2" + ); + // Native packages + assert_eq!( + compute_new_version("15.2.0build1", Bump::Ubuntu).unwrap(), + "15.2.0ubuntu1" + ); // No change rebuild assert_eq!( @@ -1395,6 +1426,38 @@ mod tests { assert_eq!(entry.new_version, "1.0-1ubuntu2"); } + /// An Ubuntu upload of a package whose changelog carries a rebuild + /// version drops the buildN marker: 1.0-1build1 numbers the next entry + /// 1.0-1ubuntu1 (1.0-1build1ubuntu1 would sort below 1.0-1ubuntu1). + /// Relies on the host distro-info data listing noble (see the + /// distro_info tests). The git repo provides the maintainer identity: + /// DEBFULLNAME/DEBEMAIL are process-global and other tests mutate them + /// in parallel. + #[tokio::test] + async fn test_generate_entry_ubuntu_series_numbering_after_rebuild() { + let temp_dir = TempDir::new().unwrap(); + let repo_dir = temp_dir.path(); + setup_repo(repo_dir); + let changelog_path = repo_dir.join("debian/changelog"); + std::fs::create_dir_all(repo_dir.join("debian")).unwrap(); + std::fs::write( + &changelog_path, + "mypackage (1.0-1build1) noble; urgency=medium\n\n * Initial release\n\n -- Maintainer Wed, 01 Jan 2020 00:00:00 +0000\n", + ) + .unwrap(); + + let entry = generate_entry( + "debian/changelog", + Some(repo_dir), + None, + Some("noble"), + EntryKind::Normal, + ) + .await + .unwrap(); + assert_eq!(entry.new_version, "1.0-1ubuntu1"); + } + #[test] fn test_get_maintainer_info() { let _identity = IDENTITY_LOCK.blocking_lock();