use clap::{Parser, Subcommand};
use std::path::PathBuf;
use thiserror::Error;
use whitaker_installer::dependency_binaries::{
find_dependency_binary, required_dependency_binaries,
};
use whitaker_installer::dependency_packaging::{
DependencyPackageParams, DependencyPackagingError, package_dependency_binary,
write_provenance_markdown,
};
use whitaker_installer::installer_packaging::TargetTriple;
#[derive(Parser, Debug)]
#[command(name = "whitaker-package-dependency-binary", version)]
#[command(about = "Package dependency binaries and provenance assets")]
struct Cli {
#[command(subcommand)]
command: Command,
}
#[derive(Subcommand, Debug)]
enum Command {
Package {
#[arg(long)]
package: String,
#[arg(long)]
target: String,
#[arg(long)]
binary_path: PathBuf,
#[arg(long)]
output_dir: PathBuf,
},
Provenance {
#[arg(long)]
output_dir: PathBuf,
},
}
#[derive(Debug, Error)]
enum CliError {
#[error("unknown dependency package: {0}")]
UnknownPackage(String),
#[error("dependency manifest error: {0}")]
Manifest(String),
#[error("{0}")]
Packaging(#[from] DependencyPackagingError),
#[error("{0}")]
Target(#[from] whitaker_installer::artefact::error::ArtefactError),
}
fn main() {
let cli = Cli::parse();
if let Err(error) = run(cli) {
eprintln!("error: {error}");
std::process::exit(1);
}
}
fn run(cli: Cli) -> Result<(), CliError> {
match cli.command {
Command::Package {
package,
target,
binary_path,
output_dir,
} => {
let dependency = find_dependency_binary(&package)
.map_err(|error| CliError::Manifest(error.to_string()))?
.cloned()
.ok_or(CliError::UnknownPackage(package))?;
let target = TargetTriple::try_from(target.as_str())?;
let output = package_dependency_binary(DependencyPackageParams {
dependency,
target,
binary_path,
output_dir,
})?;
println!("Created {}", output.archive_path.display());
}
Command::Provenance { output_dir } => {
let dependencies = required_dependency_binaries()
.map_err(|error| CliError::Manifest(error.to_string()))?;
let output = write_provenance_markdown(&output_dir, dependencies)?;
println!("Created {}", output.display());
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::tempdir;
use whitaker_installer::dependency_binaries::provenance_filename;
#[test]
fn run_package_command_rejects_invalid_target() {
let temp_dir = tempdir().expect("temp dir");
let binary_path = temp_dir.path().join("cargo-dylint");
std::fs::write(&binary_path, b"fake binary").expect("write binary");
let cli = Cli {
command: Command::Package {
package: "cargo-dylint".to_owned(),
target: "invalid-target-triple".to_owned(),
binary_path,
output_dir: temp_dir.path().join("dist"),
},
};
let result = run(cli);
assert!(matches!(result, Err(CliError::Target(_))));
}
#[test]
fn run_package_command_creates_archive() {
let temp_dir = tempdir().expect("temp dir");
let binary_path = temp_dir.path().join("cargo-dylint");
std::fs::write(&binary_path, b"fake binary").expect("write binary");
let dependency = find_dependency_binary("cargo-dylint")
.expect("manifest should be parseable")
.expect("cargo-dylint should be in manifest");
let expected_filename = format!(
"dist/cargo-dylint-x86_64-unknown-linux-gnu-v{}.tgz",
dependency.version()
);
let cli = Cli {
command: Command::Package {
package: "cargo-dylint".to_owned(),
target: "x86_64-unknown-linux-gnu".to_owned(),
binary_path,
output_dir: temp_dir.path().join("dist"),
},
};
let result = run(cli);
assert!(
result.is_ok(),
"expected package command to succeed: {result:?}"
);
assert!(temp_dir.path().join(expected_filename).is_file());
}
#[test]
fn run_provenance_command_writes_markdown() {
let temp_dir = tempdir().expect("temp dir");
let output_dir = temp_dir.path().join("dist");
let cli = Cli {
command: Command::Provenance {
output_dir: output_dir.clone(),
},
};
let result = run(cli);
assert!(
result.is_ok(),
"expected provenance command to succeed: {result:?}"
);
assert!(output_dir.join(provenance_filename()).is_file());
}
#[test]
fn run_package_command_rejects_unknown_package() {
let temp_dir = tempdir().expect("temp dir");
let binary_path = temp_dir.path().join("fake-binary");
std::fs::write(&binary_path, b"fake binary").expect("write binary");
let cli = Cli {
command: Command::Package {
package: "missing-package".to_owned(),
target: "x86_64-unknown-linux-gnu".to_owned(),
binary_path,
output_dir: temp_dir.path().join("dist"),
},
};
let result = run(cli);
assert!(
matches!(result, Err(CliError::UnknownPackage(package)) if package == "missing-package")
);
}
}