diff --git a/Cargo.lock b/Cargo.lock index 05d8134c..329df93b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -748,14 +748,12 @@ dependencies = [ "clap", "dirs", "dunce", - "env_logger", "glob", "handlebars", "heck", "hex", "image", "libflate", - "log", "md5", "minisign", "once_cell", @@ -775,6 +773,8 @@ dependencies = [ "thiserror", "time", "toml 0.8.0", + "tracing", + "tracing-subscriber", "ureq", "uuid", "walkdir", @@ -1973,19 +1973,6 @@ dependencies = [ "syn 2.0.37", ] -[[package]] -name = "env_logger" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" -dependencies = [ - "humantime", - "is-terminal", - "log", - "regex", - "termcolor", -] - [[package]] name = "epaint" version = "0.22.0" @@ -2009,15 +1996,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" -[[package]] -name = "erased-serde" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c138974f9d5e7fe373eb04df7cae98833802ae4b11c24ac7039a21d5af4b26c" -dependencies = [ - "serde", -] - [[package]] name = "errno" version = "0.3.3" @@ -2959,12 +2937,6 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - [[package]] name = "hyper" version = "0.14.27" @@ -3463,17 +3435,6 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" -[[package]] -name = "is-terminal" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" -dependencies = [ - "hermit-abi", - "rustix 0.38.14", - "windows-sys 0.48.0", -] - [[package]] name = "itertools" version = "0.11.0" @@ -3794,9 +3755,6 @@ name = "log" version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" -dependencies = [ - "value-bag", -] [[package]] name = "longest-increasing-subsequence" @@ -5500,15 +5458,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "serde_fmt" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d4ddca14104cd60529e8c7f7ba71a2c8acd8f7f5cfcdc2faf97eeb7c3010a4" -dependencies = [ - "serde", -] - [[package]] name = "serde_json" version = "1.0.107" @@ -6053,74 +6002,6 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" -[[package]] -name = "sval" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05d11eec9fbe2bc8bc71e7349f0e7534db9a96d961fb9f302574275b7880ad06" - -[[package]] -name = "sval_buffer" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b7451f69a93c5baf2653d5aa8bb4178934337f16c22830a50b06b386f72d761" -dependencies = [ - "sval", - "sval_ref", -] - -[[package]] -name = "sval_dynamic" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c34f5a2cc12b4da2adfb59d5eedfd9b174a23cc3fae84cec71dcbcd9302068f5" -dependencies = [ - "sval", -] - -[[package]] -name = "sval_fmt" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f578b2301341e246d00b35957f2952c4ec554ad9c7cfaee10bc86bc92896578" -dependencies = [ - "itoa 1.0.9", - "ryu", - "sval", -] - -[[package]] -name = "sval_json" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8346c00f5dc6efe18bea8d13c1f7ca4f112b20803434bf3657ac17c0f74cbc4b" -dependencies = [ - "itoa 1.0.9", - "ryu", - "sval", -] - -[[package]] -name = "sval_ref" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6617cc89952f792aebc0f4a1a76bc51e80c70b18c491bd52215c7989c4c3dd06" -dependencies = [ - "sval", -] - -[[package]] -name = "sval_serde" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe3d1e59f023341d9af75d86f3bc148a6704f3f831eef0dd90bbe9cb445fa024" -dependencies = [ - "serde", - "sval", - "sval_buffer", - "sval_fmt", -] - [[package]] name = "svgtypes" version = "0.11.0" @@ -7093,42 +6974,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" -[[package]] -name = "value-bag" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d92ccd67fb88503048c01b59152a04effd0782d035a83a6d256ce6085f08f4a3" -dependencies = [ - "value-bag-serde1", - "value-bag-sval2", -] - -[[package]] -name = "value-bag-serde1" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0b9f3feef403a50d4d67e9741a6d8fc688bcbb4e4f31bd4aab72cc690284394" -dependencies = [ - "erased-serde", - "serde", - "serde_fmt", -] - -[[package]] -name = "value-bag-sval2" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30b24f4146b6f3361e91cbf527d1fb35e9376c3c0cef72ca5ec5af6d640fad7d" -dependencies = [ - "sval", - "sval_buffer", - "sval_dynamic", - "sval_fmt", - "sval_json", - "sval_ref", - "sval_serde", -] - [[package]] name = "vec_map" version = "0.8.2" diff --git a/Cargo.toml b/Cargo.toml index e0717d1c..671094c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ dunce = "1" schemars = { version = "0.8", features = ["url", "preserve_order", "derive"] } cargo-packager-config = { path = "crates/config", version = "0.0.0" } clap = { version = "4.4", features = ["derive"] } -log = "0.4" dirs = "5.0" semver = "1" base64 = "0.21" +tracing = "0.1" diff --git a/crates/packager/Cargo.toml b/crates/packager/Cargo.toml index 431b7a0d..907d506d 100644 --- a/crates/packager/Cargo.toml +++ b/crates/packager/Cargo.toml @@ -7,7 +7,8 @@ license = "Apache-2.0 OR MIT" [features] default = ["cli"] -cli = ["env_logger", "clap", "cargo-packager-config/clap"] +cli = ["clap", "cargo-packager-config/clap", "tracing", "tracing-subscriber"] +tracing = ["dep:tracing"] [package.metadata.docs.rs] rustdoc-args = ["--cfg", "doc_cfg"] @@ -33,9 +34,11 @@ dirs.workspace = true semver.workspace = true base64.workspace = true clap = { workspace = true, optional = true, features = ["env"] } -log = { workspace = true, features = ["kv_unstable", "kv_unstable_std"] } +tracing = { workspace = true, optional = true } +tracing-subscriber = { version = "0.3", optional = true, features = [ + "env-filter", +] } toml = "0.8" -env_logger = { version = "0.10", optional = true } cargo_metadata = "0.18" ureq = "2.7" hex = "0.4" diff --git a/crates/packager/src/cli/config.rs b/crates/packager/src/cli/config.rs index f719486e..573e5059 100644 --- a/crates/packager/src/cli/config.rs +++ b/crates/packager/src/cli/config.rs @@ -1,4 +1,7 @@ -use std::path::{Path, PathBuf}; +use std::{ + fmt::Debug, + path::{Path, PathBuf}, +}; use crate::{config::Binary, Config}; @@ -28,7 +31,10 @@ fn find_nearset_pkg_name(path: &Path) -> crate::Result> { res } -pub fn parse_config_file>(path: P) -> crate::Result, Config)>> { +#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))] +pub fn parse_config_file + Debug>( + path: P, +) -> crate::Result, Config)>> { let path = path.as_ref().to_path_buf().canonicalize()?; let content = std::fs::read_to_string(&path)?; let mut configs = match path.extension().and_then(|e| e.to_str()) { @@ -62,25 +68,25 @@ pub fn parse_config_file>(path: P) -> crate::Result Vec { +#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))] +pub fn find_config_files() -> crate::Result> { let opts = glob::MatchOptions { case_sensitive: false, ..Default::default() }; - [ - glob::glob_with("**/packager.toml", opts) - .unwrap() + Ok([ + glob::glob_with("**/packager.toml", opts)? .flatten() .collect::>(), - glob::glob_with("**/packager.json", opts) - .unwrap() + glob::glob_with("**/packager.json", opts)? .flatten() .collect::>(), ] - .concat() + .concat()) } +#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))] pub fn load_configs_from_cargo_workspace( release: bool, profile: Option, diff --git a/crates/packager/src/cli/mod.rs b/crates/packager/src/cli/mod.rs index ad71b317..f655b301 100644 --- a/crates/packager/src/cli/mod.rs +++ b/crates/packager/src/cli/mod.rs @@ -2,11 +2,9 @@ #![cfg(feature = "cli")] -use std::{fmt::Write as FmtWrite, io::Write, path::PathBuf}; +use std::{fmt::Write, path::PathBuf}; use clap::{ArgAction, CommandFactory, FromArgMatches, Parser, Subcommand}; -use env_logger::fmt::Color; -use log::{log_enabled, Level}; use self::config::{find_config_files, load_configs_from_cargo_workspace, parse_config_file}; use crate::{ @@ -22,7 +20,7 @@ enum Commands { Signer(signer::Options), } -#[derive(Parser)] +#[derive(Parser, Debug)] #[clap( author, version, @@ -35,6 +33,10 @@ pub(crate) struct Cli { /// Enables verbose logging. #[clap(short, long, global = true, action = ArgAction::Count)] verbose: u8, + /// Disables logging + #[clap(short, long)] + quite: bool, + /// Specify the package fromats to build. #[clap(short, long, value_enum, value_delimiter = ',')] formats: Option>, @@ -74,7 +76,8 @@ pub(crate) struct Cli { command: Option, } -fn run(cli: Cli) -> Result<()> { +#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))] +fn try_run(cli: Cli) -> Result<()> { // run subcommand and exit if one was specified, // otherwise run the default packaging command if let Some(command) = cli.command { @@ -96,7 +99,7 @@ fn run(cli: Cli) -> Result<()> { Some(c) => parse_config_file(c)?, // fallback to config files and cargo workspaces configs _ => { - let config_files = find_config_files() + let config_files = find_config_files()? .into_iter() .filter_map(|c| parse_config_file(c).ok()) .collect::>() @@ -124,7 +127,8 @@ fn run(cli: Cli) -> Result<()> { }; if configs.is_empty() { - log::warn!("Couldn't detect a valid configuration file! Nothing to do here.") + tracing::warn!("Couldn't detect a valid configuration file! Nothing to do here."); + return Ok(()); } let cli_out_dir = cli.out_dir.as_ref().map(dunce::canonicalize).transpose()?; @@ -138,12 +142,15 @@ fn run(cli: Cli) -> Result<()> { config.formats.replace(formats.clone()); } - if config.log_level.is_none() { - config.log_level.replace(match cli.verbose { - 0 => LogLevel::Info, - 1 => LogLevel::Debug, - 2.. => LogLevel::Trace, - }); + if config.log_level.is_none() && !cli.quite { + let level = match parse_log_level(cli.verbose) { + tracing::Level::ERROR => LogLevel::Error, + tracing::Level::WARN => LogLevel::Warn, + tracing::Level::INFO => LogLevel::Info, + tracing::Level::DEBUG => LogLevel::Debug, + tracing::Level::TRACE => LogLevel::Trace, + }; + config.log_level.replace(level); } } @@ -162,7 +169,10 @@ fn run(cli: Cli) -> Result<()> { if let Some(path) = config_dir { // change the directory to the config being built // so paths will be read relative to it - std::env::set_current_dir(path.parent().ok_or(crate::Error::ParentDirNotFound)?)?; + std::env::set_current_dir( + path.parent() + .ok_or_else(|| crate::Error::ParentDirNotFound(path.clone()))?, + )?; } // create the packages @@ -189,9 +199,14 @@ fn run(cli: Cli) -> Result<()> { let pluralised = if len == 1 { "package" } else { "packages" }; let mut printable_paths = String::new(); for path in outputs { - writeln!(printable_paths, " {}", util::display_path(path)).unwrap(); + let _ = writeln!(printable_paths, " {}", util::display_path(path)); } - log::info!(action = "Finished"; "packaging {} {} at:\n{}", len, pluralised, printable_paths); + tracing::info!( + "Finished packaging {} {} at:\n{}", + len, + pluralised, + printable_paths + ); } let len = signatures.len(); @@ -199,16 +214,33 @@ fn run(cli: Cli) -> Result<()> { let pluralised = if len == 1 { "signature" } else { "signatures" }; let mut printable_paths = String::new(); for path in signatures { - writeln!(printable_paths, " {}", util::display_path(path)).unwrap(); + let _ = writeln!(printable_paths, " {}", util::display_path(path)); } - log::info!(action = "Finished"; "signing packages, {} {} at:\n{}", len, pluralised, printable_paths); + tracing::info!( + "Finished signing packages, {} {} at:\n{}", + len, + pluralised, + printable_paths + ); } Ok(()) } +fn parse_log_level(verbose: u8) -> tracing::Level { + match verbose { + 0 => tracing_subscriber::EnvFilter::builder() + .from_env_lossy() + .max_level_hint() + .and_then(|l| l.into_level()) + .unwrap_or(tracing::Level::INFO), + 1 => tracing::Level::DEBUG, + 2.. => tracing::Level::TRACE, + } +} + /// Run the packager CLI -pub fn try_run() -> crate::Result<()> { +pub fn run() { // prepare cli args let args = std::env::args_os().skip(1); let cli = Cli::command(); @@ -219,53 +251,23 @@ pub fn try_run() -> crate::Result<()> { Err(e) => e.exit(), }; - // setup logger - let filter_level = match cli.verbose { - 0 => Level::Info, - 1 => Level::Debug, - 2.. => Level::Trace, - } - .to_level_filter(); - let mut builder = env_logger::Builder::from_default_env(); - let logger_init_res = builder - .format_indent(Some(12)) - .filter(None, filter_level) - .format(|f, record| { - let mut is_command_output = false; - if let Some(action) = record.key_values().get("action".into()) { - let action = action.to_str().unwrap(); - is_command_output = action == "stdout" || action == "stderr"; - if !is_command_output { - let mut action_style = f.style(); - action_style.set_color(Color::Green).set_bold(true); - write!(f, "{:>12} ", action_style.value(action))?; - } - } else { - let mut level_style = f.default_level_style(record.level()); - level_style.set_bold(true); - let level = match record.level() { - Level::Error => "Error", - Level::Warn => "Warn", - Level::Info => "Info", - Level::Debug => "Debug", - Level::Trace => "Trace", - }; - write!(f, "{:>12} ", level_style.value(level))?; - } - - if !is_command_output && log_enabled!(Level::Debug) { - let mut target_style = f.style(); - target_style.set_color(Color::Black); - write!(f, "[{}] ", target_style.value(record.target()))?; - } - - writeln!(f, "{}", record.args()) - }) - .try_init(); - - if let Err(err) = logger_init_res { - eprintln!("Failed to attach logger: {err}"); + if !cli.quite { + let level = parse_log_level(cli.verbose); + + let debug = level == tracing::Level::DEBUG; + let tracing = level == tracing::Level::TRACE; + + tracing_subscriber::fmt() + .with_ansi(std::io::IsTerminal::is_terminal(&std::io::stderr())) + .without_time() + .with_target(debug) + .with_line_number(tracing) + .with_file(tracing) + .init(); } - run(cli) + if let Err(e) = try_run(cli) { + tracing::error!("{}", e); + std::process::exit(1); + } } diff --git a/crates/packager/src/cli/signer/generate.rs b/crates/packager/src/cli/signer/generate.rs index 3eea7a61..dad4156b 100644 --- a/crates/packager/src/cli/signer/generate.rs +++ b/crates/packager/src/cli/signer/generate.rs @@ -22,21 +22,25 @@ pub struct Options { pub fn command(mut options: Options) -> crate::Result<()> { options.ci = options.ci || std::env::var("CI").is_ok(); if options.ci && options.password.is_none() { - log::warn!("Generating a new private key without a password, for security reasons, we recommend setting a password instead."); + tracing::warn!("Generating a new private key without a password, for security reasons, we recommend setting a password instead."); options.password.replace("".into()); } - log::info!(action = "Generating"; "a new signgin key."); + tracing::info!("Generating a new signgin key."); let keypair = crate::sign::generate_key(options.password)?; match options.path { Some(path) => { let keys = crate::sign::save_keypair(&keypair, path, options.force)?; - log::info!(action = "Finished"; "generating and saving the keys:\n {}\n {}", keys.0.display(),keys.1.display()); + tracing::info!( + "Finished generating and saving the keys:\n {}\n {}", + keys.0.display(), + keys.1.display() + ); } None => { - log::info!(action = "Finished"; "generating secret key:\n{}", keypair.sk); - log::info!(action = "Finished"; "generating publick key:\n{}", keypair.pk); + tracing::info!("Finished generating secret key:\n{}", keypair.sk); + tracing::info!("Finished generating publick key:\n{}", keypair.pk); } } diff --git a/crates/packager/src/cli/signer/sign.rs b/crates/packager/src/cli/signer/sign.rs index aa54ce2d..a5595849 100644 --- a/crates/packager/src/cli/signer/sign.rs +++ b/crates/packager/src/cli/signer/sign.rs @@ -20,7 +20,7 @@ pub fn command(options: Options) -> crate::Result<()> { Some(path) if PathBuf::from(&path).exists() => std::fs::read_to_string(path)?, Some(key) => key, None => { - log::error!("--private-key was not specified, aborting signign."); + tracing::error!("--private-key was not specified, aborting signign."); std::process::exit(1); } }; @@ -31,7 +31,10 @@ pub fn command(options: Options) -> crate::Result<()> { }; let signature_path = crate::sign::sign_file(&config, options.file)?; - log::info!(action="Signed"; "the file successfully! find the signature at: {}" , signature_path.display()); + tracing::info!( + "Signed the file successfully! find the signature at: {}", + signature_path.display() + ); Ok(()) } diff --git a/crates/packager/src/codesign/macos.rs b/crates/packager/src/codesign/macos.rs index ab78b384..1c8a4ac1 100644 --- a/crates/packager/src/codesign/macos.rs +++ b/crates/packager/src/codesign/macos.rs @@ -20,17 +20,20 @@ const KEYCHAIN_PWD: &str = "cargo-packager"; // Then use the value of the base64 in APPLE_CERTIFICATE env variable. // You need to set APPLE_CERTIFICATE_PASSWORD to the password you set when you exported your certificate. // https://help.apple.com/xcode/mac/current/#/dev154b28f09 see: `Export a signing certificate` +#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))] pub fn setup_keychain( certificate_encoded: OsString, certificate_password: OsString, ) -> crate::Result<()> { // we delete any previous version of our keychain if present delete_keychain(); - log::info!("setting up keychain from environment variables..."); + + tracing::info!("Setting up keychain from environment variables..."); let keychain_list_output = Command::new("security") .args(["list-keychain", "-d", "user"]) - .output()?; + .output() + .map_err(crate::Error::FailedToListKeyChain)?; let tmp_dir = tempfile::tempdir()?; @@ -61,15 +64,18 @@ pub fn setup_keychain( Command::new("base64") .args(["--decode", "-i", &cert_path_tmp, "-o", &cert_path]) - .output_ok()?; + .output_ok() + .map_err(crate::Error::FailedToDecodeCert)?; Command::new("security") .args(["create-keychain", "-p", KEYCHAIN_PWD, KEYCHAIN_ID]) - .output_ok()?; + .output_ok() + .map_err(crate::Error::FailedToCreateKeyChain)?; Command::new("security") .args(["unlock-keychain", "-p", KEYCHAIN_PWD, KEYCHAIN_ID]) - .output_ok()?; + .output_ok() + .map_err(crate::Error::FailedToUnlockKeyChain)?; Command::new("security") .args([ @@ -86,11 +92,13 @@ pub fn setup_keychain( "-T", "/usr/bin/productbuild", ]) - .output_ok()?; + .output_ok() + .map_err(crate::Error::FailedToImportCert)?; Command::new("security") .args(["set-keychain-settings", "-t", "3600", "-u", KEYCHAIN_ID]) - .output_ok()?; + .output_ok() + .map_err(crate::Error::FailedToSetKeychainSettings)?; Command::new("security") .args([ @@ -102,7 +110,8 @@ pub fn setup_keychain( KEYCHAIN_PWD, KEYCHAIN_ID, ]) - .output_ok()?; + .output_ok() + .map_err(crate::Error::FailedToSetKeyPartitionList)?; let current_keychains = String::from_utf8_lossy(&keychain_list_output.stdout) .split('\n') @@ -117,11 +126,13 @@ pub fn setup_keychain( .args(["list-keychain", "-d", "user", "-s"]) .args(current_keychains) .arg(KEYCHAIN_ID) - .output_ok()?; + .output_ok() + .map_err(crate::Error::FailedToListKeyChain)?; Ok(()) } +#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))] pub fn delete_keychain() { // delete keychain if needed and skip any error let _ = Command::new("security") @@ -130,13 +141,18 @@ pub fn delete_keychain() { .output_ok(); } +#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))] pub fn try_sign( path_to_sign: &Path, identity: &str, config: &Config, is_an_executable: bool, ) -> crate::Result<()> { - log::info!(action = "Signing"; "{} with identity \"{}\"", path_to_sign.display(), identity); + tracing::info!( + "Signing {} with identity \"{}\"", + path_to_sign.display(), + identity + ); let packager_keychain = if let (Some(certificate_encoded), Some(certificate_password)) = ( std::env::var_os("APPLE_CERTIFICATE"), @@ -166,6 +182,7 @@ pub fn try_sign( res } +#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))] fn sign( path_to_sign: &Path, identity: &str, @@ -197,18 +214,20 @@ fn sign( Command::new("codesign") .args(args) .arg(path_to_sign) - .output_ok()?; + .output_ok() + .map_err(crate::Error::FailedToRunCodesign)?; Ok(()) } -#[derive(Deserialize)] +#[derive(Deserialize, Debug)] struct NotarytoolSubmitOutput { id: String, status: String, message: String, } +#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))] pub fn notarize( app_bundle_path: PathBuf, auth: NotarizeAuth, @@ -216,28 +235,30 @@ pub fn notarize( ) -> crate::Result<()> { let bundle_stem = app_bundle_path .file_stem() - .expect("failed to get bundle filename"); + .ok_or_else(|| crate::Error::FailedToExtractFilename(app_bundle_path.clone()))?; let tmp_dir = tempfile::tempdir()?; let zip_path = tmp_dir .path() .join(format!("{}.zip", bundle_stem.to_string_lossy())); + + let app_bundle_path_str = app_bundle_path.to_string_lossy().to_string(); + let zip_path_str = zip_path.to_string_lossy().to_string(); let zip_args = vec![ "-c", "-k", "--keepParent", "--sequesterRsrc", - app_bundle_path - .to_str() - .expect("failed to convert bundle_path to string"), - zip_path - .to_str() - .expect("failed to convert zip_path to string"), + &app_bundle_path_str, + &zip_path_str, ]; // use ditto to create a PKZip almost identical to Finder // this remove almost 99% of false alarm in notarization - Command::new("ditto").args(zip_args).output_ok()?; + Command::new("ditto") + .args(zip_args) + .output_ok() + .map_err(crate::Error::FailedToRunDitto)?; // sign the zip file if let Some(identity) = &config @@ -247,23 +268,24 @@ pub fn notarize( try_sign(&zip_path, identity, config, false)?; }; + let zip_path_str = zip_path.to_string_lossy().to_string(); + let notarize_args = vec![ "notarytool", "submit", - zip_path - .to_str() - .expect("failed to convert zip_path to string"), + &zip_path_str, "--wait", "--output-format", "json", ]; - log::info!(action = "Notarizing"; "{}", app_bundle_path.display()); + tracing::info!("Notarizing {}", app_bundle_path.display()); let output = Command::new("xcrun") .args(notarize_args) .notarytool_args(&auth) - .output_ok()?; + .output_ok() + .map_err(crate::Error::FailedToRunXcrun)?; if !output.status.success() { return Err(Error::FailedToNotarize); @@ -276,7 +298,7 @@ pub fn notarize( submit_output.status, submit_output.id, submit_output.message ); if submit_output.status == "Accepted" { - log::info!(action = "Notarizing"; "{}", log_message); + tracing::info!("Notarizing {}", log_message); staple_app(app_bundle_path)?; Ok(()) } else { @@ -289,24 +311,27 @@ pub fn notarize( } } -fn staple_app(mut app_bundle_path: PathBuf) -> crate::Result<()> { - let app_bundle_path_clone = app_bundle_path.clone(); - let filename = app_bundle_path_clone +fn staple_app(app_bundle_path: PathBuf) -> crate::Result<()> { + let filename = app_bundle_path .file_name() - .expect("failed to get bundle filename") - .to_str() - .expect("failed to convert bundle filename to string"); + .ok_or_else(|| crate::Error::FailedToExtractFilename(app_bundle_path.clone()))? + .to_string_lossy() + .to_string(); - app_bundle_path.pop(); + let app_bundle_path_dir = app_bundle_path + .parent() + .ok_or_else(|| crate::Error::ParentDirNotFound(app_bundle_path.clone()))?; Command::new("xcrun") - .args(vec!["stapler", "staple", "-v", filename]) - .current_dir(app_bundle_path) - .output_ok()?; + .args(vec!["stapler", "staple", "-v", &filename]) + .current_dir(app_bundle_path_dir) + .output_ok() + .map_err(crate::Error::FailedToRunXcrun)?; Ok(()) } +#[derive(Debug)] pub enum NotarizeAuth { AppleId { apple_id: String, @@ -346,6 +371,7 @@ impl NotarytoolCmdExt for Command { } } +#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))] pub fn notarize_auth() -> crate::Result { match ( std::env::var_os("APPLE_ID"), diff --git a/crates/packager/src/codesign/windows.rs b/crates/packager/src/codesign/windows.rs index 96a028ca..4f085d9b 100644 --- a/crates/packager/src/codesign/windows.rs +++ b/crates/packager/src/codesign/windows.rs @@ -1,6 +1,7 @@ #![cfg(windows)] use std::{ + fmt::Debug, path::{Path, PathBuf}, process::Command, }; @@ -17,6 +18,7 @@ use crate::{ util::{self, display_path, Bitness}, }; +#[derive(Debug)] pub struct SignParams { pub product_name: String, pub digest_algorithm: String, @@ -26,6 +28,7 @@ pub struct SignParams { } static SIGN_TOOL: Lazy> = Lazy::new(|| { + let _s = tracing::span!(tracing::Level::TRACE, "locate_signtool"); const INSTALLED_ROOTS_REGKEY_PATH: &str = r"SOFTWARE\Microsoft\Windows Kits\Installed Roots"; const KITS_ROOT_REGVALUE_NAME: &str = r"KitsRoot10"; @@ -86,14 +89,18 @@ static SIGN_TOOL: Lazy> = Lazy::new(|| { Err(crate::Error::SignToolNotFound) }); -fn locate_signtool() -> Option { +fn signtool() -> Option { (*SIGN_TOOL).as_ref().ok().cloned() } -pub fn sign_command>(path: P, params: &SignParams) -> crate::Result { - let signtool = locate_signtool().ok_or(crate::Error::SignToolNotFound)?; +#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))] +pub fn sign_command + Debug>( + path: P, + params: &SignParams, +) -> crate::Result { + let signtool = signtool().ok_or(crate::Error::SignToolNotFound)?; - let mut cmd = Command::new(&signtool); + let mut cmd = Command::new(signtool); cmd.arg("sign"); cmd.args(["/fd", ¶ms.digest_algorithm]); cmd.args(["/sha1", ¶ms.certificate_thumbprint]); @@ -147,21 +154,27 @@ impl ConfigSignExt for Config { } } -pub fn sign>(path: P, params: &SignParams) -> crate::Result<()> { - let signtool = locate_signtool().ok_or(crate::Error::SignToolNotFound)?; +#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))] +pub fn sign + Debug>(path: P, params: &SignParams) -> crate::Result<()> { + let signtool = signtool().ok_or(crate::Error::SignToolNotFound)?; let path = path.as_ref(); - log::info!(action = "Signing"; "{} with identity \"{}\"", display_path(path), params.certificate_thumbprint); + tracing::info!( + "Signing {} with identity \"{}\"", + display_path(path), + params.certificate_thumbprint + ); - log::debug!("Running signtool {:?}", signtool); + tracing::debug!("Running signtool {:?}", signtool); let mut cmd = sign_command(path, params)?; - let output = cmd.output_ok()?; + let output = cmd.output_ok().map_err(crate::Error::SignToolFailed)?; let stdout = String::from_utf8_lossy(output.stdout.as_slice()).into_owned(); - log::info!("{:?}", stdout); + tracing::info!("{:?}", stdout); Ok(()) } +#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))] pub fn try_sign( file_path: &std::path::PathBuf, config: &crate::config::Config, @@ -170,7 +183,7 @@ pub fn try_sign( if let Some(certificate_thumbprint) = windows_config.and_then(|c| c.certificate_thumbprint.as_ref()) { - log::info!(action = "Signing"; "{}", util::display_path(file_path)); + tracing::info!("Signing {}", util::display_path(file_path)); sign( file_path, &SignParams { diff --git a/crates/packager/src/config.rs b/crates/packager/src/config.rs index f4510b0e..c9b591c6 100644 --- a/crates/packager/src/config.rs +++ b/crates/packager/src/config.rs @@ -64,9 +64,9 @@ impl ConfigExt for Config { } fn target_triple(&self) -> String { - self.target_triple - .clone() - .unwrap_or_else(|| util::target_triple().unwrap()) + self.target_triple.clone().unwrap_or_else(|| { + util::target_triple().expect("Failed to detect current target triple") + }) } fn target_arch(&self) -> crate::Result<&str> { @@ -154,9 +154,9 @@ impl ConfigExtInternal for Config { #[inline] fn resources_from_glob(glob: &str) -> crate::Result> { let mut out = Vec::new(); - for src in glob::glob(glob).unwrap() { + for src in glob::glob(glob)? { let src = dunce::canonicalize(src?)?; - let target = PathBuf::from(src.file_name().unwrap()); + let target = PathBuf::from(src.file_name().unwrap_or_default()); out.push(IResource { src, target }) } Ok(out) @@ -170,7 +170,7 @@ impl ConfigExtInternal for Config { Resource::Single(src) => { let src_dir = PathBuf::from(src); if src_dir.is_dir() { - let target_dir = Path::new(src_dir.file_name().unwrap()); + let target_dir = Path::new(src_dir.file_name().unwrap_or_default()); out.extend(Self::resources_from_dir(&src_dir, target_dir)?); } else { out.extend(Self::resources_from_glob(src)?); @@ -223,7 +223,10 @@ impl ConfigExtInternal for Config { fn copy_resources(&self, path: &Path) -> crate::Result<()> { for resource in self.resources()? { let dest = path.join(resource.target); - std::fs::create_dir_all(dest.parent().ok_or(crate::Error::ParentDirNotFound)?)?; + std::fs::create_dir_all( + dest.parent() + .ok_or_else(|| crate::Error::ParentDirNotFound(dest.to_path_buf()))?, + )?; std::fs::copy(resource.src, dest)?; } Ok(()) @@ -235,7 +238,7 @@ impl ConfigExtInternal for Config { let src = dunce::canonicalize(PathBuf::from(src))?; let file_name_no_triple = src .file_name() - .expect("failed to extract external binary filename") + .ok_or_else(|| crate::Error::FailedToExtractFilename(src.clone()))? .to_string_lossy() .replace(&format!("-{}", self.target_triple()), ""); let dest = path.join(file_name_no_triple); diff --git a/crates/packager/src/error.rs b/crates/packager/src/error.rs index e08b7684..b2dadcb1 100644 --- a/crates/packager/src/error.rs +++ b/crates/packager/src/error.rs @@ -31,7 +31,7 @@ pub enum Error { #[error(transparent)] Hex(#[from] hex::FromHexError), /// Failed to validate downloaded file hash. - #[error("hash mismatch of downloaded file")] + #[error("Hash mismatch of downloaded file")] HashError, /// Zip error. #[error(transparent)] @@ -40,7 +40,7 @@ pub enum Error { #[error(transparent)] DownloadError(#[from] Box), /// Unsupported OS bitness. - #[error("unsupported OS bitness")] + #[error("Unsupported OS bitness")] UnsupportedBitness, /// Windows SignTool not found. #[error("SignTool not found")] @@ -51,16 +51,17 @@ pub enum Error { /// Unsupported architecture. #[error("Unsupported architecture for \"{0}\" target triple: {0}")] UnsupportedArch(String, String), - #[error("Couldn't find the main binary in list of provided binaries")] + /// Could not find the main binary in list of provided binaries. + #[error("Could not find the main binary in list of provided binaries")] MainBinaryNotFound, /// Semver parsing error #[error(transparent)] Semver(#[from] semver::Error), /// Non-numeric build metadata in app version. - #[error("optional build metadata in app version must be numeric-only {}", .0.clone().unwrap_or_default())] + #[error("Optional build metadata in app version must be numeric-only {}", .0.clone().unwrap_or_default())] NonNumericBuildMetadata(Option), /// Invalid app version when building [crate::PackageFormat::Wix] - #[error("invalid app version: {0}")] + #[error("Invalid app version: {0}")] InvalidAppVersion(String), /// Handlebars render error. #[error(transparent)] @@ -69,16 +70,29 @@ pub enum Error { #[error(transparent)] HandleBarsTemplateError(#[from] Box), /// Nsis error - #[error("error running makensis.exe: {0}")] - NsisFailed(String), + #[error("Error running makensis.exe: {0}")] + NsisFailed(std::io::Error), /// Nsis error - #[error("error running {0}: {0}")] - WixFailed(String, String), + #[error("Error running {0}: {0}")] + WixFailed(String, std::io::Error), + /// create-dmg script error + #[error("Error running create-dmg script: {0}")] + CreateDmgFailed(std::io::Error), + /// create-dmg script error + #[error("Error running signtool.exe: {0}")] + SignToolFailed(std::io::Error), + /// bundle_appimage script error + #[error("Error running bundle_appimage.sh script: {0}")] + AppImageScriptFailed(std::io::Error), /// Failed to get parent directory of a path - #[error("Failed to get parent directory of a path")] - ParentDirNotFound, + #[error("Failed to get parent directory of {0}")] + ParentDirNotFound(std::path::PathBuf), + /// A hook, for example `beforePackagaingCommand`, has failed. + #[error("{0} `{1}` failed: {2}")] + HookCommandFailure(String, String, std::io::Error), + /// A hook, for example `beforePackagaingCommand`, has failed with an exit code. #[error("{0} `{1}` failed with exit code {2}")] - HookCommandFailure(String, String, i32), + HookCommandFailureWithExitCode(String, String, i32), /// Regex error. #[cfg(windows)] #[error(transparent)] @@ -90,17 +104,19 @@ pub enum Error { #[error(transparent)] Glob(#[from] glob::GlobError), /// Unsupported WiX language - #[error("Language {0} not found. It must be one of {1}")] + #[cfg(windows)] + #[error("Wix language {0} not found. It must be one of {1}")] UnsupportedWixLanguage(String, String), - /// image crate errors. - #[error(transparent)] + /// Image crate errors. #[cfg(any( + target_os = "macos", target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd" ))] + #[error(transparent)] ImageError(#[from] image::ImageError), /// walkdir crate errors. #[error(transparent)] @@ -108,59 +124,74 @@ pub enum Error { /// Path prefix strip error. #[error(transparent)] StripPrefixError(#[from] std::path::StripPrefixError), - /// std::process::Command program failed - #[error("Command failed")] - CommandFailed, /// Relative paths errors #[error(transparent)] RelativeToError(#[from] relative_path::RelativeToError), /// Time error. - #[cfg(target_os = "macos")] #[error("`{0}`")] + #[cfg(target_os = "macos")] TimeError(#[from] time::error::Error), /// Plist error. - #[cfg(target_os = "macos")] #[error(transparent)] + #[cfg(target_os = "macos")] Plist(#[from] plist::Error), /// Framework not found. - #[cfg(target_os = "macos")] - #[error("framework {0} not found")] + #[error("Framework {0} not found")] FrameworkNotFound(String), /// Invalid framework. - #[cfg(target_os = "macos")] - #[error("invalid framework {framework}: {reason}")] + #[error("Invalid framework {framework}: {reason}")] InvalidFramework { framework: String, reason: &'static str, }, - /// Image error. - #[cfg(target_os = "macos")] - #[error(transparent)] - ImageError(#[from] image::ImageError), /// Invalid icons. - #[cfg(target_os = "macos")] - #[error("could not find a valid icon")] + #[error("Could not find a valid icon")] InvalidIconList, /// Failed to notarize. - #[cfg(target_os = "macos")] - #[error("failed to notarize app")] + #[error("Failed to notarize app")] FailedToNotarize, /// Rejected on notarize. - #[cfg(target_os = "macos")] - #[error("failed to notarize app: {0}")] + #[error("Failed to notarize app: {0}")] NotarizeRejected(String), /// Failed to parse notarytool output. - #[cfg(target_os = "macos")] - #[error("failed to parse notarytool output as JSON: `{0}`")] + #[error("Failed to parse notarytool output as JSON: `{0}`")] FailedToParseNotarytoolOutput(String), /// Failed to find API key file. - #[cfg(target_os = "macos")] - #[error("could not find API key file. Please set the APPLE_API_KEY_PATH environment variables to the path to the {filename} file")] + #[error("Could not find API key file. Please set the APPLE_API_KEY_PATH environment variables to the path to the {filename} file")] ApiKeyMissing { filename: String }, /// Missing notarize environment variables. - #[cfg(target_os = "macos")] - #[error("no APPLE_ID & APPLE_PASSWORD or APPLE_API_KEY & APPLE_API_ISSUER & APPLE_API_KEY_PATH environment variables found")] + #[error("Could not find APPLE_ID & APPLE_PASSWORD or APPLE_API_KEY & APPLE_API_ISSUER & APPLE_API_KEY_PATH environment variables found")] MissingNotarizeAuthVars, + /// Failed to list keychains + #[error("Failed to list keychains: {0}")] + FailedToListKeyChain(std::io::Error), + /// Failed to decode certficate as base64 + #[error("Failed to decode certficate as base64: {0}")] + FailedToDecodeCert(std::io::Error), + /// Failed to create keychain. + #[error("Failed to create keychain: {0}")] + FailedToCreateKeyChain(std::io::Error), + /// Failed to create keychain. + #[error("Failed to unlock keychain: {0}")] + FailedToUnlockKeyChain(std::io::Error), + /// Failed to import certificate. + #[error("Failed to import certificate: {0}")] + FailedToImportCert(std::io::Error), + /// Failed to set keychain settings. + #[error("Failed to set keychain settings: {0}")] + FailedToSetKeychainSettings(std::io::Error), + /// Failed to set key partition list. + #[error("Failed to set key partition list: {0}")] + FailedToSetKeyPartitionList(std::io::Error), + /// Failed to run codesign utility. + #[error("Failed to run codesign utility: {0}")] + FailedToRunCodesign(std::io::Error), + /// Failed to run ditto utility. + #[error("Failed to run ditto utility: {0}")] + FailedToRunDitto(std::io::Error), + /// Failed to run xcrun utility. + #[error("Failed to run xcrun utility: {0}")] + FailedToRunXcrun(std::io::Error), /// Path already exists. #[error("{0} already exists")] AlreadyExists(PathBuf), @@ -170,11 +201,8 @@ pub enum Error { /// Path is not a directory. #[error("{0} is not a directory")] IsNotDirectory(PathBuf), - /// Failed to run command. - #[error("failed to run command {0}")] - FailedToRunCommand(String), - /// Couldn't find a square icon to use as AppImage icon - #[error("couldn't find a square icon to use as AppImage icon")] + /// Could not find a square icon to use as AppImage icon + #[error("Could not find a square icon to use as AppImage icon")] AppImageSquareIcon, /// Base64 decoding error. #[error(transparent)] @@ -189,8 +217,11 @@ pub enum Error { #[error(transparent)] SystemTimeError(#[from] std::time::SystemTimeError), /// Signing keys generation error. - #[error("Key generation aborted, {0} already exists and force overrwite wasnot desired.")] + #[error("aborted key generation, {0} already exists and force overrwite wasnot desired.")] SigningKeyExists(PathBuf), + /// Failed to extract external binary filename + #[error("Failed to extract filename from {0}")] + FailedToExtractFilename(PathBuf), } /// Convenient type alias of Result type for cargo-packager. diff --git a/crates/packager/src/lib.rs b/crates/packager/src/lib.rs index 8d167f61..2c6eea2b 100644 --- a/crates/packager/src/lib.rs +++ b/crates/packager/src/lib.rs @@ -37,6 +37,7 @@ pub use package::{package, PackageOuput}; /// /// If `packages` contain a directory in the case of [`PackageFormat::App`] /// it will zip the directory before signing and appends it to `packages`. +#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))] pub fn sign_outputs( config: &SigningConfig, packages: &mut Vec, @@ -46,11 +47,12 @@ pub fn sign_outputs( for path in &package.paths.clone() { let path = if path.is_dir() { let extension = path.extension().unwrap_or_default().to_string_lossy(); - let zip = path.with_extension(format!( + let extension = format!( "{}{}tar.gz", extension, if extension.is_empty() { "" } else { "." } - )); + ); + let zip = path.with_extension(extension); let dest_file = util::create_file(&zip)?; let gzip_encoder = libflate::gzip::Encoder::new(dest_file)?; util::create_tar_from_dir(path, gzip_encoder)?; @@ -72,6 +74,7 @@ pub fn sign_outputs( /// This is similar to calling `sign_outputs(signing_config, package(config)?)` /// /// Returns a tuple of list of packages and list of signatures. +#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))] pub fn package_and_sign( config: &Config, signing_config: &SigningConfig, diff --git a/crates/packager/src/main.rs b/crates/packager/src/main.rs index 9cfb017d..2ba43553 100644 --- a/crates/packager/src/main.rs +++ b/crates/packager/src/main.rs @@ -1,6 +1,3 @@ fn main() { - if let Err(e) = cargo_packager::cli::try_run() { - log::error!("{}", e); - std::process::exit(1); - } + cargo_packager::cli::run() } diff --git a/crates/packager/src/package/app/mod.rs b/crates/packager/src/package/app/mod.rs index 1901413e..582b2e3d 100644 --- a/crates/packager/src/package/app/mod.rs +++ b/crates/packager/src/package/app/mod.rs @@ -7,6 +7,7 @@ use crate::{ util, }; +#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))] pub(crate) fn package(ctx: &Context) -> crate::Result> { let Context { config, .. } = ctx; // we should use the bundle name (App name) as a MacOS standard. @@ -14,7 +15,11 @@ pub(crate) fn package(ctx: &Context) -> crate::Result> { let app_product_name = format!("{}.app", config.product_name); let app_bundle_path = config.out_dir().join(&app_product_name); - log::info!(action = "Packaging"; "{} ({})", app_product_name, app_bundle_path.display()); + tracing::info!( + "Packaging {} ({})", + app_product_name, + app_bundle_path.display() + ); let contents_directory = app_bundle_path.join("Contents"); std::fs::create_dir_all(&contents_directory)?; @@ -24,19 +29,19 @@ pub(crate) fn package(ctx: &Context) -> crate::Result> { let bundle_icon_file = util::create_icns_file(&resources_dir, config)?; - log::debug!("creating Info.plist"); + tracing::debug!("Creating Info.plist"); create_info_plist(&contents_directory, bundle_icon_file, config)?; - log::debug!("copying frameworks"); + tracing::debug!("Copying frameworks"); copy_frameworks_to_bundle(&contents_directory, config)?; - log::debug!("copying resources"); + tracing::debug!("Copying resources"); config.copy_resources(&resources_dir)?; - log::debug!("copying external binaries"); + tracing::debug!("Copying external binaries"); config.copy_external_binaries(&bin_dir)?; - log::debug!("copying binaries"); + tracing::debug!("Copying binaries"); let bin_dir = contents_directory.join("MacOS"); std::fs::create_dir_all(&bin_dir)?; for bin in &config.binaries { @@ -48,17 +53,17 @@ pub(crate) fn package(ctx: &Context) -> crate::Result> { .macos() .and_then(|macos| macos.signing_identity.as_ref()) { - log::debug!(action = "Codesigning"; "{}", app_bundle_path.display()); + tracing::debug!("Codesigning {}", app_bundle_path.display()); codesign::try_sign(&app_bundle_path, identity, config, true)?; // notarization is required for distribution match codesign::notarize_auth() { Ok(auth) => { - log::debug!(action = "Notarizing"; "{}", app_bundle_path.display()); + tracing::debug!("Notarizing {}", app_bundle_path.display()); codesign::notarize(app_bundle_path.clone(), auth, config)?; } Err(e) => { - log::warn!("skipping app notarization, {}", e.to_string()); + tracing::warn!("Skipping app notarization, {}", e.to_string()); } } } @@ -67,6 +72,7 @@ pub(crate) fn package(ctx: &Context) -> crate::Result> { } // Creates the Info.plist file. +#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))] fn create_info_plist( contents_directory: &Path, bundle_icon_file: Option, @@ -92,7 +98,7 @@ fn create_info_plist( plist.insert( "CFBundleIconFile".into(), path.file_name() - .expect("No file name") + .ok_or_else(|| crate::Error::FailedToExtractFilename(path.clone()))? .to_string_lossy() .into_owned() .into(), @@ -197,6 +203,7 @@ fn create_info_plist( Ok(()) } +#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))] fn copy_dir(from: &Path, to: &Path) -> crate::Result<()> { if !from.exists() { return Err(crate::Error::AlreadyExists(from.to_path_buf())); @@ -208,7 +215,9 @@ fn copy_dir(from: &Path, to: &Path) -> crate::Result<()> { return Err(crate::Error::AlreadyExists(to.to_path_buf())); } - let parent = to.parent().expect("No data in parent"); + let parent = to + .parent() + .ok_or_else(|| crate::Error::ParentDirNotFound(to.to_path_buf()))?; std::fs::create_dir_all(parent)?; for entry in walkdir::WalkDir::new(from) { let entry = entry?; @@ -228,6 +237,7 @@ fn copy_dir(from: &Path, to: &Path) -> crate::Result<()> { } // Copies the framework under `{src_dir}/{framework}.framework` to `{dest_dir}/{framework}.framework`. +#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))] fn copy_framework_from(dest_dir: &Path, framework: &str, src_dir: &Path) -> crate::Result { let src_name = format!("{}.framework", framework); let src_path = src_dir.join(&src_name); @@ -240,6 +250,7 @@ fn copy_framework_from(dest_dir: &Path, framework: &str, src_dir: &Path) -> crat } // Copies the macOS application bundle frameworks to the .app +#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))] fn copy_frameworks_to_bundle(contents_directory: &Path, config: &Config) -> crate::Result<()> { if let Some(frameworks) = config.macos().and_then(|m| m.frameworks.as_ref()) { let dest_dir = contents_directory.join("Frameworks"); @@ -250,7 +261,7 @@ fn copy_frameworks_to_bundle(contents_directory: &Path, config: &Config) -> crat let src_path = PathBuf::from(framework); let src_name = src_path .file_name() - .expect("Couldn't get framework filename"); + .ok_or_else(|| crate::Error::FailedToExtractFilename(src_path.clone()))?; copy_dir(&src_path, &dest_dir.join(src_name))?; continue; } else if framework.ends_with(".dylib") { @@ -258,7 +269,9 @@ fn copy_frameworks_to_bundle(contents_directory: &Path, config: &Config) -> crat if !src_path.exists() { return Err(crate::Error::FrameworkNotFound(framework.to_string())); } - let src_name = src_path.file_name().expect("Couldn't get library filename"); + let src_name = src_path + .file_name() + .ok_or_else(|| crate::Error::FailedToExtractFilename(src_path.clone()))?; std::fs::create_dir_all(&dest_dir)?; std::fs::copy(&src_path, dest_dir.join(src_name))?; continue; diff --git a/crates/packager/src/package/appimage/mod.rs b/crates/packager/src/package/appimage/mod.rs index 35b6a21a..61b7b033 100644 --- a/crates/packager/src/package/appimage/mod.rs +++ b/crates/packager/src/package/appimage/mod.rs @@ -14,6 +14,7 @@ use crate::{ util, }; +#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))] fn donwload_dependencies( ctx: &Context, appimage_tools_path: &Path, @@ -47,7 +48,10 @@ fn donwload_dependencies( let path = appimage_tools_path.join(path); if !path.exists() { let data = util::download(&url)?; - log::debug!(action = "Writing"; "{} and setting its permissions to 764", path.display()); + tracing::debug!( + "Writing {} and setting its permissions to 764", + path.display() + ); std::fs::write(&path, data)?; std::fs::set_permissions(path, std::fs::Permissions::from_mode(0o764))?; } @@ -56,6 +60,7 @@ fn donwload_dependencies( Ok(()) } +#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))] pub(crate) fn package(ctx: &Context) -> crate::Result> { let Context { config, @@ -80,7 +85,7 @@ pub(crate) fn package(ctx: &Context) -> crate::Result> { let intermediates_path = intermediates_path.join("appimage"); // generate deb_folder structure - log::debug!("generating data"); + tracing::debug!("Generating data"); let icons = super::deb::generate_data(config, &appimage_deb_data_dir)?; let icons: Vec = icons.into_iter().collect(); @@ -144,21 +149,28 @@ pub(crate) fn package(ctx: &Context) -> crate::Result> { handlebars.register_escape_fn(handlebars::no_escape); handlebars .register_template_string("appimage", include_str!("appimage")) - .expect("Failed to register template for handlebars"); + .map_err(Box::new)?; let template = handlebars.render("appimage", &sh_map)?; - let sh_file = intermediates_path.join("build_appimage.sh"); - log::debug!(action = "Writing"; "{template} and setting its permissions to 764"); + let sh_file = intermediates_path.join("build_appimage.sh"); + tracing::debug!( + "Writing {} and setting its permissions to 764", + sh_file.display() + ); std::fs::write(&sh_file, template)?; std::fs::set_permissions(&sh_file, std::fs::Permissions::from_mode(0o764))?; - log::info!(action = "Packaging"; "{} ({})", appimage_filename, appimage_path.display()); + tracing::info!( + "Packaging {} ({})", + appimage_filename, + appimage_path.display() + ); // execute the shell script to build the appimage. Command::new(&sh_file) .current_dir(intermediates_path) .output_ok() - .expect("error running appimage.sh"); + .map_err(crate::Error::AppImageScriptFailed)?; Ok(vec![appimage_path]) } diff --git a/crates/packager/src/package/context.rs b/crates/packager/src/package/context.rs index ff3de2b8..45eae61b 100644 --- a/crates/packager/src/package/context.rs +++ b/crates/packager/src/package/context.rs @@ -3,6 +3,7 @@ use std::path::PathBuf; use crate::{config::ConfigExt, util, Config}; /// The packaging context info +#[derive(Debug)] pub struct Context { /// The config for the app we are packaging pub config: Config, diff --git a/crates/packager/src/package/deb/mod.rs b/crates/packager/src/package/deb/mod.rs index e2780753..73d98a12 100644 --- a/crates/packager/src/package/deb/mod.rs +++ b/crates/packager/src/package/deb/mod.rs @@ -28,15 +28,17 @@ pub struct DebIcon { } /// Generate the icon files and store them under the `data_dir`. +#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))] fn generate_icon_files(config: &Config, data_dir: &Path) -> crate::Result> { let hicolor_dir = data_dir.join("usr/share/icons/hicolor"); + let main_binary_name = config.main_binary_name()?; let get_dest_path = |width: u32, height: u32, is_high_density: bool| { hicolor_dir.join(format!( "{}x{}{}/apps/{}.png", width, height, if is_high_density { "@2" } else { "" }, - config.main_binary_name().unwrap() + main_binary_name )) }; let mut icons_set = BTreeSet::new(); @@ -65,7 +67,7 @@ fn generate_icon_files(config: &Config, data_dir: &Path) -> crate::Result crate::Result crate::Result<()> { let bin_name = config.main_binary_name()?; let desktop_file_name = format!("{}.desktop", bin_name); @@ -139,27 +142,28 @@ fn generate_desktop_file(config: &Config, data_dir: &Path) -> crate::Result<()> Ok(()) } +#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))] pub fn generate_data(config: &Config, data_dir: &Path) -> crate::Result> { let bin_dir = data_dir.join("usr/bin"); - log::debug!("copying binaries"); + tracing::debug!("Copying binaries"); std::fs::create_dir_all(&bin_dir)?; for bin in config.binaries.iter() { let bin_path = config.binary_path(bin); std::fs::copy(&bin_path, bin_dir.join(&bin.filename))?; } - log::debug!("copying resources"); + tracing::debug!("Copying resources"); let resource_dir = data_dir.join("usr/lib").join(config.main_binary_name()?); config.copy_resources(&resource_dir)?; - log::debug!("copying external binaries"); + tracing::debug!("Copying external binaries"); config.copy_external_binaries(&bin_dir)?; - log::debug!("generating icons"); + tracing::debug!("Generating icons"); let icons = generate_icon_files(config, data_dir)?; - log::debug!("generating desktop file"); + tracing::debug!("Generating desktop file"); generate_desktop_file(config, data_dir)?; Ok(icons) @@ -186,6 +190,7 @@ pub fn get_size>(path: P) -> crate::Result { } /// Copies user-defined files to the deb package. +#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))] fn copy_custom_files(config: &Config, data_dir: &Path) -> crate::Result<()> { if let Some(files) = config.deb().and_then(|d| d.files.as_ref()) { for (src, target) in files.iter() { @@ -199,7 +204,9 @@ fn copy_custom_files(config: &Config, data_dir: &Path) -> crate::Result<()> { if src.is_file() { let dest = data_dir.join(target); - let parent = dest.parent().ok_or(crate::Error::ParentDirNotFound)?; + let parent = dest + .parent() + .ok_or_else(|| crate::Error::ParentDirNotFound(dest.clone()))?; std::fs::create_dir_all(parent)?; std::fs::copy(src, dest)?; } else if src.is_dir() { @@ -221,6 +228,7 @@ fn copy_custom_files(config: &Config, data_dir: &Path) -> crate::Result<()> { } /// Generates the debian control file and stores it under the `control_dir`. +#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))] fn generate_control_file( config: &Config, arch: &str, @@ -275,6 +283,7 @@ fn generate_control_file( /// Create an `md5sums` file in the `control_dir` containing the MD5 checksums /// for each file within the `data_dir`. +#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))] fn generate_md5sums(control_dir: &Path, data_dir: &Path) -> crate::Result<()> { let md5sums_path = control_dir.join("md5sums"); let mut md5sums_file = util::create_file(&md5sums_path)?; @@ -325,6 +334,7 @@ fn create_archive(srcs: Vec, dest: &Path) -> crate::Result<()> { Ok(()) } +#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))] pub(crate) fn package(ctx: &Context) -> crate::Result> { let Context { config, @@ -349,38 +359,38 @@ pub(crate) fn package(ctx: &Context) -> crate::Result> { let deb_dir = intermediates_path.join(&deb_base_name); let deb_path = config.out_dir().join(&deb_name); - log::info!(action = "Packaging"; "{} ({})", deb_name, deb_path.display()); + tracing::info!("Packaging {} ({})", deb_name, deb_path.display()); - log::debug!("generating data"); + tracing::debug!("Generating data"); let data_dir = deb_dir.join("data"); let _ = generate_data(config, &data_dir)?; - log::debug!("copying files specifeid in `deb.files`"); + tracing::debug!("Copying files specifeid in `deb.files`"); copy_custom_files(config, &data_dir)?; let control_dir = deb_dir.join("control"); - log::debug!("generating control file"); + tracing::debug!("Generating control file"); generate_control_file(config, arch, &control_dir, &data_dir)?; - log::debug!("generating md5sums"); + tracing::debug!("Generating md5sums"); generate_md5sums(&control_dir, &data_dir)?; // Generate `debian-binary` file; see // http://www.tldp.org/HOWTO/Debian-Binary-Package-Building-HOWTO/x60.html#AEN66 - log::debug!("creating debian-binary file"); + tracing::debug!("Creating debian-binary file"); let debian_binary_path = deb_dir.join("debian-binary"); let mut file = util::create_file(&debian_binary_path)?; file.write_all(b"2.0\n")?; file.flush()?; // Apply tar/gzip/ar to create the final package file. - log::debug!("tar_and_gzip control dir"); + tracing::debug!("Zipping control dir using tar and gzip"); let control_tar_gz_path = tar_and_gzip_dir(control_dir)?; - log::debug!("tar_and_gzip data dir"); + tracing::debug!("Zipping data dir using tar and gzip"); let data_tar_gz_path = tar_and_gzip_dir(data_dir)?; - log::debug!("creating final archive: {}", deb_path.display()); + tracing::debug!("Creating final archive: {}", deb_path.display()); create_archive( vec![debian_binary_path, control_tar_gz_path, data_tar_gz_path], &deb_path, diff --git a/crates/packager/src/package/dmg/mod.rs b/crates/packager/src/package/dmg/mod.rs index eecf6c2f..dd7f39de 100644 --- a/crates/packager/src/package/dmg/mod.rs +++ b/crates/packager/src/package/dmg/mod.rs @@ -11,6 +11,7 @@ use crate::{ const CREATE_DMG_URL: &str = "https://raw.githubusercontent.com/create-dmg/create-dmg/28867ba3563ddef62f55dcf130677103b4296c42/create-dmg"; +#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))] pub(crate) fn package(ctx: &Context) -> crate::Result> { let Context { config, @@ -36,7 +37,7 @@ pub(crate) fn package(ctx: &Context) -> crate::Result> { let dmg_name = format!("{}.dmg", &package_base_name); let dmg_path = out_dir.join(&dmg_name); - log::info!(action = "Packaging"; "{} ({})", dmg_name, dmg_path.display()); + tracing::info!("Packaging {} ({})", dmg_name, dmg_path.display()); if dmg_path.exists() { std::fs::remove_file(&dmg_path)?; @@ -56,9 +57,12 @@ pub(crate) fn package(ctx: &Context) -> crate::Result> { std::fs::create_dir_all(&dmg_tools_path)?; } if !create_dmg_script_path.exists() { - log::debug!("Downloading create-dmg script"); + tracing::debug!("Downloading create-dmg script"); let data = download(CREATE_DMG_URL)?; - log::debug!(action = "Writing"; "{} and setting its permissions to 764", create_dmg_script_path.display()); + tracing::debug!( + "Writing {} and setting its permissions to 764", + create_dmg_script_path.display() + ); std::fs::write(&create_dmg_script_path, data)?; std::fs::set_permissions( &create_dmg_script_path, @@ -66,13 +70,13 @@ pub(crate) fn package(ctx: &Context) -> crate::Result> { )?; } - log::debug!(action = "Writing"; "template.applescript"); + tracing::debug!("Writing template.applescript"); std::fs::write( support_directory_path.join("template.applescript"), include_str!("template.applescript"), )?; - log::debug!(action = "Writing"; "eula-resources-template.xml"); + tracing::debug!("Writing eula-resources-template.xml"); std::fs::write( support_directory_path.join("eula-resources-template.xml"), include_str!("eula-resources-template.xml"), @@ -95,6 +99,7 @@ pub(crate) fn package(ctx: &Context) -> crate::Result> { &app_bundle_file_name, ]; + tracing::debug!("Creating icns file"); let icns_icon_path = util::create_icns_file(&intermediates_path, config)? .map(|path| path.to_string_lossy().to_string()); if let Some(icon) = &icns_icon_path { @@ -122,21 +127,22 @@ pub(crate) fn package(ctx: &Context) -> crate::Result> { } } - log::info!(action = "Running"; "create-dmg"); + tracing::info!("Running create-dmg"); // execute the bundle script Command::new(&create_dmg_script_path) .current_dir(&out_dir) .args(args) .args(vec![dmg_name.as_str(), app_bundle_file_name.as_str()]) - .output_ok()?; + .output_ok() + .map_err(crate::Error::CreateDmgFailed)?; // Sign DMG if needed if let Some(identity) = &config .macos() .and_then(|macos| macos.signing_identity.as_ref()) { - log::debug!(action = "Codesigning"; "{}", dmg_path.display()); + tracing::debug!("Codesigning {}", dmg_path.display()); codesign::try_sign(&dmg_path, identity, config, false)?; } diff --git a/crates/packager/src/package/mod.rs b/crates/packager/src/package/mod.rs index ce641827..24b86dd7 100644 --- a/crates/packager/src/package/mod.rs +++ b/crates/packager/src/package/mod.rs @@ -1,6 +1,6 @@ use std::path::PathBuf; -use crate::{util, Config, PackageFormat}; +use crate::{shell::CommandExt, util, Config, PackageFormat}; use self::context::Context; @@ -40,6 +40,7 @@ pub struct PackageOuput { } /// Package an app using the specified config. +#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))] pub fn package(config: &Config) -> crate::Result> { let mut formats = config .formats @@ -117,7 +118,7 @@ pub fn package(config: &Config) -> crate::Result> { PackageFormat::AppImage => appimage::package(&ctx), _ => { - log::warn!("ignoring {}", format.short_name()); + tracing::warn!("ignoring {}", format.short_name()); continue; } }?; @@ -139,7 +140,7 @@ pub fn package(config: &Config) -> crate::Result> { .map(|b| b.paths) { for path in &app_bundle_paths { - log::debug!(action = "Cleaning"; "{}", path.display()); + tracing::debug!("Cleaning {}", path.display()); match path.is_dir() { true => std::fs::remove_dir_all(path)?, false => std::fs::remove_file(path)?, @@ -172,17 +173,24 @@ fn run_before_each_packaging_command_hook( } }; - log::info!(action = "Running"; "[\x1b[34m{}\x1b[0m] beforeEachPackageCommand `{}`", format, script); - let status = cmd + tracing::info!("Running beforeEachPackageCommand [{format}] `{script}`"); + let output = cmd .env("CARGO_PACKAGER_FORMATS", formats_comma_separated) .env("CARGO_PACKAGER_FORMAT", format) - .status()?; - - if !status.success() { - return Err(crate::Error::HookCommandFailure( + .output_ok() + .map_err(|e| { + crate::Error::HookCommandFailure( + "beforeEachPackageCommand".into(), + script.into(), + e, + ) + })?; + + if !output.status.success() { + return Err(crate::Error::HookCommandFailureWithExitCode( "beforeEachPackageCommand".into(), script.into(), - status.code().unwrap_or_default(), + output.status.code().unwrap_or_default(), )); } } @@ -209,16 +217,19 @@ fn run_before_packaging_command_hook( } }; - log::info!(action = "Running"; "beforePackagingCommand `{}`", script); - let status = cmd + tracing::info!("Running beforePackageCommand `{script}`"); + let output = cmd .env("CARGO_PACKAGER_FORMATS", formats_comma_separated) - .status()?; + .output_ok() + .map_err(|e| { + crate::Error::HookCommandFailure("beforePackagingCommand".into(), script.into(), e) + })?; - if !status.success() { - return Err(crate::Error::HookCommandFailure( + if !output.status.success() { + return Err(crate::Error::HookCommandFailureWithExitCode( "beforePackagingCommand".into(), script.into(), - status.code().unwrap_or_default(), + output.status.code().unwrap_or_default(), )); } } diff --git a/crates/packager/src/package/nsis/mod.rs b/crates/packager/src/package/nsis/mod.rs index 82430558..36047316 100644 --- a/crates/packager/src/package/nsis/mod.rs +++ b/crates/packager/src/package/nsis/mod.rs @@ -1,5 +1,6 @@ use std::{ collections::{BTreeMap, BTreeSet, HashMap}, + fmt::Debug, path::{Path, PathBuf}, process::Command, }; @@ -49,6 +50,8 @@ const NSIS_REQUIRED_FILES: &[&str] = &[ type DirectoriesSet = BTreeSet; type ResourcesMap = Vec<(PathBuf, PathBuf)>; + +#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))] fn generate_resource_data(config: &Config) -> crate::Result<(DirectoriesSet, ResourcesMap)> { let mut directories = BTreeSet::new(); let mut resources_map = Vec::new(); @@ -66,19 +69,20 @@ fn generate_resource_data(config: &Config) -> crate::Result<(DirectoriesSet, Res /// BTreeMap type BinariesMap = BTreeMap; +#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))] fn generate_binaries_data(config: &Config) -> crate::Result { let mut binaries = BinariesMap::new(); let cwd = std::env::current_dir()?; if let Some(external_binaries) = &config.external_binaries { for src in external_binaries { - let binary_path = dunce::canonicalize(cwd.join(src))?; - let dest_filename = binary_path + let bin_path = dunce::canonicalize(cwd.join(src))?; + let dest_filename = bin_path .file_name() - .expect("failed to extract external binary filename") + .ok_or_else(|| crate::Error::FailedToExtractFilename(bin_path.clone()))? .to_string_lossy() .replace(&format!("-{}", config.target_triple()), ""); - binaries.insert(binary_path, dest_filename); + binaries.insert(bin_path, dest_filename); } } @@ -87,7 +91,7 @@ fn generate_binaries_data(config: &Config) -> crate::Result { let bin_path = config.binary_path(bin); let dest_filename = bin_path .file_name() - .expect("failed to extract binary filename") + .ok_or_else(|| crate::Error::FailedToExtractFilename(bin_path.clone()))? .to_string_lossy() .to_string(); binaries.insert(bin_path, dest_filename); @@ -97,6 +101,7 @@ fn generate_binaries_data(config: &Config) -> crate::Result { Ok(binaries) } +#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))] fn get_lang_data( lang: &str, custom_lang_files: Option<&HashMap>, @@ -128,7 +133,10 @@ fn get_lang_data( Ok(Some((lang_path, lang_content))) } -fn write_ut16_le_with_bom>(path: P, content: &str) -> crate::Result<()> { +#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))] +fn write_ut16_le_with_bom + Debug>(path: P, content: &str) -> crate::Result<()> { + tracing::debug!("Writing {path:?} in UTF-16 LE encoding"); + use std::fs::File; use std::io::{BufWriter, Write}; @@ -221,6 +229,7 @@ fn add_build_number_if_needed(version_str: &str) -> crate::Result { )) } +#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))] fn get_and_extract_nsis( #[allow(unused)] ctx: &Context, nsis_toolset_path: &Path, @@ -228,7 +237,7 @@ fn get_and_extract_nsis( #[cfg(target_os = "windows")] { let data = download_and_verify("nsis-3.09.zip", NSIS_URL, NSIS_SHA1, HashAlgorithm::Sha1)?; - log::info!(action = "Extracting"; "nsis-3.09.zip"); + tracing::info!("Extracting nsis-3.09.zip"); extract_zip(&data, &ctx.tools_path)?; std::fs::rename(ctx.tools_path.join("nsis-3.09"), nsis_toolset_path)?; } @@ -239,7 +248,7 @@ fn get_and_extract_nsis( std::fs::create_dir_all(&unicode_plugins)?; let data = download(NSIS_APPLICATIONID_URL)?; - log::info!(action = "Extracting"; "NSIS ApplicationID plugin"); + tracing::info!("ExtractingNSIS ApplicationID plugin"); extract_zip(&data, &nsis_plugins)?; std::fs::copy( nsis_plugins.join("ReleaseUnicode/ApplicationID.dll"), @@ -257,10 +266,8 @@ fn get_and_extract_nsis( Ok(()) } -fn build_nsis_app_installer( - ctx: &Context, - #[allow(unused)] nsis_path: &Path, -) -> crate::Result> { +#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))] +fn build_nsis_app_installer(ctx: &Context, nsis_path: &Path) -> crate::Result> { let Context { config, intermediates_path, @@ -279,12 +286,12 @@ fn build_nsis_app_installer( let main_binary = config.main_binary()?; let app_exe_source = config.binary_path(main_binary); let signing_path = app_exe_source.with_extension("exe"); - log::debug!(action = "Codesigning"; "{}", signing_path.display()); + tracing::debug!("Codesigning {}", signing_path.display()); codesign::try_sign(&signing_path, config)?; } #[cfg(not(target_os = "windows"))] - log::warn!("Code signing is currently only supported on Windows hosts, skipping signing the main binary..."); + tracing::warn!("Codesigning is currently only supported on Windows hosts, skipping signing the main binary..."); let intermediates_path = intermediates_path.join("nsis").join(arch); util::create_clean_dir(&intermediates_path)?; @@ -293,9 +300,7 @@ fn build_nsis_app_installer( #[cfg(not(target_os = "windows"))] { - let dir = dirs::cache_dir() - .unwrap() - .join("cargo-packager/NSIS/Plugins/x86-unicode"); + let dir = nsis_path.join("Plugins/x86-unicode"); data.insert("additional_plugins_path", to_json(dir)); } @@ -395,7 +400,7 @@ fn build_nsis_app_installer( if let Some(data) = get_lang_data(lang, custom_language_files.as_ref())? { languages_data.push(data); } else { - log::warn!("Custom cargo-packager messages for {lang} are not translated.\nIf it is a valid language listed on , please open a cargo-packager feature request\n or you can provide a custom language file for it in ` nsis.custom_language_files`"); + tracing::warn!("Custom cargo-packager messages for {lang} are not translated.\nIf it is a valid language listed on , please open a cargo-packager feature request\n or you can provide a custom language file for it in ` nsis.custom_language_files`"); } } data.insert("languages", to_json(languages.clone())); @@ -457,13 +462,11 @@ fn build_nsis_app_installer( if let Some(path) = custom_template_path { handlebars .register_template_string("installer.nsi", std::fs::read_to_string(path)?) - .map_err(|e| e.to_string()) - .expect("Failed to setup custom handlebar template"); + .map_err(Box::new)?; } else { handlebars .register_template_string("installer.nsi", include_str!("./installer.nsi")) - .map_err(|e| e.to_string()) - .expect("Failed to setup handlebar template"); + .map_err(Box::new)?; } write_ut16_le_with_bom( @@ -489,41 +492,50 @@ fn build_nsis_app_installer( "{}_{}_{}-setup.exe", main_binary.filename, config.version, arch )); - std::fs::create_dir_all(installer_path.parent().unwrap())?; + std::fs::create_dir_all( + installer_path + .parent() + .ok_or_else(|| crate::Error::ParentDirNotFound(installer_path.clone()))?, + )?; - log::info!(action = "Running"; "makensis.exe to produce {}", util::display_path(&installer_path)); + tracing::info!( + "Running makensis.exe to produce {}", + util::display_path(&installer_path) + ); #[cfg(target_os = "windows")] let mut nsis_cmd = Command::new(nsis_path.join("makensis.exe")); #[cfg(not(target_os = "windows"))] let mut nsis_cmd = Command::new("makensis"); - let log_level = config.log_level.unwrap_or_default(); - - nsis_cmd - .arg(match log_level { + if let Some(level) = config.log_level { + nsis_cmd.arg(match level { LogLevel::Error => "/V1", - LogLevel::Warn => "/V2", - LogLevel::Info => "/V3", + LogLevel::Warn | LogLevel::Info => "/V2", + LogLevel::Debug => "/V3", _ => "/V4", - }) + }); + } + + nsis_cmd .arg(installer_nsi_path) .current_dir(intermediates_path) .output_ok() - .map_err(|e| crate::Error::NsisFailed(e.to_string()))?; + .map_err(crate::Error::NsisFailed)?; std::fs::rename(nsis_output_path, &installer_path)?; #[cfg(target_os = "windows")] { - log::debug!(action = "Codesigning"; "{}", installer_path.display()); + tracing::debug!("Codesigning {}", installer_path.display()); codesign::try_sign(&installer_path, config)?; } #[cfg(not(target_os = "windows"))] - log::warn!("Code signing is currently only supported on Windows hosts, skipping signing the installer..."); + tracing::warn!("Codesigning is currently only supported on Windows hosts, skipping signing the installer..."); Ok(vec![installer_path]) } +#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))] pub(crate) fn package(ctx: &Context) -> crate::Result> { let nsis_toolset_path = ctx.tools_path.join("NSIS"); @@ -533,7 +545,7 @@ pub(crate) fn package(ctx: &Context) -> crate::Result> { .iter() .any(|p| !nsis_toolset_path.join(p).exists()) { - log::warn!("NSIS directory is missing some files. Recreating it..."); + tracing::warn!("NSIS directory is missing some files. Recreating it..."); std::fs::remove_dir_all(&nsis_toolset_path)?; get_and_extract_nsis(ctx, &nsis_toolset_path)?; } diff --git a/crates/packager/src/package/wix/mod.rs b/crates/packager/src/package/wix/mod.rs index c7b45f2c..5d15f28a 100644 --- a/crates/packager/src/package/wix/mod.rs +++ b/crates/packager/src/package/wix/mod.rs @@ -126,6 +126,7 @@ struct Binary { } /// Generates the data required for the external binaries. +#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))] fn generate_binaries_data(config: &Config) -> crate::Result> { let mut binaries = Vec::new(); let cwd = std::env::current_dir()?; @@ -135,9 +136,10 @@ fn generate_binaries_data(config: &Config) -> crate::Result> { if let Some(external_binaries) = &config.external_binaries { for src in external_binaries { let binary_path = cwd.join(src); - let dest_filename = PathBuf::from(src) + let src = PathBuf::from(src); + let dest_filename = src .file_name() - .expect("failed to extract external binary filename") + .ok_or_else(|| crate::Error::FailedToExtractFilename(src.clone()))? .to_string_lossy() .replace(&format!("-{}", config.target_triple()), ""); let dest = tmp_dir.join(&dest_filename); @@ -145,10 +147,7 @@ fn generate_binaries_data(config: &Config) -> crate::Result> { binaries.push(Binary { guid: Uuid::new_v4().to_string(), - path: dest - .into_os_string() - .into_string() - .expect("failed to read external binary path"), + path: dest.into_os_string().into_string().unwrap_or_default(), id: regex .replace_all(&dest_filename.replace('-', "_"), "") .to_string(), @@ -164,7 +163,7 @@ fn generate_binaries_data(config: &Config) -> crate::Result> { .binary_path(bin) .into_os_string() .into_string() - .expect("failed to read binary path"), + .unwrap_or_default(), id: regex .replace_all(&bin.filename.replace('-', "_"), "") .to_string(), @@ -250,6 +249,7 @@ impl ResourceDirectory { type ResourceMap = BTreeMap; /// Generates the data required for the resource on wix +#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))] fn generate_resource_data(config: &Config) -> crate::Result { let mut resources_map = ResourceMap::new(); for resource in config.resources()? { @@ -285,9 +285,7 @@ fn generate_resource_data(config: &Config) -> crate::Result { ); } - let mut directory_entry = resources_map - .get_mut(&first_directory) - .expect("Unable to handle resources"); + let mut directory_entry = resources_map.get_mut(&first_directory).unwrap(); let mut path = String::new(); // the first component is already parsed on `first_directory` so we skip(1) @@ -296,7 +294,7 @@ fn generate_resource_data(config: &Config) -> crate::Result { .as_os_str() .to_os_string() .into_string() - .expect("failed to read resource folder name"); + .unwrap_or_default(); path.push_str(directory_name.as_str()); path.push(std::path::MAIN_SEPARATOR); @@ -367,21 +365,25 @@ fn run_candle( let candle_exe = wix_path.join("candle.exe"); - log::info!(action = "Running"; "candle for {:?}", wxs_file_path); + tracing::info!("Running candle for {:?}", wxs_file_path); let mut cmd = Command::new(candle_exe); for ext in extensions { cmd.arg("-ext"); cmd.arg(ext); } + clear_env_for_wix(&mut cmd); - if config.log_level.unwrap_or_default() >= LogLevel::Debug { - cmd.arg("-v"); + + if let Some(level) = config.log_level { + if level >= LogLevel::Debug { + cmd.arg("-v"); + } } cmd.args(&args) .current_dir(intermediates_path) .output_ok() - .map_err(|e| crate::Error::WixFailed("candle.exe".into(), e.to_string()))?; + .map_err(|e| crate::Error::WixFailed("candle.exe".into(), e))?; Ok(()) } @@ -406,18 +408,24 @@ fn run_light( cmd.arg("-ext"); cmd.arg(ext); } + clear_env_for_wix(&mut cmd); - if config.log_level.unwrap_or_default() >= LogLevel::Debug { - cmd.arg("-v"); + + if let Some(level) = config.log_level { + if level >= LogLevel::Debug { + cmd.arg("-v"); + } } + cmd.args(&args) .current_dir(intermediates_path) .output_ok() - .map_err(|e| crate::Error::WixFailed("light.exe".into(), e.to_string()))?; + .map_err(|e| crate::Error::WixFailed("light.exe".into(), e))?; Ok(()) } +#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))] fn get_and_extract_wix(path: &Path) -> crate::Result<()> { let data = download_and_verify( "wix311-binaries.zip", @@ -425,10 +433,11 @@ fn get_and_extract_wix(path: &Path) -> crate::Result<()> { WIX_SHA256, HashAlgorithm::Sha256, )?; - log::info!("extracting WIX"); + tracing::info!("extracting WIX"); extract_zip(&data, path) } +#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))] fn build_wix_app_installer(ctx: &Context, wix_path: &Path) -> crate::Result> { let Context { config, @@ -446,7 +455,7 @@ fn build_wix_app_installer(ctx: &Context, wix_path: &Path) -> crate::Result crate::Result crate::Result crate::Result = - serde_json::from_str(include_str!("./languages.json")).unwrap(); + serde_json::from_str(include_str!("./languages.json"))?; let configured_languages = config .wix() .map(|w| w.languages.clone()) @@ -672,7 +681,8 @@ fn build_wix_app_installer(ctx: &Context, wix_path: &Path) -> crate::Result crate::Result crate::Result crate::Result crate::Result> { let wix_path = ctx.tools_path.join("WixTools"); if !wix_path.exists() { @@ -723,7 +738,7 @@ pub(crate) fn package(ctx: &Context) -> crate::Result> { .iter() .any(|p| !wix_path.join(p).exists()) { - log::warn!("WixTools directory is missing some files. Recreating it."); + tracing::warn!("WixTools directory is missing some files. Recreating it."); std::fs::remove_dir_all(&wix_path)?; get_and_extract_wix(&wix_path)?; } diff --git a/crates/packager/src/shell.rs b/crates/packager/src/shell.rs index 2efc93c9..39a68cf2 100644 --- a/crates/packager/src/shell.rs +++ b/crates/packager/src/shell.rs @@ -8,22 +8,19 @@ pub trait CommandExt { // The `piped` method sets the stdout and stderr to properly // show the command output in the Node.js wrapper. fn piped(&mut self) -> std::io::Result; - fn output_ok(&mut self) -> crate::Result; + fn output_ok(&mut self) -> std::io::Result; } impl CommandExt for Command { fn piped(&mut self) -> std::io::Result { self.stdout(os_pipe::dup_stdout()?); self.stderr(os_pipe::dup_stderr()?); - let program = self.get_program().to_string_lossy().into_owned(); - log::debug!(action = "Running"; "Command `{} {}`", program, self.get_args().map(|arg| arg.to_string_lossy()).fold(String::new(), |acc, arg| format!("{acc} {arg}"))); - + tracing::debug!("Running Command `{self:?}`"); self.status().map_err(Into::into) } - fn output_ok(&mut self) -> crate::Result { - let program = self.get_program().to_string_lossy().into_owned(); - log::debug!(action = "Running"; "Command `{} {}`", program, self.get_args().map(|arg| arg.to_string_lossy()).fold(String::new(), |acc, arg| format!("{} {}", acc, arg))); + fn output_ok(&mut self) -> std::io::Result { + tracing::debug!("Running Command `{self:?}`"); self.stdout(Stdio::piped()); self.stderr(Stdio::piped()); @@ -42,9 +39,8 @@ impl CommandExt for Command { Ok(s) if s == 0 => break, _ => (), } - log::debug!(action = "stdout"; "{buf}"); - lines.extend(buf.as_bytes().to_vec()); - lines.push(b'\n'); + tracing::debug!("{}", buf.strip_suffix('\n').unwrap_or_else(|| &buf)); + lines.extend(buf.as_bytes()); } }); @@ -60,9 +56,8 @@ impl CommandExt for Command { Ok(s) if s == 0 => break, _ => (), } - log::debug!(action = "stderr"; "{buf}"); - lines.extend(buf.as_bytes().to_vec()); - lines.push(b'\n'); + tracing::debug!("{}", buf.strip_suffix('\n').unwrap_or_else(|| &buf)); + lines.extend(buf.as_bytes()); } }); @@ -76,7 +71,7 @@ impl CommandExt for Command { if output.status.success() { Ok(output) } else { - Err(crate::Error::FailedToRunCommand(program)) + Err(std::io::Error::last_os_error()) } } } diff --git a/crates/packager/src/sign.rs b/crates/packager/src/sign.rs index 987d87f8..a434580b 100644 --- a/crates/packager/src/sign.rs +++ b/crates/packager/src/sign.rs @@ -1,6 +1,7 @@ //! File singing and signing keys creation and decoding. use std::{ + fmt::Debug, fs::OpenOptions, io::{BufReader, Write}, path::{Path, PathBuf}, @@ -22,11 +23,12 @@ pub struct KeyPair { /// Generates a new signing key. If `password` is `None`, it will prompt /// the user for a password, so if you want to skip the prompt, specify and /// empty string as the password. +#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))] pub fn generate_key(password: Option) -> crate::Result { - let KP { pk, sk } = KP::generate_encrypted_keypair(password).unwrap(); + let KP { pk, sk } = KP::generate_encrypted_keypair(password)?; - let pk_box_str = pk.to_box().unwrap().to_string(); - let sk_box_str = sk.to_box(None).unwrap().to_string(); + let pk_box_str = pk.to_box()?.to_string(); + let sk_box_str = sk.to_box(None)?.to_string(); let encoded_pk = base64::engine::general_purpose::STANDARD.encode(pk_box_str); let encoded_sk = base64::engine::general_purpose::STANDARD.encode(sk_box_str); @@ -43,6 +45,7 @@ fn decode_base64(base64_key: &str) -> crate::Result { } /// Decodes a private key using the specified password. +#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))] pub fn decode_private_key(private_key: &str, password: Option<&str>) -> crate::Result { let decoded_secret = decode_base64(private_key)?; let sk_box = SecretKeyBox::from_string(&decoded_secret)?; @@ -51,7 +54,8 @@ pub fn decode_private_key(private_key: &str, password: Option<&str>) -> crate::R } /// Saves a [`KeyPair`] to disk. -pub fn save_keypair>( +#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))] +pub fn save_keypair + Debug>( keypair: &KeyPair, path: P, force: bool, @@ -97,13 +101,18 @@ pub struct SigningConfig { } /// Signs a specified file using the specified signing configuration. -pub fn sign_file>(config: &SigningConfig, path: P) -> crate::Result { +#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))] +pub fn sign_file + Debug>( + config: &SigningConfig, + path: P, +) -> crate::Result { let secret_key = decode_private_key(&config.private_key, config.password.as_deref())?; sign_file_with_secret_key(&secret_key, path) } /// Signs a specified file using an already decoded secret key. -pub fn sign_file_with_secret_key>( +#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))] +pub fn sign_file_with_secret_key + Debug>( secret_key: &SecretKey, path: P, ) -> crate::Result { @@ -118,7 +127,9 @@ pub fn sign_file_with_secret_key>( let trusted_comment = format!( "timestamp:{}\tfile:{}", since_epoch, - path.file_name().unwrap().to_string_lossy() + path.file_name() + .ok_or_else(|| crate::Error::FailedToExtractFilename(path.to_path_buf()))? + .to_string_lossy() ); let file = OpenOptions::new().read(true).open(path)?; diff --git a/crates/packager/src/util.rs b/crates/packager/src/util.rs index f98e8920..32fb3b62 100644 --- a/crates/packager/src/util.rs +++ b/crates/packager/src/util.rs @@ -74,9 +74,8 @@ pub fn target_triple() -> crate::Result { .target_arch .expect("could not find `target_arch` when running `rustc --print cfg`."), Err(err) => { - log:: warn!( - "failed to determine target arch using rustc, error: `{}`. The fallback is the architecture of the machine that compiled this crate.", - err, + tracing:: warn!( + "Failed to determine target arch using rustc, error: `{err}`. Falling back to the architecture of the machine that compiled this crate.", ); if cfg!(target_arch = "x86") { "i686".into() @@ -124,7 +123,7 @@ pub fn target_triple() -> crate::Result { } pub(crate) fn download(url: &str) -> crate::Result> { - log::info!(action = "Downloading"; "{}", url); + tracing::info!("Downloading {}", url); let response = ureq::get(url).call().map_err(Box::new)?; let mut bytes = Vec::new(); response.into_reader().read_to_end(&mut bytes)?; @@ -145,7 +144,7 @@ pub(crate) fn download_and_verify( hash_algorithm: HashAlgorithm, ) -> crate::Result> { let data = download(url)?; - log::info!(action = "Validating"; "{file} hash"); + tracing::info!("Validating {file} hash"); match hash_algorithm { #[cfg(target_os = "windows")] @@ -190,7 +189,9 @@ pub(crate) fn extract_zip(data: &[u8], path: &Path) -> crate::Result<()> { continue; } - let parent = dest_path.parent().ok_or(crate::Error::ParentDirNotFound)?; + let parent = dest_path + .parent() + .ok_or_else(|| crate::Error::ParentDirNotFound(dest_path.clone()))?; if !parent.exists() { std::fs::create_dir_all(parent)?; @@ -281,8 +282,11 @@ pub fn create_icns_file(out_dir: &Path, config: &crate::Config) -> crate::Result for icon_path in icons { let icon_path = PathBuf::from(icon_path); if icon_path.extension() == Some(std::ffi::OsStr::new("icns")) { - let dest_path = - out_dir.join(icon_path.file_name().expect("could not get icon filename")); + let dest_path = out_dir.join( + icon_path + .file_name() + .ok_or_else(|| crate::Error::FailedToExtractFilename(icon_path.clone()))?, + ); std::fs::copy(&icon_path, &dest_path)?; return Ok(Some(dest_path));