deb: strip VCS metadata from prepared build trees
Copying a git checkout verbatim into /tmp/pkh-build-*/ ships '.git', flipping autotools' building-from-VCS detection (GNU hello's BUILD_FROM_GIT): the shipped man page gets cleaned and regenerated via help2man, which is correctly not in Build-Depends. Skip VCS dirs (.git/.hg/.svn/.bzr/CVS) in local/unshare/ssh copies, and prune them after an unshare overlayfs mount.
This commit is contained in:
@@ -20,6 +20,49 @@ pub fn current() -> Arc<Context> {
|
||||
manager::MANAGER.current()
|
||||
}
|
||||
|
||||
/// Version-control metadata directories that must never leak into a
|
||||
/// prepared build tree.
|
||||
///
|
||||
/// Their presence flips autotools' "building from VCS" detection (e.g. GNU
|
||||
/// hello's `BUILD_FROM_GIT`, triggered by a `.git` directory next to
|
||||
/// `configure.ac`), which activates maintainer-only regeneration rules
|
||||
/// requiring tools that are deliberately not declared as build-dependencies
|
||||
/// (e.g. `help2man`). Source packages produced by dpkg-source never contain
|
||||
/// them, so package builds must not see them either.
|
||||
pub(crate) fn is_vcs_dir_name(name: &std::ffi::OsStr) -> bool {
|
||||
matches!(
|
||||
name.to_str(),
|
||||
Some(".git") | Some(".hg") | Some(".svn") | Some(".bzr") | Some("CVS")
|
||||
)
|
||||
}
|
||||
|
||||
/// Recursively remove version-control metadata directories below `root`.
|
||||
///
|
||||
/// Used after an overlay mount, where the source tree is exposed verbatim
|
||||
/// and entries cannot be filtered during the copy.
|
||||
pub(crate) fn prune_vcs_dirs(root: &std::path::Path) -> std::io::Result<()> {
|
||||
let mut pending = vec![root.to_path_buf()];
|
||||
while let Some(dir) = pending.pop() {
|
||||
for entry in std::fs::read_dir(&dir)? {
|
||||
let entry = entry?;
|
||||
let path = entry.path();
|
||||
// symlink_metadata: never follow symlinks while pruning.
|
||||
let Ok(meta) = std::fs::symlink_metadata(&path) else {
|
||||
continue;
|
||||
};
|
||||
if meta.is_dir() {
|
||||
if is_vcs_dir_name(&entry.file_name()) {
|
||||
log::debug!("Removing VCS metadata from build tree: {}", path.display());
|
||||
std::fs::remove_dir_all(&path)?;
|
||||
} else {
|
||||
pending.push(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -178,4 +221,43 @@ mod tests {
|
||||
std::path::Path::new("/nonexistent/target")
|
||||
);
|
||||
}
|
||||
|
||||
/// Copying a source tree into a build context must strip version-control
|
||||
/// metadata directories at any depth: their presence flips autotools
|
||||
/// "building from git" detection and activates maintainer-only rules
|
||||
/// needing undeclared tools (e.g. help2man for GNU hello's man page).
|
||||
#[test]
|
||||
fn test_ensure_available_strips_vcs_metadata() {
|
||||
let temp_dir = tempfile::tempdir().unwrap();
|
||||
let src_root = temp_dir.path().join("pkg");
|
||||
fs::create_dir_all(src_root.join(".git/objects")).unwrap();
|
||||
fs::write(src_root.join(".git/HEAD"), "ref: refs/heads/main").unwrap();
|
||||
fs::create_dir_all(src_root.join("src/.svn")).unwrap();
|
||||
fs::write(src_root.join("src/hello.c"), "int main() {}").unwrap();
|
||||
|
||||
let ctx = Context::new(ContextConfig::Local);
|
||||
let dest = ctx.ensure_available(&src_root, "/tmp").unwrap();
|
||||
|
||||
assert!(dest.join("src/hello.c").exists());
|
||||
assert!(!dest.join(".git").exists());
|
||||
assert!(!dest.join("src/.svn").exists());
|
||||
}
|
||||
|
||||
/// The overlay-mount path exposes the tree verbatim, so pruning happens
|
||||
/// after the fact: nested VCS metadata must be removed recursively.
|
||||
#[test]
|
||||
fn test_prune_vcs_dirs_removes_nested_metadata() {
|
||||
let temp_dir = tempfile::tempdir().unwrap();
|
||||
let root = temp_dir.path().join("tree");
|
||||
fs::create_dir_all(root.join("a/.git/objects")).unwrap();
|
||||
fs::create_dir_all(root.join("b/c/CVS")).unwrap();
|
||||
fs::write(root.join("a/.git/HEAD"), "ref").unwrap();
|
||||
fs::write(root.join("b/keep.txt"), "x").unwrap();
|
||||
|
||||
prune_vcs_dirs(&root).unwrap();
|
||||
|
||||
assert!(!root.join("a/.git").exists());
|
||||
assert!(!root.join("b/c/CVS").exists());
|
||||
assert!(root.join("b/keep.txt").exists());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user