diff --git a/cargo/src/cargo_commands.rs b/cargo/src/cargo_commands.rs index 72c926e..fa2acac 100644 --- a/cargo/src/cargo_commands.rs +++ b/cargo/src/cargo_commands.rs @@ -40,8 +40,16 @@ pub fn cargo_fetch(curdir: &Path, manifest: &str, respect_lockfile: bool) -> io: info!("‡️ Running `cargo fetch`..."); let mut default_options: Vec = vec![]; let manifest_path = PathBuf::from(&manifest); - let manifest_path_parent = manifest_path.parent().unwrap_or(curdir); - let possible_lockfile = manifest_path_parent.join("Cargo.lock"); + if !manifest_path.is_file() { + let msg = format!( + "There seems to be no manifest at this path `{}`.", + manifest_path.display() + ); + error!(msg, ?manifest_path); + return Err(io::Error::new(io::ErrorKind::NotFound, msg)); + } + let manifest_path_parent = manifest_path.parent().unwrap_or(curdir).canonicalize()?; + let possible_lockfile = manifest_path_parent.join("Cargo.lock").canonicalize()?; if possible_lockfile.is_file() { if respect_lockfile { default_options.push("--locked".to_string()); @@ -51,10 +59,6 @@ pub fn cargo_fetch(curdir: &Path, manifest: &str, respect_lockfile: bool) -> io: cargo_generate_lockfile(curdir, manifest)?; info!("πŸ”’Regenerated lockfile."); } - if !manifest.is_empty() { - default_options.push("--manifest-path".to_string()); - default_options.push(manifest.to_string()); - } TARGET_TRIPLES.iter().for_each(|target| { default_options.push("--target".to_string()); default_options.push(target.to_string()); @@ -109,13 +113,20 @@ pub fn cargo_vendor( )); } } else { - return Err(io::Error::new( - io::ErrorKind::NotFound, - "Failed to vendor as their are no manifest files to use.", - )); + let msg = "Failed to vendor as their are no manifest files to use."; + error!(msg, ?manifest_paths); + return Err(io::Error::new(io::ErrorKind::NotFound, msg)); }; + } else { + let msg = format!( + "There seems to be no manifest at this path `{}`.", + first_manifest.display() + ); + error!(msg, ?first_manifest); + return Err(io::Error::new(io::ErrorKind::NotFound, msg)); } - let first_manifest_parent = first_manifest.parent().unwrap_or(curdir); + + let first_manifest_parent = first_manifest.parent().unwrap_or(curdir).canonicalize()?; let possible_lockfile = first_manifest_parent.join("Cargo.lock"); let is_manifest_workspace = is_workspace(&first_manifest)?; let has_deps = has_dependencies(&first_manifest)?; @@ -174,7 +185,7 @@ pub fn cargo_vendor( false to true." ); info!("πŸ”“Attempting to regenerate lockfile..."); - cargo_generate_lockfile(curdir, &first_manifest.to_string_lossy())?; + cargo_generate_lockfile(&first_manifest_parent, &first_manifest.to_string_lossy())?; info!("πŸ”’Regenerated lockfile."); } @@ -194,13 +205,15 @@ pub fn cargo_vendor( if !update { warn!("πŸ˜₯ Disabled update of dependencies. You should enable this for security updates."); } + cargo_update( update, crates, - curdir, + &first_manifest_parent, &first_manifest.to_string_lossy(), respect_lockfile, )?; + info!("🚝 Attempting to fetch dependencies."); cargo_fetch(curdir, &first_manifest.to_string_lossy(), respect_lockfile)?; info!("πŸ’Ό Fetched dependencies."); @@ -208,7 +221,7 @@ pub fn cargo_vendor( // NOTE: Vendor filterer's default output format is directory so we // don't need to set that ourselves. info!("πŸͺ Running `cargo {}`...", &which_subcommand); - let res = cargo_command(which_subcommand, &default_options, curdir); + let res = cargo_command(which_subcommand, &default_options, first_manifest_parent); if possible_lockfile.is_file() { info!(?possible_lockfile, "πŸ”“ Adding lockfile."); @@ -223,10 +236,12 @@ pub fn cargo_vendor( })?; } info!("πŸ›‘οΈπŸ™‚ All lockfiles are audited"); + if !global_has_deps { info!("πŸŽ‰ Nothing to vendor."); return Ok(None); } + match res { Ok(output_cargo_configuration) => { info!("πŸͺ `cargo {}` finished.", &which_subcommand); @@ -289,9 +304,9 @@ pub fn cargo_update( let mut default_options = vec![]; if global_update { info!("⏫ Updating dependencies..."); - let manifest_path = PathBuf::from(&manifest); + let manifest_path = PathBuf::from(&manifest).canonicalize()?; let manifest_path_parent = manifest_path.parent().unwrap_or(curdir); - let possible_lockfile = manifest_path_parent.join("Cargo.lock"); + let possible_lockfile = manifest_path_parent.join("Cargo.lock").canonicalize()?; if !manifest.is_empty() { default_options.push("--manifest-path".to_string()); default_options.push(manifest.to_string()); @@ -311,55 +326,123 @@ pub fn cargo_update( // and user might have specified specific crates to update if !crates.is_empty() { for crate_ in crates.iter() { - if let Some((crate_name, crate_ver)) = crate_.split_once("@") { + let mut new_cur_dir = curdir.to_path_buf(); + if let Some((crate_name, string_tail)) = crate_.split_once("@") { default_options.push(crate_name.to_string()); - if !crate_ver.trim().is_empty() { - if *crate_ver == *"recursive" { - info!( - "πŸ“¦πŸ”„ Applying recursive update for crate dependency {}", - crate_name - ); - default_options.push("--recursive".to_string()); - } else if semver::Version::parse(crate_ver) - .map_err(|err| { - error!(?err); - let msg = format!("Expected a valid version string. Got {}", crate_ver); - io::Error::new(io::ErrorKind::InvalidInput, msg) - }) - .is_ok() - { - info!( + if let Some((crate_ver, dependent)) = string_tail.split_once("+") { + if !crate_ver.trim().is_empty() { + if *crate_ver == *"recursive" { + info!( + "πŸ“¦πŸ”„ Applying recursive update for crate dependency {}", + crate_name + ); + default_options.push("--recursive".to_string()); + } else if semver::Version::parse(crate_ver) + .map_err(|err| { + error!(?err); + let msg = + format!("Expected a valid version string. Got {}", crate_ver); + io::Error::new(io::ErrorKind::InvalidInput, msg) + }) + .is_ok() + { + info!( "πŸ“¦πŸ₯„ Applying precise update for crate dependency {} to version {}", crate_name, crate_ver ); - default_options.push("--precise".to_string()); - default_options.push(crate_ver.to_string()); - } else { - let msg = format!( - "Expected a valid `cargo update` option for {}. Got {}", + default_options.push("--precise".to_string()); + default_options.push(crate_ver.to_string()); + } else { + let msg = format!( + "Expected a valid `cargo update` option for {}. Got {}", + crate_name, crate_ver + ); + return Err(io::Error::new(io::ErrorKind::InvalidInput, msg)); + } + } + + if !dependent.trim().is_empty() { + info!("πŸ—οΈ Updating {} at {}.", crate_name, dependent); + let dependent_manifest_path = curdir.join(dependent).canonicalize()?; + default_options.push("--manifest-path".to_string()); + default_options.push(dependent_manifest_path.to_string_lossy().to_string()); + let manifest_path_parent = + dependent_manifest_path.parent().unwrap_or(curdir); + new_cur_dir = manifest_path_parent.to_path_buf(); + let possible_lockfile = manifest_path_parent.join("Cargo.lock"); + if possible_lockfile.is_file() && respect_lockfile { + default_options.push("--locked".to_string()); + } + } + } + // NOTE: `+` can be first then `@` second. + } else if let Some((crate_name, string_tail)) = crate_.split_once("+") { + default_options.push(crate_name.to_string()); + if let Some((dependent, crate_ver)) = string_tail.split_once("@") { + if !crate_ver.trim().is_empty() { + if *crate_ver == *"recursive" { + info!( + "πŸ“¦πŸ”„ Applying recursive update for crate dependency {}", + crate_name + ); + default_options.push("--recursive".to_string()); + } else if semver::Version::parse(crate_ver) + .map_err(|err| { + error!(?err); + let msg = + format!("Expected a valid version string. Got {}", crate_ver); + io::Error::new(io::ErrorKind::InvalidInput, msg) + }) + .is_ok() + { + info!( + "πŸ“¦πŸ₯„ Applying precise update for crate dependency {} to version {}", crate_name, crate_ver ); - return Err(io::Error::new(io::ErrorKind::InvalidInput, msg)); + default_options.push("--precise".to_string()); + default_options.push(crate_ver.to_string()); + } else { + let msg = format!( + "Expected a valid `cargo update` option for {}. Got {}", + crate_name, crate_ver + ); + return Err(io::Error::new(io::ErrorKind::InvalidInput, msg)); + } + } + + if !dependent.trim().is_empty() { + info!("πŸ—οΈ Updating {} at {}.", crate_name, dependent); + let dependent_manifest_path = curdir.join(dependent).canonicalize()?; + default_options.push("--manifest-path".to_string()); + default_options.push(dependent_manifest_path.to_string_lossy().to_string()); + let manifest_path_parent = + dependent_manifest_path.parent().unwrap_or(curdir); + let possible_lockfile = manifest_path_parent.join("Cargo.lock"); + new_cur_dir = manifest_path_parent.to_path_buf(); + if possible_lockfile.is_file() && respect_lockfile { + default_options.push("--locked".to_string()); + } } } } + cargo_command("update", &default_options, new_cur_dir) + .inspect(|_| { + info!("βœ… Updated dependencies."); + }) + .inspect_err(|err| { + error!(?err); + // There is no point of handling error if a PKGID or crate does not exist for a particular manifest path + // because at the end of the day, if two manifest paths do have the same crate that was specified to update + // then the one in the registry or vendor gets updated with the same version as well. + // NOTE: Maybe in the future we can add ways to be specific on each manifest path + warn!("This error will be ignored."); + })?; } - let _ = cargo_command("update", &default_options, curdir) - .inspect(|_| { - info!("βœ… Updated dependencies."); - }) - .inspect_err(|err| { - error!(?err); - // There is no point of handling error if a PKGID or crate does not exist for a particular manifest path - // because at the end of the day, if two manifest paths do have the same crate that was specified to update - // then the one in the registry or vendor gets updated with the same version as well. - // NOTE: Maybe in the future we can add ways to be specific on each manifest path - warn!("This error will be ignored."); - }); - Ok("".to_string()) + let success_msg = "ℹ️ Finished updating specified crate dependencies.".to_string(); + Ok(success_msg) } else { let msg = "🫠 Nothing to update.".to_string(); info!("{}", &msg); - Ok(msg) + return Ok(msg); } } diff --git a/cargo/src/cli.rs b/cargo/src/cli.rs index 2ba7801..d84cb5c 100644 --- a/cargo/src/cli.rs +++ b/cargo/src/cli.rs @@ -113,7 +113,7 @@ pub struct Opts { pub i_accept_the_risk: Vec, #[arg( long, - help = "Set of specific crates to update. If not empty, it will set the global update flag to false. You can specify a valid version string by adding a `@` after the crate name e.g. `foo@1.2.3`. You can also do recursive updates of a crate by appending `recursive` to `@` e.g. `foo@recursive`. However, recursive can't be used with precise. See `cargo help update` for info about how to update specific crates." + help = "Set of specific crates to update. If not empty, it will set the global update flag to false. You can specify a valid version string by adding a `@` after the crate name e.g. `foo@1.2.3`. You can also do recursive updates of a crate by appending `recursive` to `@` e.g. `foo@recursive`. However, recursive can't be used with precise. You can specify a manifest path to update a package with `+` e.g. `foo@1.0+foo/better/Cargo.toml`. See `cargo help update` for info about how to update specific crates." )] pub update_crate: Vec, #[clap(flatten)] diff --git a/cargo/src/registry.rs b/cargo/src/registry.rs index 9a8f98c..5b01cb7 100644 --- a/cargo/src/registry.rs +++ b/cargo/src/registry.rs @@ -54,17 +54,21 @@ pub fn run_cargo_vendor_home_registry( } } global_has_deps = has_deps || global_has_deps; + let possible_root_manifest_parent = possible_root_manifest + .parent() + .unwrap_or(custom_root) + .canonicalize()?; cargo_update( registry.update, ®istry.update_crate, - custom_root, + &possible_root_manifest_parent, &possible_root_manifest.to_string_lossy(), registry.respect_lockfile, )?; info!(?setup_workdir, "🌳 Finished setting up workdir."); info!("🚝 Attempting to fetch dependencies."); cargo_fetch( - custom_root, + &possible_root_manifest_parent, &possible_root_manifest.to_string_lossy(), registry.respect_lockfile, )?; @@ -119,7 +123,7 @@ pub fn run_cargo_vendor_home_registry( "🚝 Attempting to fetch dependencies at extra manifest path..." ); cargo_fetch( - custom_root, + full_manifest_path_parent, &full_manifest_path.to_string_lossy(), registry.respect_lockfile, )?;