Merge pull request #282 from cargo-bins/skip-if-already-installed

Skip if already installed and add new cmdline option `--force`
This commit is contained in:
Jiahao XU 2022-08-08 19:52:52 +10:00 committed by GitHub
commit 9034f78df4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 252 additions and 69 deletions

View file

@ -19,7 +19,7 @@ done
cargo binstall --help >/dev/null cargo binstall --help >/dev/null
# Install binaries using `--manifest-path` # Install binaries using `--manifest-path`
"./$1" binstall --log-level debug --manifest-path . --no-confirm cargo-binstall "./$1" binstall --force --log-level debug --manifest-path . --no-confirm cargo-binstall
# Test that the installed binaries can be run # Test that the installed binaries can be run
cargo binstall --help >/dev/null cargo binstall --help >/dev/null
@ -28,6 +28,7 @@ min_tls=1.3
[[ "${2:-}" == "Windows" ]] && min_tls=1.2 # WinTLS on GHA doesn't support 1.3 yet [[ "${2:-}" == "Windows" ]] && min_tls=1.2 # WinTLS on GHA doesn't support 1.3 yet
"./$1" binstall \ "./$1" binstall \
--force \
--log-level debug \ --log-level debug \
--secure \ --secure \
--min-tls-version $min_tls \ --min-tls-version $min_tls \
@ -35,3 +36,23 @@ min_tls=1.3
cargo-binstall cargo-binstall
# Test that the installed binaries can be run # Test that the installed binaries can be run
cargo binstall --help >/dev/null cargo binstall --help >/dev/null
# Test --version
"./$1" binstall --force --log-level debug --no-confirm --version 0.11.1 cargo-binstall
# Test that the installed binaries can be run
cargo binstall --help >/dev/null
# Test "$crate_name@$version"
"./$1" binstall --force --log-level debug --no-confirm cargo-binstall@0.11.1
# Test that the installed binaries can be run
cargo binstall --help >/dev/null
# Test skip when installed
"./$1" binstall --no-confirm cargo-binstall | grep -q 'package cargo-binstall is already installed'
"./$1" binstall --no-confirm cargo-binstall@0.11.1 | grep -q 'package cargo-binstall@=0.11.1 is already installed'
"./$1" binstall --no-confirm cargo-binstall@0.10.0 | grep -q -v 'package cargo-binstall@=0.10.0 is already installed'
## Test When 0.11.0 is installed but can be upgraded.
"./$1" binstall --force --log-level debug --no-confirm cargo-binstall@0.11.0
"./$1" binstall --no-confirm cargo-binstall@^0.11.0 | grep -q -v 'package cargo-binstall@^0.11.0 is already installed'

View file

