new: scaffold new Debian source packages (non-interactive core)

This commit is contained in:
2026-09-16 12:14:09 +02:00
parent 9c3394750d
commit d044f757e9
12 changed files with 3243 additions and 1 deletions
+149
View File
@@ -35,6 +35,105 @@ fn main() {
let matches = command!()
.subcommand_required(true)
.disable_version_flag(true)
.subcommand(
Command::new("new")
.about("Scaffold a new Debian source package (buildable right away)")
.arg(arg!([name] "Package name: creates ./<name>/ with a fresh project skeleton. Without it (or with --source), the given/current directory is packaged"))
// NOTE: hyphenated long names are defined via the builder API
// because clap's `arg!` macro mis-tokenizes them (see the
// prune subcommand note below).
.arg(
clap::Arg::new("lang")
.long("lang")
.value_name("LANG")
.help("Language/build system: rust, python, meson, cmake, autotools, go, shell, makefile or empty"),
)
.arg(
clap::Arg::new("source")
.long("source")
.value_name("PATH")
.conflicts_with("name")
.help("Package the sources in PATH instead of creating ./<name>/"),
)
.arg(
clap::Arg::new("upstream_version")
.long("upstream-version")
.value_name("VERSION")
.help("Upstream version (default: 0.1.0)"),
)
.arg(
clap::Arg::new("revision")
.long("revision")
.value_name("N")
.value_parser(clap::value_parser!(u32))
.help("Debian revision (default: 1)"),
)
.arg(
clap::Arg::new("description")
.long("description")
.value_name("DESC")
.help("One-line package description"),
)
.arg(
clap::Arg::new("homepage")
.long("homepage")
.value_name("URL")
.help("Upstream homepage (http:// or https://)"),
)
.arg(
clap::Arg::new("license")
.long("license")
.value_name("SPDX")
.help("Upstream license (SPDX identifier, e.g. MIT, GPL-3.0+)"),
)
.arg(
clap::Arg::new("command")
.long("command")
.value_name("CMD")
.help("Installed command name (default: the package name)"),
)
.arg(
clap::Arg::new("maintainer")
.long("maintainer")
.value_name("NAME <EMAIL>")
.help("Maintainer (default: DEBFULLNAME/DEBEMAIL, then git config)"),
)
.arg(
clap::Arg::new("depends")
.long("depends")
.value_name("LIST")
.action(clap::ArgAction::Append)
.long_help("Runtime Depends of the metapackage flavor ('empty' template), as a comma-separated list (e.g. \"hello, hello-data (>= 1.0)\"). Can be specified multiple times.")
.help("Metapackage Depends list, comma-separated ('empty' template only)"),
)
.arg(
clap::Arg::new("dist")
.long("dist")
.value_name("DIST")
.help("Target distribution: debian or ubuntu (default: current vendor)"),
)
.arg(
clap::Arg::new("series")
.long("series")
.value_name("SERIES")
.help("Target series (default: the development series of --dist)"),
)
.arg(arg!(--release "Write the --series into debian/changelog instead of UNRELEASED").required(false))
.arg(arg!(--native "Use the 3.0 (native) source format (no orig tarball)").required(false))
.arg(
clap::Arg::new("no_git")
.long("no-git")
.action(clap::ArgAction::SetTrue)
.help("Do not initialize a git repository (.gitignore files are written anyway)"),
)
.arg(
clap::Arg::new("no_verify")
.long("no-verify")
.action(clap::ArgAction::SetTrue)
.help("Skip the post-scaffold build verification (the structural self-checks always run)"),
)
.arg(arg!(--defaults "Take the default answer for every question left unanswered (the package name is still required)").required(false)),
)
.subcommand(
Command::new("pull")
.about("Pull a source package from the archive or git")
@@ -134,6 +233,56 @@ fn main() {
.get_matches();
match matches.subcommand() {
Some(("new", sub_matches)) => {
// Without the interactive wizard (follow-up work), missing
// required answers produce one error listing all of them;
// --defaults fills everything else from the defaults.
let depends: Vec<String> = sub_matches
.get_many::<String>("depends")
.map(|values| values.cloned().collect())
.unwrap_or_default();
let cli = pkh::new::options::NewCli {
name: sub_matches.get_one::<String>("name").cloned(),
lang: sub_matches.get_one::<String>("lang").cloned(),
source: sub_matches
.get_one::<String>("source")
.map(std::path::PathBuf::from),
upstream_version: sub_matches.get_one::<String>("upstream_version").cloned(),
revision: sub_matches.get_one::<u32>("revision").copied(),
description: sub_matches.get_one::<String>("description").cloned(),
homepage: sub_matches.get_one::<String>("homepage").cloned(),
license: sub_matches.get_one::<String>("license").cloned(),
command: sub_matches.get_one::<String>("command").cloned(),
maintainer: sub_matches.get_one::<String>("maintainer").cloned(),
depends,
dist: sub_matches.get_one::<String>("dist").cloned(),
series: sub_matches.get_one::<String>("series").cloned(),
release: sub_matches
.get_one::<bool>("release")
.copied()
.unwrap_or(false),
native: sub_matches
.get_one::<bool>("native")
.copied()
.unwrap_or(false),
git: !sub_matches
.get_one::<bool>("no_git")
.copied()
.unwrap_or(false),
defaults: sub_matches
.get_one::<bool>("defaults")
.copied()
.unwrap_or(false),
};
if let Err(e) = rt.block_on(async {
let opts = pkh::new::options::resolve(cli).await?;
pkh::new::scaffold(opts, &multi)
}) {
error!("{}", e);
std::process::exit(1);
}
}
Some(("pull", sub_matches)) => {
let package = sub_matches.get_one::<String>("package").expect("required");
let series = sub_matches.get_one::<String>("series").map(|s| s.as_str());