pull: fetch every component tarball of multi-orig packages

Sources listed with "3.0 (quilt)" extra components (node-jest, php-*,
...) carry one tarball per bundled module next to the main orig, named
<package>_<uver>.orig-<component>.tar.<ext>. fetch_orig_tarball picked
the single file matching ".orig.tar." — which cannot even match the
component naming — so a git pull only fetched the main orig. The later
dpkg-source -b quilt verification then failed with "can't find file to
patch" on the first patch touching a component directory.

Select the files with the existing build::changes::is_orig_tarball
helper (mirroring dpkg's \.orig(-.+)?\.tar\. strip pattern) and fetch
all of them, pristine-tar checkout first with a checksummed archive
download fallback, per tarball.

The end-to-end test now asserts every stanza-listed orig lands in the
package dir instead of just any *.orig.tar.* file, and gains a
node-jest (trixie, 24 components) regression case.

Verified live: pkh pull node-jest -d debian fetches all 15 origs of the
sid ds7 repack, and dpkg-source -b builds the debian.tar.xz and dsc
without touching the series.
This commit is contained in:
2026-09-21 11:28:55 +02:00
parent 6c0b200241
commit 37e0b5c978
+40 -16
View File
@@ -452,15 +452,18 @@ async fn fetch_orig_tarball(
Path::new(&info.stanza.package).to_path_buf() Path::new(&info.stanza.package).to_path_buf()
}; };
// Find the orig tarball in the file list // Upstream tarballs in the file list: the main orig tarball plus, for
// Usually ends with .orig.tar.gz or .orig.tar.xz // multi-orig ("3.0 (quilt)" extra-component) sources, one component
let orig_file = info // tarball per bundled module (`*.orig-<component>.tar.<ext>`). dpkg-source
// unpacks all of them side by side, so a git pull must fetch them all.
let orig_files: Vec<_> = info
.stanza .stanza
.files .files
.iter() .iter()
.find(|f| f.name.contains(".orig.tar.")) .filter(|f| crate::build::changes::is_orig_tarball(&f.name))
.ok_or_else(|| { .collect();
format!( if orig_files.is_empty() {
return Err(format!(
"Could not find orig tarball in file list for package '{}'. \ "Could not find orig tarball in file list for package '{}'. \
Available files: {:?}", Available files: {:?}",
info.stanza.package, info.stanza.package,
@@ -470,14 +473,17 @@ async fn fetch_orig_tarball(
.map(|f| &f.name) .map(|f| &f.name)
.collect::<Vec<_>>() .collect::<Vec<_>>()
) )
})?; .into());
let filename = &orig_file.name; }
// 1. Try executing pristine-tar // 1. Try executing pristine-tar
// Setup pristine-tar branch if needed (by tracking remote branch) // Setup pristine-tar branch if needed (by tracking remote branch)
let _ = setup_pristine_tar_branch(&package_dir, info.dist.as_str()); let _ = setup_pristine_tar_branch(&package_dir, info.dist.as_str());
for orig_file in orig_files {
let filename = &orig_file.name;
if let Err(e) = checkout_pristine_tar(&package_dir, filename.as_str()) { if let Err(e) = checkout_pristine_tar(&package_dir, filename.as_str()) {
debug!( debug!(
"pristine-tar failed: {}. Falling back to archive download.", "pristine-tar failed: {}. Falling back to archive download.",
@@ -497,6 +503,7 @@ async fn fetch_orig_tarball(
) )
.await?; .await?;
} }
}
Ok(()) Ok(())
} }
@@ -844,23 +851,29 @@ mod tests {
} }
} }
// Check for orig tarball in package dir (only for non-native packages) // Check for the orig tarballs in the package dir (only for non-native
let mut found_tarball = false; // packages): every orig listed in the stanza must be present, including
// the component tarballs of multi-orig packages (dpkg-source needs them
// all to unpack the merged upstream tree)
let mut found_dsc = false; let mut found_dsc = false;
for entry in std::fs::read_dir(package_dir).unwrap() { for entry in std::fs::read_dir(&package_dir).unwrap() {
let entry = entry.unwrap(); let entry = entry.unwrap();
let name = entry.file_name().to_string_lossy().to_string(); let name = entry.file_name().to_string_lossy().to_string();
if name.contains(".orig.tar.") {
found_tarball = true;
}
if name.ends_with(".dsc") { if name.ends_with(".dsc") {
found_dsc = true; found_dsc = true;
} }
} }
// Only check for orig tarball if the package is not native
if !info.is_native() { if !info.is_native() {
assert!(found_tarball, "Orig tarball not found in package dir"); for file in &info.stanza.files {
if crate::build::changes::is_orig_tarball(&file.name) {
assert!(
package_dir.join(&file.name).exists(),
"Orig tarball '{}' not found in package dir",
file.name
);
}
}
} }
assert!(found_dsc, "DSC file not found in package dir"); assert!(found_dsc, "DSC file not found in package dir");
} }
@@ -914,6 +927,17 @@ mod tests {
test_pull_package_end_to_end("paraview", Some("noble"), None, None).await; test_pull_package_end_to_end("paraview", Some("noble"), None, None).await;
} }
/// Multi-orig ("3.0 (quilt)" extra component) regression test: node-jest
/// ships its bundled modules as separate `*.orig-<component>.tar.xz`
/// tarballs next to the main orig. The git pull path must fetch every
/// component, or the later `dpkg-source -b` quilt verification fails
/// with "can't find file to patch" on the first patch touching a
/// component directory.
#[tokio::test]
async fn test_pull_node_jest_debian_end_to_end() {
test_pull_package_end_to_end("node-jest", Some("trixie"), None, None).await;
}
/// Build a minimal uncompressed ustar archive from (name, data) entries. /// Build a minimal uncompressed ustar archive from (name, data) entries.
/// ///
/// Raw header blocks are crafted instead of using `tar::Builder` because /// Raw header blocks are crafted instead of using `tar::Builder` because