@ -1,6 +1,6 @@
use std::path::PathBuf; use std::path::PathBuf;
use compact_str::CompactString; use semver::VersionReq;
use crate::{metafiles::binstall_v1::MetaData, DesiredTargets, PkgOverride}; use crate::{metafiles::binstall_v1::MetaData, DesiredTargets, PkgOverride};
@ -13,7 +13,8 @@ pub use install::*;
pub struct Options { pub struct Options {
pub no_symlinks: bool, pub no_symlinks: bool,
pub dry_run: bool, pub dry_run: bool,
pub version: Option<CompactString>, pub force: bool,
pub version_req: Option<VersionReq>,
pub manifest_path: Option<PathBuf>, pub manifest_path: Option<PathBuf>,
pub cli_overrides: PkgOverride, pub cli_overrides: PkgOverride,
pub desired_targets: DesiredTargets, pub desired_targets: DesiredTargets,

View file

@ -15,11 +15,12 @@ pub async fn install(
jobserver_client: LazyJobserverClient, jobserver_client: LazyJobserverClient,
) -> Result<Option<MetaData>> { ) -> Result<Option<MetaData>> {
match resolution { match resolution {
Resolution::AlreadyUpToDate => Ok(None),
Resolution::Fetch { Resolution::Fetch {
fetcher, fetcher,
package, package,
name, name,
version, version_req,
bin_path, bin_path,
bin_files, bin_files,
} => { } => {
@ -31,7 +32,7 @@ pub async fn install(
.map(|option| { .map(|option| {
option.map(|bins| MetaData { option.map(|bins| MetaData {
name, name,
version_req: version, version_req,
current_version, current_version,
source: Source::cratesio_registry(), source: Source::cratesio_registry(),
target, target,
@ -47,7 +48,7 @@ pub async fn install(
.ok_or_else(|| miette!("No viable targets found, try with `--targets`"))?; .ok_or_else(|| miette!("No viable targets found, try with `--targets`"))?;
if !opts.dry_run { if !opts.dry_run {
install_from_source(package, target, jobserver_client, opts.quiet) install_from_source(package, target, jobserver_client, opts.quiet, opts.force)
.await .await
.map(|_| None) .map(|_| None)
} else { } else {
@ -127,6 +128,7 @@ async fn install_from_source(
target: &str, target: &str,
lazy_jobserver_client: LazyJobserverClient, lazy_jobserver_client: LazyJobserverClient,
quiet: bool, quiet: bool,
force: bool,
) -> Result<()> { ) -> Result<()> {
let jobserver_client = lazy_jobserver_client.get().await?; let jobserver_client = lazy_jobserver_client.get().await?;
@ -150,6 +152,10 @@ async fn install_from_source(
cmd.arg("--quiet"); cmd.arg("--quiet");
} }
if force {
cmd.arg("--force");
}
let mut child = cmd let mut child = cmd
.spawn() .spawn()
.into_diagnostic() .into_diagnostic()

View file

@ -4,10 +4,11 @@ use std::{
}; };
use cargo_toml::{Package, Product}; use cargo_toml::{Package, Product};
use compact_str::{format_compact, CompactString}; use compact_str::{CompactString, ToCompactString};
use log::{debug, error, info, warn}; use log::{debug, error, info, warn};
use miette::{miette, Result}; use miette::{miette, Result};
use reqwest::Client; use reqwest::Client;
use semver::{Version, VersionReq};
use super::Options; use super::Options;
use crate::{ use crate::{
@ -21,13 +22,14 @@ pub enum Resolution {
fetcher: Arc<dyn Fetcher>, fetcher: Arc<dyn Fetcher>,
package: Package<Meta>, package: Package<Meta>,
name: CompactString, name: CompactString,
version: CompactString, version_req: CompactString,
bin_path: PathBuf, bin_path: PathBuf,
bin_files: Vec<bins::BinFile>, bin_files: Vec<bins::BinFile>,
}, },
InstallFromSource { InstallFromSource {
package: Package<Meta>, package: Package<Meta>,
}, },
AlreadyUpToDate,
} }
impl Resolution { impl Resolution {
fn print(&self, opts: &Options) { fn print(&self, opts: &Options) {
@ -69,6 +71,7 @@ impl Resolution {
Resolution::InstallFromSource { .. } => { Resolution::InstallFromSource { .. } => {
warn!("The package will be installed from source (with cargo)",) warn!("The package will be installed from source (with cargo)",)
} }
Resolution::AlreadyUpToDate => (),
} }
} }
} }
@ -76,6 +79,7 @@ impl Resolution {
pub async fn resolve( pub async fn resolve(
opts: Arc<Options>, opts: Arc<Options>,
crate_name: CrateName, crate_name: CrateName,
curr_version: Option<Version>,
temp_dir: Arc<Path>, temp_dir: Arc<Path>,
install_path: Arc<Path>, install_path: Arc<Path>,
client: Client, client: Client,
@ -83,35 +87,44 @@ pub async fn resolve(
) -> Result<Resolution> { ) -> Result<Resolution> {
info!("Installing package: '{}'", crate_name); info!("Installing package: '{}'", crate_name);
let mut version: CompactString = match (&crate_name.version, &opts.version) { let version_req: VersionReq = match (&crate_name.version_req, &opts.version_req) {
(Some(version), None) => version.clone(), (Some(version), None) => version.clone(),
(None, Some(version)) => version.clone(), (None, Some(version)) => version.clone(),
(Some(_), Some(_)) => Err(BinstallError::SuperfluousVersionOption)?, (Some(_), Some(_)) => Err(BinstallError::SuperfluousVersionOption)?,
(None, None) => "*".into(), (None, None) => VersionReq::STAR,
}; };
// Treat 0.1.2 as =0.1.2
if version
.chars()
.next()
.map(|ch| ch.is_ascii_digit())
.unwrap_or(false)
{
version = format_compact!("={version}");
}
// Fetch crate via crates.io, git, or use a local manifest path // Fetch crate via crates.io, git, or use a local manifest path
// TODO: work out which of these to do based on `opts.name` // TODO: work out which of these to do based on `opts.name`
// TODO: support git-based fetches (whole repo name rather than just crate name) // TODO: support git-based fetches (whole repo name rather than just crate name)
let manifest = match opts.manifest_path.clone() { let manifest = match opts.manifest_path.clone() {
Some(manifest_path) => load_manifest_path(manifest_path)?, Some(manifest_path) => load_manifest_path(manifest_path)?,
None => { None => {
fetch_crate_cratesio(&client, &crates_io_api_client, &crate_name.name, &version).await? fetch_crate_cratesio(
&client,
&crates_io_api_client,
&crate_name.name,
&version_req,
)
.await?
} }
}; };
let package = manifest.package.unwrap(); let package = manifest.package.unwrap();
if let Some(curr_version) = curr_version {
let new_version =
Version::parse(&package.version).map_err(|err| BinstallError::VersionParse {
v: package.version.clone(),
err,
})?;
if new_version == curr_version {
info!("package {crate_name} is already up to date {curr_version}");
return Ok(Resolution::AlreadyUpToDate);
}
}
let (mut meta, binaries) = ( let (mut meta, binaries) = (
package package
.metadata .metadata
@ -175,7 +188,7 @@ pub async fn resolve(
fetcher, fetcher,
package, package,
name: crate_name.name, name: crate_name.name,
version, version_req: version_req.to_compact_string(),
bin_path, bin_path,
bin_files, bin_files,
} }

View file

@ -4,6 +4,7 @@ use cargo_toml::Manifest;
use crates_io_api::AsyncClient; use crates_io_api::AsyncClient;
use log::debug; use log::debug;
use reqwest::Client; use reqwest::Client;
use semver::VersionReq;
use url::Url; use url::Url;
use super::find_version; use super::find_version;
@ -19,7 +20,7 @@ pub async fn fetch_crate_cratesio(
client: &Client, client: &Client,
crates_io_api_client: &AsyncClient, crates_io_api_client: &AsyncClient,
name: &str, name: &str,
version_req: &str, version_req: &VersionReq,
) -> Result<Manifest<Meta>, BinstallError> { ) -> Result<Manifest<Meta>, BinstallError> {
// Fetch / update index // Fetch / update index
debug!("Looking up crate information"); debug!("Looking up crate information");

View file

@ -28,15 +28,9 @@ impl Version for crates_io_api::Version {
} }
pub(super) fn find_version<Item: Version, VersionIter: Iterator<Item = Item>>( pub(super) fn find_version<Item: Version, VersionIter: Iterator<Item = Item>>(
requirement: &str, version_req: &VersionReq,
version_iter: VersionIter, version_iter: VersionIter,
) -> Result<(Item, semver::Version), BinstallError> { ) -> Result<(Item, semver::Version), BinstallError> {
// Parse version requirement
let version_req = VersionReq::parse(requirement).map_err(|err| BinstallError::VersionReq {
req: requirement.into(),
err,
})?;
version_iter version_iter
// Filter for matching versions // Filter for matching versions
.filter_map(|item| { .filter_map(|item| {
@ -52,5 +46,7 @@ pub(super) fn find_version<Item: Version, VersionIter: Iterator<Item = Item>>(
}) })
// Return highest version // Return highest version
.max_by_key(|(_item, ver)| ver.clone()) .max_by_key(|(_item, ver)| ver.clone())
.ok_or(BinstallError::VersionMismatch { req: version_req }) .ok_or(BinstallError::VersionMismatch {
req: version_req.clone(),
})
} }

View file

@ -8,6 +8,7 @@ use std::sync::Arc;
use bytes::Bytes; use bytes::Bytes;
use cargo_toml::Manifest; use cargo_toml::Manifest;
use compact_str::format_compact;
use futures_util::stream::Stream; use futures_util::stream::Stream;
use log::debug; use log::debug;
use once_cell::sync::{Lazy, OnceCell}; use once_cell::sync::{Lazy, OnceCell};
@ -50,6 +51,9 @@ pub use flock::FileLock;
mod signal; mod signal;
pub use signal::cancel_on_user_sig_term; pub use signal::cancel_on_user_sig_term;
mod version;
pub use version::VersionReqExt;
pub fn cargo_home() -> Result<&'static Path, io::Error> { pub fn cargo_home() -> Result<&'static Path, io::Error> {
static CARGO_HOME: OnceCell<PathBuf> = OnceCell::new(); static CARGO_HOME: OnceCell<PathBuf> = OnceCell::new();
@ -86,6 +90,20 @@ pub async fn await_task<T>(task: tokio::task::JoinHandle<miette::Result<T>>) ->
} }
} }
pub fn parse_version(version: &str) -> Result<semver::VersionReq, semver::Error> {
// Treat 0.1.2 as =0.1.2
if version
.chars()
.next()
.map(|ch| ch.is_ascii_digit())
.unwrap_or(false)
{
format_compact!("={version}").parse()
} else {
version.parse()
}
}
/// Load binstall metadata from the crate `Cargo.toml` at the provided path /// Load binstall metadata from the crate `Cargo.toml` at the provided path
pub fn load_manifest_path<P: AsRef<Path>>( pub fn load_manifest_path<P: AsRef<Path>>(
manifest_path: P, manifest_path: P,

View file

@ -1,19 +1,22 @@
use std::{convert::Infallible, fmt, str::FromStr}; use std::{fmt, str::FromStr};
use compact_str::CompactString; use compact_str::CompactString;
use itertools::Itertools; use itertools::Itertools;
use semver::{Error, VersionReq};
use super::parse_version;
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq)]
pub struct CrateName { pub struct CrateName {
pub name: CompactString, pub name: CompactString,
pub version: Option<CompactString>, pub version_req: Option<VersionReq>,
} }
impl fmt::Display for CrateName { impl fmt::Display for CrateName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name)?; write!(f, "{}", self.name)?;
if let Some(version) = &self.version { if let Some(version) = &self.version_req {
write!(f, "@{version}")?; write!(f, "@{version}")?;
} }
@ -22,18 +25,18 @@ impl fmt::Display for CrateName {
} }
impl FromStr for CrateName { impl FromStr for CrateName {
type Err = Infallible; type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(if let Some((name, version)) = s.split_once('@') { Ok(if let Some((name, version)) = s.split_once('@') {
CrateName { CrateName {
name: name.into(), name: name.into(),
version: Some(version.into()), version_req: Some(parse_version(version)?),
} }
} else { } else {
CrateName { CrateName {
name: s.into(), name: s.into(),
version: None, version_req: None,
} }
}) })
} }
@ -60,11 +63,11 @@ mod tests {
([ $( ( $input_name:expr, $input_version:expr ) ),* ], [ $( ( $output_name:expr, $output_version:expr ) ),* ]) => { ([ $( ( $input_name:expr, $input_version:expr ) ),* ], [ $( ( $output_name:expr, $output_version:expr ) ),* ]) => {
let input_crate_names = vec![$( CrateName { let input_crate_names = vec![$( CrateName {
name: $input_name.into(), name: $input_name.into(),
version: Some($input_version.into()) version_req: Some($input_version.parse().unwrap())
}, )*]; }, )*];
let mut output_crate_names: Vec<CrateName> = vec![$( CrateName { let mut output_crate_names: Vec<CrateName> = vec![$( CrateName {
name: $output_name.into(), version: Some($output_version.into()) name: $output_name.into(), version_req: Some($output_version.parse().unwrap())
}, )*]; }, )*];
output_crate_names.sort_by(|x, y| x.name.cmp(&y.name)); output_crate_names.sort_by(|x, y| x.name.cmp(&y.name));

78
src/helpers/version.rs Normal file
View file

@ -0,0 +1,78 @@
use compact_str::format_compact;
use semver::{Prerelease, Version, VersionReq};
/// Extension trait for [`VersionReq`].
pub trait VersionReqExt {
/// Return `true` if `self.matches(version)` returns `true`
/// and the `version` is the latest one acceptable by `self`.
fn is_latest_compatible(&self, version: &Version) -> bool;
}
impl VersionReqExt for VersionReq {
fn is_latest_compatible(&self, version: &Version) -> bool {
if !self.matches(version) {
return false;
}
// Test if bumping patch will be accepted
let bumped_version = Version::new(version.major, version.minor, version.patch + 1);
if self.matches(&bumped_version) {
return false;
}
// Test if bumping prerelease will be accepted if version has one.
let pre = &version.pre;
if !pre.is_empty() {
// Bump pre by appending random number to the end.
let bumped_pre = format_compact!("{}.1", pre.as_str());
let bumped_version = Version {
major: version.major,
minor: version.minor,
patch: version.patch,
pre: Prerelease::new(&bumped_pre).unwrap(),
build: Default::default(),
};
if self.matches(&bumped_version) {
return false;
}
}
true
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test() {
// Test star
assert!(!VersionReq::STAR.is_latest_compatible(&Version::parse("0.0.1").unwrap()));
assert!(!VersionReq::STAR.is_latest_compatible(&Version::parse("0.1.1").unwrap()));
assert!(!VersionReq::STAR.is_latest_compatible(&Version::parse("0.1.1-alpha").unwrap()));
// Test ^x.y.z
assert!(!VersionReq::parse("^0.1")
.unwrap()
.is_latest_compatible(&Version::parse("0.1.99").unwrap()));
// Test =x.y.z
assert!(VersionReq::parse("=0.1.0")
.unwrap()
.is_latest_compatible(&Version::parse("0.1.0").unwrap()));
// Test =x.y.z-alpha
assert!(VersionReq::parse("=0.1.0-alpha")
.unwrap()
.is_latest_compatible(&Version::parse("0.1.0-alpha").unwrap()));
// Test >=x.y.z-alpha
assert!(!VersionReq::parse(">=0.1.0-alpha")
.unwrap()
.is_latest_compatible(&Version::parse("0.1.0-alpha").unwrap()));
}
}

View file

@ -9,9 +9,9 @@ use std::{
}; };
use clap::{builder::PossibleValue, AppSettings, Parser}; use clap::{builder::PossibleValue, AppSettings, Parser};
use compact_str::CompactString;
use log::{debug, error, info, warn, LevelFilter}; use log::{debug, error, info, warn, LevelFilter};
use miette::{miette, Result, WrapErr}; use miette::{miette, Result, WrapErr};
use semver::VersionReq;
use simplelog::{ColorChoice, ConfigBuilder, TermLogger, TerminalMode}; use simplelog::{ColorChoice, ConfigBuilder, TermLogger, TerminalMode};
use tokio::{runtime::Runtime, task::block_in_place}; use tokio::{runtime::Runtime, task::block_in_place};
@ -46,8 +46,8 @@ struct Options {
/// ///
/// Cannot be used when multiple packages are installed at once, use the attached version /// Cannot be used when multiple packages are installed at once, use the attached version
/// syntax in that case. /// syntax in that case.
#[clap(help_heading = "Package selection", long = "version")] #[clap(help_heading = "Package selection", long = "version", parse(try_from_str = parse_version))]
version_req: Option<CompactString>, version_req: Option<VersionReq>,
/// Override binary target set. /// Override binary target set.
/// ///
@ -131,6 +131,10 @@ struct Options {
#[clap(help_heading = "Options", long)] #[clap(help_heading = "Options", long)]
secure: bool, secure: bool,
/// Force a crate to be installed even if it is already installed.
#[clap(help_heading = "Options", long)]
force: bool,
/// Require a minimum TLS version from remote endpoints. /// Require a minimum TLS version from remote endpoints.
/// ///
/// The default is not to require any minimum TLS version, and use the negotiated highest /// The default is not to require any minimum TLS version, and use the negotiated highest
@ -281,15 +285,15 @@ async fn entry(jobserver_client: LazyJobserverClient) -> Result<()> {
} }
} }
// Remove duplicate crate_name, keep the last one
let crate_names = CrateName::dedup(crate_names);
let cli_overrides = PkgOverride { let cli_overrides = PkgOverride {
pkg_url: opts.pkg_url.take(), pkg_url: opts.pkg_url.take(),
pkg_fmt: opts.pkg_fmt.take(), pkg_fmt: opts.pkg_fmt.take(),
bin_dir: opts.bin_dir.take(), bin_dir: opts.bin_dir.take(),
}; };
// Launch target detection
let desired_targets = get_desired_targets(&opts.targets);
// Initialize reqwest client // Initialize reqwest client
let client = create_reqwest_client(opts.secure, opts.min_tls_version.map(|v| v.into()))?; let client = create_reqwest_client(opts.secure, opts.min_tls_version.map(|v| v.into()))?;
@ -317,28 +321,64 @@ async fn entry(jobserver_client: LazyJobserverClient) -> Result<()> {
// Initialize UI thread // Initialize UI thread
let mut uithread = UIThread::new(!opts.no_confirm); let mut uithread = UIThread::new(!opts.no_confirm);
// Launch target detection let (install_path, metadata, temp_dir) = block_in_place(|| -> Result<_> {
let desired_targets = get_desired_targets(&opts.targets); // Compute install directory
let (install_path, custom_install_path) = get_install_path(opts.install_path.as_deref());
let install_path = install_path.ok_or_else(|| {
error!("No viable install path found of specified, try `--install-path`");
miette!("No install path found or specified")
})?;
fs::create_dir_all(&install_path).map_err(BinstallError::Io)?;
debug!("Using install path: {}", install_path.display());
// Compute install directory // Load metadata
let (install_path, custom_install_path) = get_install_path(opts.install_path.as_deref()); let metadata = if !custom_install_path {
let install_path = install_path.ok_or_else(|| { debug!("Reading binstall/crates-v1.json");
error!("No viable install path found of specified, try `--install-path`"); Some(metafiles::binstall_v1::Records::load()?)
miette!("No install path found or specified") } else {
None
};
// Create a temporary directory for downloads etc.
//
// Put all binaries to a temporary directory under `dst` first, catching
// some failure modes (e.g., out of space) before touching the existing
// binaries. This directory will get cleaned up via RAII.
let temp_dir = tempfile::Builder::new()
.prefix("cargo-binstall")
.tempdir_in(&install_path)
.map_err(BinstallError::from)
.wrap_err("Creating a temporary directory failed.")?;
Ok((install_path, metadata, temp_dir))
})?; })?;
fs::create_dir_all(&install_path).map_err(BinstallError::Io)?;
debug!("Using install path: {}", install_path.display());
// Create a temporary directory for downloads etc. // Remove installed crates
// let crate_names = CrateName::dedup(crate_names).filter_map(|crate_name| {
// Put all binaries to a temporary directory under `dst` first, catching if opts.force {
// some failure modes (e.g., out of space) before touching the existing Some((crate_name, None))
// binaries. This directory will get cleaned up via RAII. } else if let Some(records) = &metadata {
let temp_dir = tempfile::Builder::new() if let Some(metadata) = records.get(&crate_name.name) {
.prefix("cargo-binstall") if let Some(version_req) = &crate_name.version_req {
.tempdir_in(&install_path) if version_req.is_latest_compatible(&metadata.current_version) {
.map_err(BinstallError::from) info!(
.wrap_err("Creating a temporary directory failed.")?; "package {crate_name} is already installed and cannot be upgraded, use --force to override"
);
None
} else {
Some((crate_name, Some(metadata.current_version.clone())))
}
} else {
info!("package {crate_name} is already installed, use --force to override");
None
}
} else {
Some((crate_name, None))
}
} else {
Some((crate_name, None))
}
});
let temp_dir_path: Arc<Path> = Arc::from(temp_dir.path()); let temp_dir_path: Arc<Path> = Arc::from(temp_dir.path());
@ -346,7 +386,8 @@ async fn entry(jobserver_client: LazyJobserverClient) -> Result<()> {
let binstall_opts = Arc::new(binstall::Options { let binstall_opts = Arc::new(binstall::Options {
no_symlinks: opts.no_symlinks, no_symlinks: opts.no_symlinks,
dry_run: opts.dry_run, dry_run: opts.dry_run,
version: opts.version_req.take(), force: opts.force,
version_req: opts.version_req.take(),
manifest_path: opts.manifest_path.take(), manifest_path: opts.manifest_path.take(),
cli_overrides, cli_overrides,
desired_targets, desired_targets,
@ -357,10 +398,11 @@ async fn entry(jobserver_client: LazyJobserverClient) -> Result<()> {
// Resolve crates // Resolve crates
let tasks: Vec<_> = crate_names let tasks: Vec<_> = crate_names
.into_iter() .into_iter()
.map(|crate_name| { .map(|(crate_name, current_version)| {
AutoAbortJoinHandle::spawn(binstall::resolve( AutoAbortJoinHandle::spawn(binstall::resolve(
binstall_opts.clone(), binstall_opts.clone(),
crate_name, crate_name,
current_version,
temp_dir_path.clone(), temp_dir_path.clone(),
install_path.clone(), install_path.clone(),
client.clone(), client.clone(),
@ -392,7 +434,7 @@ async fn entry(jobserver_client: LazyJobserverClient) -> Result<()> {
// Resolve crates and install without confirmation // Resolve crates and install without confirmation
crate_names crate_names
.into_iter() .into_iter()
.map(|crate_name| { .map(|(crate_name, current_version)| {
let opts = binstall_opts.clone(); let opts = binstall_opts.clone();
let temp_dir_path = temp_dir_path.clone(); let temp_dir_path = temp_dir_path.clone();
let jobserver_client = jobserver_client.clone(); let jobserver_client = jobserver_client.clone();
@ -404,6 +446,7 @@ async fn entry(jobserver_client: LazyJobserverClient) -> Result<()> {
let resolution = binstall::resolve( let resolution = binstall::resolve(
opts.clone(), opts.clone(),
crate_name, crate_name,
current_version,
temp_dir_path, temp_dir_path,
install_path, install_path,
client, client,
@ -425,7 +468,7 @@ async fn entry(jobserver_client: LazyJobserverClient) -> Result<()> {
} }
block_in_place(|| { block_in_place(|| {
if !custom_install_path { if let Some(mut records) = metadata {
// If using standardised install path, // If using standardised install path,
// then create_dir_all(&install_path) would also // then create_dir_all(&install_path) would also
// create .cargo. // create .cargo.
@ -434,7 +477,10 @@ async fn entry(jobserver_client: LazyJobserverClient) -> Result<()> {
metafiles::v1::CratesToml::append(metadata_vec.iter())?; metafiles::v1::CratesToml::append(metadata_vec.iter())?;
debug!("Writing binstall/crates-v1.json"); debug!("Writing binstall/crates-v1.json");
metafiles::binstall_v1::append(metadata_vec)?; for metadata in metadata_vec {
records.replace(metadata);
}
records.overwrite()?;
} }
if opts.no_cleanup { if opts.no_cleanup {