diff --git a/src/build/binary.rs b/src/build/binary.rs
index 1867d82..72828a9 100644
--- a/src/build/binary.rs
+++ b/src/build/binary.rs
@@ -130,22 +130,31 @@ pub fn generate_binary_metadata(
let mut source_display = entry.source.clone();
let mut binary_only_changes = None;
- if entry.binary_only
- && let Ok(prev_entry) = crate::debian::changelog::parse_previous_version_from_str(
- &ctx.read_file(&package_dir.join("debian/changelog"))?,
- )
- && let Some(prev) = prev_entry
- {
- source_display = format!("{} ({})", entry.source, prev);
- binary_only_changes = Some(format!(
- "{}\n\n -- {} <{}> {}",
- entry.changes_field, entry.maintainer_name, entry.maintainer_email, entry.date_raw
- ));
- let prev_version = crate::debian::DebianVersion::parse(&prev)?;
- let dsc_name = format!("{}_{}.dsc", entry.source, prev_version.no_epoch());
- let dsc_path = upload_dir.join(&dsc_name);
- if ctx.exists(&dsc_path)? {
- include_dsc_artifacts(ctx, upload_dir, &dsc_name, &mut checksums)?;
+ if entry.binary_only {
+ // A binary-only upload must reference the previous source version;
+ // a changelog that cannot yield it is a hard error, like in the
+ // source-build path. Reuse the changelog read above instead of
+ // reading the file a second time.
+ let changelog_path = package_dir.join("debian/changelog");
+ let prev = crate::debian::changelog::parse_previous_version_from_str(&changelog_content)
+ .map_err(|e| {
+ format!(
+ "cannot parse the previous version from '{}': {e}",
+ changelog_path.display()
+ )
+ })?;
+ if let Some(prev) = prev {
+ source_display = format!("{} ({})", entry.source, prev);
+ binary_only_changes = Some(format!(
+ "{}\n\n -- {} <{}> {}",
+ entry.changes_field, entry.maintainer_name, entry.maintainer_email, entry.date_raw
+ ));
+ let prev_version = crate::debian::DebianVersion::parse(&prev)?;
+ let dsc_name = format!("{}_{}.dsc", entry.source, prev_version.no_epoch());
+ let dsc_path = upload_dir.join(&dsc_name);
+ if ctx.exists(&dsc_path)? {
+ include_dsc_artifacts(ctx, upload_dir, &dsc_name, &mut checksums)?;
+ }
}
}
@@ -580,4 +589,65 @@ Files:
assert_eq!(tar_entry.sha1, "aaa111");
assert_eq!(tar_entry.sha256, "bbb222");
}
+
+ /// A binary-only (binNMU) build whose changelog cannot yield the
+ /// previous version (malformed second header, unbalanced parenthesis)
+ /// must fail the metadata generation with a diagnostic naming the
+ /// changelog, instead of silently emitting a plain `Source:` `.changes`
+ /// with no `Binary-Only-Changes` and no redistributed previous `.dsc`.
+ #[test]
+ fn binary_only_prev_version_parse_failure_errors_instead_of_wrong_metadata() {
+ let changelog = "\
+hello (1.0-1+b1) unstable; urgency=medium, binary-only=yes
+
+ * Binary-only rebuild.
+
+ -- A B Mon, 01 Jan 2024 00:00:00 +0000
+
+hello (1.0-1 unstable; urgency=medium
+
+ * Previous entry with an unbalanced parenthesis.
+
+ -- A B Sun, 31 Dec 2023 00:00:00 +0000
+";
+ let control = "\
+Source: hello
+Section: devel
+Priority: optional
+Maintainer: A B
+
+Package: hello
+Architecture: all
+Description: test package
+";
+ let base = tempfile::tempdir().expect("tempdir");
+ let tree = base.path().join("hello-1.0");
+ std::fs::create_dir_all(tree.join("debian")).expect("mkdir tree");
+ std::fs::write(tree.join("debian/changelog"), changelog).expect("write changelog");
+ std::fs::write(tree.join("debian/control"), control).expect("write control");
+ std::fs::write(
+ tree.join("debian/files"),
+ "hello_1.0-1+b1_all.deb devel optional\n",
+ )
+ .expect("write files");
+ std::fs::write(base.path().join("hello_1.0-1+b1_all.deb"), "deb payload")
+ .expect("write deb");
+
+ let ctx = Arc::new(
+ crate::context::Context::new(crate::context::ContextConfig::Local).expect("context"),
+ );
+ let opts = BinaryMetadataOptions {
+ profiles: Vec::new(),
+ vendor: "debian".to_string(),
+ exported_env: BTreeMap::new(),
+ build_arch: "amd64".to_string(),
+ host_arch: "amd64".to_string(),
+ };
+ let err = generate_binary_metadata(&ctx, &tree, base.path(), &opts)
+ .expect_err("binary-only build with an unparseable changelog must fail");
+ let err = err.to_string();
+ assert!(err.contains("debian/changelog"), "{err}");
+ assert!(err.contains("previous version"), "{err}");
+ assert!(err.contains("unbalanced parenthesis"), "{err}");
+ }
}