mirror of
https://github.com/cargo-bins/cargo-binstall.git
synced 2025-04-24 22:30:03 +00:00
parent
4500e4af63
commit
763d4610e5
8 changed files with 205 additions and 95 deletions
23
README.md
23
README.md
|
@ -5,18 +5,15 @@
|
|||
`binstall` works by fetching the crate information from `crates.io`, then searching the linked `repository` for matching releases and artifacts, with fallbacks to [quickinstall](https://github.com/alsuren/cargo-quickinstall) and finally `cargo install` if these are not found.
|
||||
To support `binstall` maintainers must add configuration values to `Cargo.toml` to allow the tool to locate the appropriate binary package for a given version and target. See [SUPPORT.md](./SUPPORT.md) for more detail.
|
||||
|
||||
|
||||
## Status
|
||||
|
||||

|
||||
[](https://github.com/ryankurte/cargo-binstall)
|
||||

|
||||
[](https://github.com/cargo-bins/cargo-binstall)
|
||||
[](https://crates.io/crates/cargo-binstall)
|
||||
[](https://docs.rs/cargo-binstall)
|
||||
|
||||
## Installation
|
||||
|
||||
To get started _using_ `cargo-binstall` first install the binary (either via `cargo install cargo-binstall` or by downloading a pre-compiled [release](https://github.com/ryankurte/cargo-binstall/releases)).
|
||||
|
||||
To get started _using_ `cargo-binstall` first install the binary (either via `cargo install cargo-binstall` or by downloading a pre-compiled [release](https://github.com/cargo-bins/cargo-binstall/releases)).
|
||||
|
||||
| OS | Arch | URL |
|
||||
| ------- | ------- | ------------------------------------------------------------ |
|
||||
|
@ -27,7 +24,7 @@ To get started _using_ `cargo-binstall` first install the binary (either via `ca
|
|||
| macos | m1 | https://github.com/cargo-bins/cargo-binstall/releases/latest/download/cargo-binstall-aarch64-apple-darwin.zip |
|
||||
| windows | x86\_64 | https://github.com/cargo-bins/cargo-binstall/releases/latest/download/cargo-binstall-x86_64-pc-windows-msvc.zip |
|
||||
|
||||
|
||||
To upgrade, use `cargo binstall cargo-binstall`!
|
||||
|
||||
## Usage
|
||||
|
||||
|
@ -37,16 +34,15 @@ Package versions and targets may be specified using the `--version` and `--targe
|
|||
|
||||
```
|
||||
[garry] ➜ ~ cargo binstall radio-sx128x --version 0.14.1-alpha.5
|
||||
21:14:09 [INFO] Installing package: 'radio-sx128x'
|
||||
21:14:13 [INFO] Downloading package from: 'https://github.com/rust-iot/rust-radio-sx128x/releases/download/v0.14.1-alpha.5/sx128x-util-x86_64-apple-darwin.tgz'
|
||||
21:14:15 [INFO] Resolving package: 'radio-sx128x'
|
||||
21:14:18 [INFO] This will install the following binaries:
|
||||
21:14:18 [INFO] - sx128x-util (sx128x-util-x86_64-apple-darwin -> /Users/ryankurte/.cargo/bin/sx128x-util-v0.14.1-alpha.5)
|
||||
21:14:18 [INFO] And create (or update) the following symlinks:
|
||||
21:14:18 [INFO] - sx128x-util (/Users/ryankurte/.cargo/bin/sx128x-util-v0.14.1-alpha.5 -> /Users/ryankurte/.cargo/bin/sx128x-util)
|
||||
21:14:18 [INFO] Do you wish to continue? yes/no
|
||||
yes
|
||||
21:15:30 [INFO] Installing binaries...
|
||||
21:15:30 [INFO] Installation complete!
|
||||
21:14:18 [INFO] Do you wish to continue? yes/[no]
|
||||
? yes
|
||||
21:14:20 [INFO] Installing binaries...
|
||||
21:14:21 [INFO] Done in 6.212736s
|
||||
```
|
||||
|
||||
### Unsupported crates
|
||||
|
@ -60,7 +56,6 @@ $ binstall \
|
|||
--pkg-fmt="txz" crate_name
|
||||
```
|
||||
|
||||
|
||||
## FAQ
|
||||
|
||||
- Why use this?
|
||||
|
|
|
@ -48,11 +48,12 @@ cargo binstall --help >/dev/null
|
|||
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 --force cargo-binstall@0.11.1
|
||||
"./$1" binstall --no-confirm cargo-binstall@0.11.1 | grep -q 'cargo-binstall v0.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'
|
||||
"./$1" binstall --no-confirm cargo-binstall@0.10.0 | grep -q -v 'cargo-binstall v0.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'
|
||||
"./$1" binstall --no-confirm cargo-binstall@0.11.0
|
||||
"./$1" binstall --no-confirm cargo-binstall@0.11.0 | grep -q 'cargo-binstall v0.11.0 is already installed'
|
||||
"./$1" binstall --no-confirm cargo-binstall@^0.11.0 | grep -q -v 'cargo-binstall v0.11.0 is already installed'
|
||||
|
|
|
@ -3,17 +3,16 @@ use std::{path::PathBuf, process, sync::Arc};
|
|||
use cargo_toml::Package;
|
||||
use compact_str::CompactString;
|
||||
use log::{debug, error, info};
|
||||
use miette::{miette, IntoDiagnostic, Result, WrapErr};
|
||||
use tokio::{process::Command, task::block_in_place};
|
||||
|
||||
use super::{MetaData, Options, Resolution};
|
||||
use crate::{bins, fetchers::Fetcher, metafiles::binstall_v1::Source, *};
|
||||
use crate::{bins, fetchers::Fetcher, metafiles::binstall_v1::Source, BinstallError, *};
|
||||
|
||||
pub async fn install(
|
||||
resolution: Resolution,
|
||||
opts: Arc<Options>,
|
||||
jobserver_client: LazyJobserverClient,
|
||||
) -> Result<Option<MetaData>> {
|
||||
) -> Result<Option<MetaData>, BinstallError> {
|
||||
match resolution {
|
||||
Resolution::AlreadyUpToDate => Ok(None),
|
||||
Resolution::Fetch {
|
||||
|
@ -24,7 +23,14 @@ pub async fn install(
|
|||
bin_path,
|
||||
bin_files,
|
||||
} => {
|
||||
let current_version = package.version.parse().into_diagnostic()?;
|
||||
let current_version =
|
||||
package
|
||||
.version
|
||||
.parse()
|
||||
.map_err(|err| BinstallError::VersionParse {
|
||||
v: package.version,
|
||||
err,
|
||||
})?;
|
||||
let target = fetcher.target().into();
|
||||
|
||||
install_from_package(fetcher, opts, bin_path, bin_files)
|
||||
|
@ -45,7 +51,7 @@ pub async fn install(
|
|||
let desired_targets = opts.desired_targets.get().await;
|
||||
let target = desired_targets
|
||||
.first()
|
||||
.ok_or_else(|| miette!("No viable targets found, try with `--targets`"))?;
|
||||
.ok_or(BinstallError::NoViableTargets)?;
|
||||
|
||||
if !opts.dry_run {
|
||||
install_from_source(package, target, jobserver_client, opts.quiet, opts.force)
|
||||
|
@ -67,7 +73,7 @@ async fn install_from_package(
|
|||
opts: Arc<Options>,
|
||||
bin_path: PathBuf,
|
||||
bin_files: Vec<bins::BinFile>,
|
||||
) -> Result<Option<Vec<CompactString>>> {
|
||||
) -> Result<Option<Vec<CompactString>>, BinstallError> {
|
||||
// Download package
|
||||
if opts.dry_run {
|
||||
info!("Dry run, not downloading package");
|
||||
|
@ -129,7 +135,7 @@ async fn install_from_source(
|
|||
lazy_jobserver_client: LazyJobserverClient,
|
||||
quiet: bool,
|
||||
force: bool,
|
||||
) -> Result<()> {
|
||||
) -> Result<(), BinstallError> {
|
||||
let jobserver_client = lazy_jobserver_client.get().await?;
|
||||
|
||||
debug!(
|
||||
|
@ -156,22 +162,20 @@ async fn install_from_source(
|
|||
cmd.arg("--force");
|
||||
}
|
||||
|
||||
let mut child = cmd
|
||||
.spawn()
|
||||
.into_diagnostic()
|
||||
.wrap_err("Spawning cargo install failed.")?;
|
||||
let command_string = format!("{:?}", cmd);
|
||||
|
||||
let mut child = cmd.spawn()?;
|
||||
debug!("Spawned command pid={:?}", child.id());
|
||||
|
||||
let status = child
|
||||
.wait()
|
||||
.await
|
||||
.into_diagnostic()
|
||||
.wrap_err("Running cargo install failed.")?;
|
||||
let status = child.wait().await?;
|
||||
if status.success() {
|
||||
info!("Cargo finished successfully");
|
||||
Ok(())
|
||||
} else {
|
||||
error!("Cargo errored! {status:?}");
|
||||
Err(miette!("Cargo install error"))
|
||||
Err(BinstallError::SubProcess {
|
||||
command: command_string,
|
||||
status,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,8 +5,7 @@ use std::{
|
|||
|
||||
use cargo_toml::{Package, Product};
|
||||
use compact_str::{CompactString, ToCompactString};
|
||||
use log::{debug, error, info, warn};
|
||||
use miette::{miette, Result};
|
||||
use log::{debug, info, warn};
|
||||
use reqwest::Client;
|
||||
use semver::{Version, VersionReq};
|
||||
|
||||
|
@ -14,7 +13,7 @@ use super::Options;
|
|||
use crate::{
|
||||
bins,
|
||||
fetchers::{Data, Fetcher, GhCrateMeta, MultiFetcher, QuickInstall},
|
||||
*,
|
||||
BinstallError, *,
|
||||
};
|
||||
|
||||
pub enum Resolution {
|
||||
|
@ -84,8 +83,31 @@ pub async fn resolve(
|
|||
install_path: Arc<Path>,
|
||||
client: Client,
|
||||
crates_io_api_client: crates_io_api::AsyncClient,
|
||||
) -> Result<Resolution> {
|
||||
info!("Installing package: '{}'", crate_name);
|
||||
) -> Result<Resolution, BinstallError> {
|
||||
let crate_name_name = crate_name.name.clone();
|
||||
resolve_inner(
|
||||
opts,
|
||||
crate_name,
|
||||
curr_version,
|
||||
temp_dir,
|
||||
install_path,
|
||||
client,
|
||||
crates_io_api_client,
|
||||
)
|
||||
.await
|
||||
.map_err(|err| err.crate_context(crate_name_name))
|
||||
}
|
||||
|
||||
async fn resolve_inner(
|
||||
opts: Arc<Options>,
|
||||
crate_name: CrateName,
|
||||
curr_version: Option<Version>,
|
||||
temp_dir: Arc<Path>,
|
||||
install_path: Arc<Path>,
|
||||
client: Client,
|
||||
crates_io_api_client: crates_io_api::AsyncClient,
|
||||
) -> Result<Resolution, BinstallError> {
|
||||
info!("Resolving package: '{}'", crate_name);
|
||||
|
||||
let version_req: VersionReq = match (&crate_name.version_req, &opts.version_req) {
|
||||
(Some(version), None) => version.clone(),
|
||||
|
@ -120,7 +142,10 @@ pub async fn resolve(
|
|||
})?;
|
||||
|
||||
if new_version == curr_version {
|
||||
info!("package {crate_name} is already up to date {curr_version}");
|
||||
info!(
|
||||
"{} v{curr_version} is already installed, use --force to override",
|
||||
crate_name.name
|
||||
);
|
||||
return Ok(Resolution::AlreadyUpToDate);
|
||||
}
|
||||
}
|
||||
|
@ -208,7 +233,7 @@ fn collect_bin_files(
|
|||
binaries: Vec<Product>,
|
||||
bin_path: PathBuf,
|
||||
install_path: PathBuf,
|
||||
) -> Result<Vec<bins::BinFile>> {
|
||||
) -> Result<Vec<bins::BinFile>, BinstallError> {
|
||||
// Update meta
|
||||
if fetcher.source_name() == "QuickInstall" {
|
||||
// TODO: less of a hack?
|
||||
|
@ -217,10 +242,7 @@ fn collect_bin_files(
|
|||
|
||||
// Check binaries
|
||||
if binaries.is_empty() {
|
||||
error!("No binaries specified (or inferred from file system)");
|
||||
return Err(miette!(
|
||||
"No binaries specified (or inferred from file system)"
|
||||
));
|
||||
return Err(BinstallError::UnspecifiedBinaries);
|
||||
}
|
||||
|
||||
// List files to be installed
|
||||
|
|
100
src/errors.rs
100
src/errors.rs
|
@ -1,13 +1,13 @@
|
|||
use std::process::{ExitCode, Termination};
|
||||
use std::process::{ExitCode, ExitStatus, Termination};
|
||||
|
||||
use compact_str::CompactString;
|
||||
use log::{error, warn};
|
||||
use miette::{Diagnostic, Report};
|
||||
use thiserror::Error;
|
||||
use tokio::task;
|
||||
|
||||
/// Errors emitted by cargo-binstall.
|
||||
/// Error kinds emitted by cargo-binstall.
|
||||
#[derive(Error, Diagnostic, Debug)]
|
||||
#[diagnostic(url(docsrs))]
|
||||
#[non_exhaustive]
|
||||
pub enum BinstallError {
|
||||
/// Internal: a task could not be joined.
|
||||
|
@ -71,7 +71,7 @@ pub enum BinstallError {
|
|||
///
|
||||
/// - Code: `binstall::http`
|
||||
/// - Exit: 69
|
||||
#[error("could not {method} {url}: {err}")]
|
||||
#[error("could not {method} {url}")]
|
||||
#[diagnostic(severity(error), code(binstall::http))]
|
||||
Http {
|
||||
method: reqwest::Method,
|
||||
|
@ -80,6 +80,16 @@ pub enum BinstallError {
|
|||
err: reqwest::Error,
|
||||
},
|
||||
|
||||
/// A subprocess failed.
|
||||
///
|
||||
/// This is often about cargo-install calls.
|
||||
///
|
||||
/// - Code: `binstall::subprocess`
|
||||
/// - Exit: 70
|
||||
#[error("subprocess {command} errored with {status}")]
|
||||
#[diagnostic(severity(error), code(binstall::subprocess))]
|
||||
SubProcess { command: String, status: ExitStatus },
|
||||
|
||||
/// A generic I/O error.
|
||||
///
|
||||
/// - Code: `binstall::io`
|
||||
|
@ -94,7 +104,7 @@ pub enum BinstallError {
|
|||
///
|
||||
/// - Code: `binstall::crates_io_api`
|
||||
/// - Exit: 76
|
||||
#[error("crates.io api error fetching crate information for '{crate_name}': {err}")]
|
||||
#[error("crates.io API error")]
|
||||
#[diagnostic(
|
||||
severity(error),
|
||||
code(binstall::crates_io_api),
|
||||
|
@ -137,7 +147,7 @@ pub enum BinstallError {
|
|||
///
|
||||
/// - Code: `binstall::version::parse`
|
||||
/// - Exit: 80
|
||||
#[error("version string '{v}' is not semver: {err}")]
|
||||
#[error("version string '{v}' is not semver")]
|
||||
#[diagnostic(severity(error), code(binstall::version::parse))]
|
||||
VersionParse {
|
||||
v: String,
|
||||
|
@ -154,7 +164,7 @@ pub enum BinstallError {
|
|||
///
|
||||
/// - Code: `binstall::version::requirement`
|
||||
/// - Exit: 81
|
||||
#[error("version requirement '{req}' is not semver: {err}")]
|
||||
#[error("version requirement '{req}' is not semver")]
|
||||
#[diagnostic(severity(error), code(binstall::version::requirement))]
|
||||
VersionReq {
|
||||
req: String,
|
||||
|
@ -220,17 +230,52 @@ pub enum BinstallError {
|
|||
help("You cannot use --{option} and specify multiple packages at the same time. Do one or the other.")
|
||||
)]
|
||||
OverrideOptionUsedWithMultiInstall { option: &'static str },
|
||||
|
||||
/// No binaries were found for the crate.
|
||||
///
|
||||
/// When installing, either the binaries are specified in the crate's Cargo.toml, or they're
|
||||
/// inferred from the crate layout (e.g. src/main.rs or src/bins/name.rs). If no binaries are
|
||||
/// found through these methods, we can't know what to install!
|
||||
///
|
||||
/// - Code: `binstall::resolve::binaries`
|
||||
/// - Exit: 86
|
||||
#[error("no binaries specified nor inferred")]
|
||||
#[diagnostic(
|
||||
severity(error),
|
||||
code(binstall::resolve::binaries),
|
||||
help("This crate doesn't specify any binaries, so there's nothing to install.")
|
||||
)]
|
||||
UnspecifiedBinaries,
|
||||
|
||||
/// No viable targets were found.
|
||||
///
|
||||
/// When installing, we attempt to find which targets the host (your computer) supports, and
|
||||
/// discover builds for these targets from the remote binary source. This error occurs when we
|
||||
/// fail to discover the host's target.
|
||||
///
|
||||
/// You should in this case specify --target manually.
|
||||
///
|
||||
/// - Code: `binstall::targets::none_host`
|
||||
/// - Exit: 87
|
||||
#[error("failed to discovered a viable target from the host")]
|
||||
#[diagnostic(
|
||||
severity(error),
|
||||
code(binstall::targets::none_host),
|
||||
help("Try to specify --target")
|
||||
)]
|
||||
NoViableTargets,
|
||||
|
||||
/// A wrapped error providing the context of which crate the error is about.
|
||||
#[error("for crate {crate_name}")]
|
||||
CrateContext {
|
||||
#[source]
|
||||
error: Box<BinstallError>,
|
||||
crate_name: CompactString,
|
||||
},
|
||||
}
|
||||
|
||||
impl BinstallError {
|
||||
/// The recommended exit code for this error.
|
||||
///
|
||||
/// This will never output:
|
||||
/// - 0 (success)
|
||||
/// - 1 and 2 (catchall and shell)
|
||||
/// - 16 (binstall errors not handled here)
|
||||
/// - 64 (generic error)
|
||||
pub fn exit_code(&self) -> ExitCode {
|
||||
fn exit_number(&self) -> u8 {
|
||||
use BinstallError::*;
|
||||
let code: u8 = match self {
|
||||
TaskJoinError(_) => 17,
|
||||
|
@ -240,6 +285,7 @@ impl BinstallError {
|
|||
Template(_) => 67,
|
||||
Reqwest(_) => 68,
|
||||
Http { .. } => 69,
|
||||
SubProcess { .. } => 70,
|
||||
Io(_) => 74,
|
||||
CratesIoApi { .. } => 76,
|
||||
CargoManifestPath => 77,
|
||||
|
@ -250,12 +296,34 @@ impl BinstallError {
|
|||
VersionUnavailable { .. } => 83,
|
||||
SuperfluousVersionOption => 84,
|
||||
OverrideOptionUsedWithMultiInstall { .. } => 85,
|
||||
UnspecifiedBinaries => 86,
|
||||
NoViableTargets => 87,
|
||||
CrateContext { error, .. } => error.exit_number(),
|
||||
};
|
||||
|
||||
// reserved codes
|
||||
debug_assert!(code != 64 && code != 16 && code != 1 && code != 2 && code != 0);
|
||||
|
||||
code.into()
|
||||
code
|
||||
}
|
||||
|
||||
/// The recommended exit code for this error.
|
||||
///
|
||||
/// This will never output:
|
||||
/// - 0 (success)
|
||||
/// - 1 and 2 (catchall and shell)
|
||||
/// - 16 (binstall errors not handled here)
|
||||
/// - 64 (generic error)
|
||||
pub fn exit_code(&self) -> ExitCode {
|
||||
self.exit_number().into()
|
||||
}
|
||||
|
||||
/// Add crate context to the error
|
||||
pub fn crate_context(self, crate_name: impl Into<CompactString>) -> Self {
|
||||
Self::CrateContext {
|
||||
error: Box::new(self),
|
||||
crate_name: crate_name.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ use std::path::Path;
|
|||
use std::sync::Arc;
|
||||
|
||||
use compact_str::{CompactString, ToCompactString};
|
||||
use log::{debug, info, warn};
|
||||
use log::{debug, warn};
|
||||
use once_cell::sync::OnceCell;
|
||||
use reqwest::Client;
|
||||
use reqwest::Method;
|
||||
|
@ -43,7 +43,7 @@ impl super::Fetcher for GhCrateMeta {
|
|||
let client = self.client.clone();
|
||||
AutoAbortJoinHandle::spawn(async move {
|
||||
let url = url?;
|
||||
info!("Checking for package at: '{url}'");
|
||||
debug!("Checking for package at: '{url}'");
|
||||
remote_exists(client, url.clone(), Method::HEAD)
|
||||
.await
|
||||
.map(|exists| (url.clone(), exists))
|
||||
|
@ -61,7 +61,7 @@ impl super::Fetcher for GhCrateMeta {
|
|||
);
|
||||
}
|
||||
|
||||
info!("Winning URL is {url}");
|
||||
debug!("Winning URL is {url}");
|
||||
self.url.set(url).unwrap(); // find() is called first
|
||||
return Ok(true);
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ impl super::Fetcher for GhCrateMeta {
|
|||
|
||||
async fn fetch_and_extract(&self, dst: &Path) -> Result<(), BinstallError> {
|
||||
let url = self.url.get().unwrap(); // find() is called first
|
||||
info!("Downloading package from: '{url}'");
|
||||
debug!("Downloading package from: '{url}'");
|
||||
download_and_extract(&self.client, url, self.pkg_fmt(), dst).await
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ use std::path::Path;
|
|||
use std::sync::Arc;
|
||||
|
||||
use compact_str::CompactString;
|
||||
use log::{debug, info};
|
||||
use log::debug;
|
||||
use reqwest::Client;
|
||||
use reqwest::Method;
|
||||
use tokio::task::JoinHandle;
|
||||
|
@ -36,13 +36,13 @@ impl super::Fetcher for QuickInstall {
|
|||
async fn find(&self) -> Result<bool, BinstallError> {
|
||||
let url = self.package_url();
|
||||
self.report();
|
||||
info!("Checking for package at: '{url}'");
|
||||
debug!("Checking for package at: '{url}'");
|
||||
remote_exists(self.client.clone(), Url::parse(&url)?, Method::HEAD).await
|
||||
}
|
||||
|
||||
async fn fetch_and_extract(&self, dst: &Path) -> Result<(), BinstallError> {
|
||||
let url = self.package_url();
|
||||
info!("Downloading package from: '{url}'");
|
||||
debug!("Downloading package from: '{url}'");
|
||||
download_and_extract(&self.client, &Url::parse(&url)?, self.pkg_fmt(), dst).await
|
||||
}
|
||||
|
||||
|
|
62
src/main.rs
62
src/main.rs
|
@ -36,7 +36,11 @@ struct Options {
|
|||
///
|
||||
/// If duplicate names are provided, the last one (and their version requirement)
|
||||
/// is kept.
|
||||
#[clap(help_heading = "Package selection", value_name = "crate[@version]")]
|
||||
#[clap(
|
||||
help_heading = "Package selection",
|
||||
value_name = "crate[@version]",
|
||||
required_unless_present_any = ["version", "help"],
|
||||
)]
|
||||
crate_names: Vec<CrateName>,
|
||||
|
||||
/// Package version to install.
|
||||
|
@ -202,7 +206,7 @@ impl Termination for MainExit {
|
|||
fn report(self) -> ExitCode {
|
||||
match self {
|
||||
Self::Success(spent) => {
|
||||
info!("Installation completed in {spent:?}");
|
||||
info!("Done in {spent:?}");
|
||||
ExitCode::SUCCESS
|
||||
}
|
||||
Self::Error(err) => err.report(),
|
||||
|
@ -354,31 +358,39 @@ async fn entry(jobserver_client: LazyJobserverClient) -> Result<()> {
|
|||
})?;
|
||||
|
||||
// Remove installed crates
|
||||
let crate_names = CrateName::dedup(crate_names).filter_map(|crate_name| {
|
||||
if opts.force {
|
||||
Some((crate_name, None))
|
||||
} else if let Some(records) = &metadata {
|
||||
if let Some(metadata) = records.get(&crate_name.name) {
|
||||
if let Some(version_req) = &crate_name.version_req {
|
||||
if version_req.is_latest_compatible(&metadata.current_version) {
|
||||
let crate_names = CrateName::dedup(crate_names)
|
||||
.filter_map(|crate_name| {
|
||||
match (
|
||||
opts.force,
|
||||
metadata.as_ref().and_then(|records| records.get(&crate_name.name)),
|
||||
&crate_name.version_req,
|
||||
) {
|
||||
(false, Some(metadata), Some(version_req))
|
||||
if version_req.is_latest_compatible(&metadata.current_version) =>
|
||||
{
|
||||
debug!("Bailing out early because we can assume wanted is already installed from metafile");
|
||||
info!(
|
||||
"package {crate_name} is already installed and cannot be upgraded, use --force to override"
|
||||
"{} v{} is already installed, use --force to override",
|
||||
crate_name.name, metadata.current_version
|
||||
);
|
||||
None
|
||||
} else {
|
||||
}
|
||||
|
||||
// we have to assume that the version req could be *,
|
||||
// and therefore a remote upgraded version could exist
|
||||
(false, Some(metadata), _) => {
|
||||
Some((crate_name, Some(metadata.current_version.clone())))
|
||||
}
|
||||
} else {
|
||||
info!("package {crate_name} is already installed, use --force to override");
|
||||
None
|
||||
|
||||
_ => Some((crate_name, None)),
|
||||
}
|
||||
} else {
|
||||
Some((crate_name, None))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if crate_names.is_empty() {
|
||||
debug!("Nothing to do");
|
||||
return Ok(());
|
||||
}
|
||||
} else {
|
||||
Some((crate_name, None))
|
||||
}
|
||||
});
|
||||
|
||||
let temp_dir_path: Arc<Path> = Arc::from(temp_dir.path());
|
||||
|
||||
|
@ -414,7 +426,15 @@ async fn entry(jobserver_client: LazyJobserverClient) -> Result<()> {
|
|||
// Confirm
|
||||
let mut resolutions = Vec::with_capacity(tasks.len());
|
||||
for task in tasks {
|
||||
resolutions.push(task.await??);
|
||||
match task.await?? {
|
||||
binstall::Resolution::AlreadyUpToDate => {}
|
||||
res => resolutions.push(res),
|
||||
}
|
||||
}
|
||||
|
||||
if resolutions.is_empty() {
|
||||
debug!("Nothing to do");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
uithread.confirm().await?;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue