new: add upstream-aware orig tarball origins and the orig-vendor component
This commit is contained in:
+81
-22
@@ -12,12 +12,13 @@ use chrono::Datelike;
|
||||
use tar::Builder;
|
||||
use xz2::write::XzEncoder;
|
||||
|
||||
use super::options::NewOptions;
|
||||
use super::options::{NewOptions, SourceFormat};
|
||||
use super::templates::{OutputFile, Template};
|
||||
|
||||
/// `3.0 (quilt)` source format, the pkh new default.
|
||||
/// `3.0 (quilt)` source format, the default when packaging an existing
|
||||
/// project.
|
||||
pub const SOURCE_FORMAT_QUILT: &str = "3.0 (quilt)";
|
||||
/// `3.0 (native)` source format, selected by `--native`.
|
||||
/// `3.0 (native)` source format, the default for a fresh skeleton.
|
||||
pub const SOURCE_FORMAT_NATIVE: &str = "3.0 (native)";
|
||||
|
||||
/// The three source formats pkh knows how to build.
|
||||
@@ -59,7 +60,7 @@ pub fn files(opts: &NewOptions, template: &dyn Template) -> Vec<OutputFile> {
|
||||
copyright(opts),
|
||||
debian_gitignore(opts),
|
||||
];
|
||||
if !opts.native {
|
||||
if opts.source_format == SourceFormat::Quilt {
|
||||
files.push(local_options());
|
||||
}
|
||||
if opts.autopkgtest {
|
||||
@@ -94,19 +95,13 @@ fn autopkgtest_smoke(opts: &NewOptions) -> OutputFile {
|
||||
)
|
||||
}
|
||||
|
||||
/// `debian/source/format`: `3.0 (quilt)` by default, `3.0 (native)` with
|
||||
/// `--native`.
|
||||
/// `debian/source/format`: `3.0 (native)` for a skeleton by default,
|
||||
/// `3.0 (quilt)` for an existing project; either can be forced with
|
||||
/// `--native` / `--quilt`.
|
||||
fn source_format(opts: &NewOptions) -> OutputFile {
|
||||
OutputFile::new(
|
||||
"debian/source/format",
|
||||
format!(
|
||||
"{}\n",
|
||||
if opts.native {
|
||||
SOURCE_FORMAT_NATIVE
|
||||
} else {
|
||||
SOURCE_FORMAT_QUILT
|
||||
}
|
||||
),
|
||||
format!("{}\n", opts.source_format.deb_string()),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -369,6 +364,20 @@ pub fn create_orig_tarball(
|
||||
tree: &Path,
|
||||
name: &str,
|
||||
upstream_version: &str,
|
||||
) -> Result<std::path::PathBuf, Box<dyn std::error::Error>> {
|
||||
create_orig_tarball_excluding(tree, name, upstream_version, false)
|
||||
}
|
||||
|
||||
/// [`create_orig_tarball`] with the generated `vendor/` directory of a
|
||||
/// vendored rust package excluded from the snapshot: its contents travel in
|
||||
/// the separate `<name>_<uver>.orig-vendor.tar.xz` component instead (see
|
||||
/// [`super::orig`]), so they can be regenerated independently of the
|
||||
/// upstream sources.
|
||||
pub fn create_orig_tarball_excluding(
|
||||
tree: &Path,
|
||||
name: &str,
|
||||
upstream_version: &str,
|
||||
exclude_vendor: bool,
|
||||
) -> Result<std::path::PathBuf, Box<dyn std::error::Error>> {
|
||||
let tarball_path = orig_tarball_path(tree, name, upstream_version).ok_or_else(|| {
|
||||
format!(
|
||||
@@ -393,7 +402,8 @@ pub fn create_orig_tarball(
|
||||
let prefix = format!("{name}-{upstream_version}");
|
||||
// The single top-level directory dpkg-source expects.
|
||||
builder.append_dir(&prefix, tree)?;
|
||||
append_tree(&mut builder, tree, &prefix, 0)?;
|
||||
let top_excludes: &[&str] = if exclude_vendor { &["vendor"] } else { &[] };
|
||||
append_tree(&mut builder, tree, &prefix, 0, ORIG_EXCLUDE, top_excludes)?;
|
||||
|
||||
builder
|
||||
.finish()
|
||||
@@ -407,12 +417,15 @@ pub fn create_orig_tarball(
|
||||
}
|
||||
|
||||
/// Recursively append `dir` to the archive under `archive_path`, skipping
|
||||
/// the [`ORIG_EXCLUDE`] names and non-regular files.
|
||||
fn append_tree(
|
||||
/// non-regular files, the names of `excludes` at any depth, the `debian/`
|
||||
/// directory and the names of `top_excludes` at the top level (depth 0).
|
||||
pub(crate) fn append_tree(
|
||||
builder: &mut Builder<XzEncoder<std::fs::File>>,
|
||||
dir: &Path,
|
||||
archive_path: &str,
|
||||
depth: usize,
|
||||
excludes: &[&str],
|
||||
top_excludes: &[&str],
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut entries: Vec<std::fs::DirEntry> = std::fs::read_dir(dir)?.collect::<Result<_, _>>()?;
|
||||
entries.sort_by_key(|entry| entry.file_name());
|
||||
@@ -422,10 +435,10 @@ fn append_tree(
|
||||
let file_name = entry.file_name();
|
||||
let name = file_name.to_string_lossy().into_owned();
|
||||
|
||||
if depth == 0 && name == "debian" {
|
||||
if depth == 0 && (name == "debian" || top_excludes.contains(&name.as_str())) {
|
||||
continue;
|
||||
}
|
||||
if ORIG_EXCLUDE.contains(&name.as_str()) {
|
||||
if excludes.contains(&name.as_str()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -434,7 +447,14 @@ fn append_tree(
|
||||
.map_err(|e| format!("cannot stat '{}': {}", path.display(), e))?;
|
||||
if metadata.is_dir() {
|
||||
builder.append_dir(&entry_archive_path, &path)?;
|
||||
append_tree(builder, &path, &entry_archive_path, depth + 1)?;
|
||||
append_tree(
|
||||
builder,
|
||||
&path,
|
||||
&entry_archive_path,
|
||||
depth + 1,
|
||||
excludes,
|
||||
top_excludes,
|
||||
)?;
|
||||
} else if metadata.is_file() {
|
||||
// The mode (including the exec bit) travels through the header.
|
||||
let mut header = tar::Header::new_gnu();
|
||||
@@ -502,7 +522,8 @@ mod tests {
|
||||
series: "resolute".into(),
|
||||
release: false,
|
||||
depends: Vec::new(),
|
||||
native: false,
|
||||
source_format: SourceFormat::Quilt,
|
||||
orig: None,
|
||||
git: true,
|
||||
autopkgtest: false,
|
||||
pkg_config: false,
|
||||
@@ -528,7 +549,7 @@ mod tests {
|
||||
);
|
||||
|
||||
let native = NewOptions {
|
||||
native: true,
|
||||
source_format: SourceFormat::Native,
|
||||
..opts()
|
||||
};
|
||||
let files = super::files(
|
||||
@@ -799,6 +820,44 @@ mod tests {
|
||||
assert!(err.to_string().contains("already exists"));
|
||||
}
|
||||
|
||||
/// The vendored-rust variant excludes the top-level `vendor/` (it
|
||||
/// travels in the orig-vendor component) but keeps unrelated trees.
|
||||
#[test]
|
||||
fn orig_tarball_vendor_exclusion() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let tree = dir.path().join("mytool");
|
||||
std::fs::create_dir_all(tree.join("vendor/serde/src")).unwrap();
|
||||
std::fs::create_dir_all(tree.join("src/vendor")).unwrap();
|
||||
std::fs::write(tree.join("vendor/serde/src/lib.rs"), "code").unwrap();
|
||||
std::fs::write(tree.join("src/vendor/mod.rs"), "code").unwrap();
|
||||
std::fs::write(tree.join("Cargo.toml"), "[package]").unwrap();
|
||||
|
||||
let tarball = create_orig_tarball_excluding(&tree, "mytool", "0.1.0", true).unwrap();
|
||||
let mut archive = tar::Archive::new(xz2::read::XzDecoder::new(
|
||||
std::fs::File::open(&tarball).unwrap(),
|
||||
));
|
||||
let names: Vec<String> = archive
|
||||
.entries()
|
||||
.unwrap()
|
||||
.map(|e| e.unwrap().path().unwrap().to_string_lossy().into_owned())
|
||||
.collect();
|
||||
// The generated vendored tree is out...
|
||||
assert!(
|
||||
!names.iter().any(|n| n.starts_with("mytool-0.1.0/vendor")),
|
||||
"{names:?}"
|
||||
);
|
||||
// ...an unrelated nested vendor/ stays in...
|
||||
assert!(
|
||||
names.iter().any(|n| n == "mytool-0.1.0/src/vendor/mod.rs"),
|
||||
"{names:?}"
|
||||
);
|
||||
// ...and normal files are unaffected.
|
||||
assert!(
|
||||
names.iter().any(|n| n == "mytool-0.1.0/Cargo.toml"),
|
||||
"{names:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_files_sets_exec_bit_and_parents() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
|
||||
Reference in New Issue
Block a user