Feature: Impl opt-out options (#510)

Fixed #136

* Impl opt-out optioins in binstalk
* Replace field `Options::{gh_crate, quickinstall}_fetcher` with `resolver`
  which can determine order of resolver used.
* Add new field `Args::{disable_}strategies`
* Add new variant `BinstallError::InvalidStrategies`
* Add variant `BinstallError::NoFallbackToCargoInstall`
* Add code for supporting strategies in mod entry
* Test `--disable-strategies` in `tests.sh`
* Test for `--strategies` in `tests.sh`

Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com>
This commit is contained in:
Jiahao XU 2022-11-11 16:24:46 +11:00 committed by GitHub
parent 89fa5b1769
commit 9e80cf0700
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 141 additions and 11 deletions

View file

@ -77,16 +77,13 @@ cargo binstall --help >/dev/null
"./$1" binstall --no-confirm cargo-binstall@0.12.0 | grep -q 'cargo-binstall v0.12.0 is already installed' "./$1" binstall --no-confirm cargo-binstall@0.12.0 | grep -q 'cargo-binstall v0.12.0 is already installed'
"./$1" binstall --no-confirm cargo-binstall@^0.12.0 | grep -q -v 'cargo-binstall v0.12.0 is already installed' "./$1" binstall --no-confirm cargo-binstall@^0.12.0 | grep -q -v 'cargo-binstall v0.12.0 is already installed'
# to force failure if falling back to source
# FIXME: remove/replace once #136 lands
export PATH="$test_resources/fake-cargo:$PATH"
# Test default GitLab pkg-url templates # Test default GitLab pkg-url templates
"./$1" binstall \ "./$1" binstall \
--force \ --force \
--manifest-path "$test_resources/gitlab-test-Cargo.toml" \ --manifest-path "$test_resources/gitlab-test-Cargo.toml" \
--log-level debug \ --log-level debug \
--no-confirm \ --no-confirm \
--disable-strategies compile \
cargo-binstall cargo-binstall
# Test default BitBucket pkg-url templates # Test default BitBucket pkg-url templates
@ -95,6 +92,7 @@ export PATH="$test_resources/fake-cargo:$PATH"
--manifest-path "$test_resources/bitbucket-test-Cargo.toml" \ --manifest-path "$test_resources/bitbucket-test-Cargo.toml" \
--log-level debug \ --log-level debug \
--no-confirm \ --no-confirm \
--disable-strategies compile \
cargo-binstall cargo-binstall
# Test default Github pkg-url templates, # Test default Github pkg-url templates,
@ -104,4 +102,25 @@ export PATH="$test_resources/fake-cargo:$PATH"
--manifest-path "$test_resources/github-test-Cargo2.toml" \ --manifest-path "$test_resources/github-test-Cargo2.toml" \
--log-level debug \ --log-level debug \
--no-confirm \ --no-confirm \
--disable-strategies compile \
cargo-binstall cargo-binstall
## Test --disable-strategies
set +e
"./$1" binstall --no-confirm --disable-strategies quick-install,compile cargo-update
exit_code="$?"
if [ "$exit_code" != 94 ]; then
echo "Expected exit code 94, but actual exit code $exit_code"
exit 1
fi
## Test --strategies
"./$1" binstall --no-confirm --strategies crate-meta-data cargo-update
exit_code="$?"
if [ "$exit_code" != 94 ]; then
echo "Expected exit code 94, but actual exit code $exit_code"
exit 1
fi

View file

@ -129,6 +129,19 @@ pub struct Args {
#[clap(help_heading = "Overrides", long, default_value_t = RateLimit::default())] #[clap(help_heading = "Overrides", long, default_value_t = RateLimit::default())]
pub rate_limit: RateLimit, pub rate_limit: RateLimit,
/// Specify the strategies to be used,
/// binstall will run the strategies specified in order.
///
/// Default value is "crate-meta-data,quick-install,compile".
#[clap(help_heading = "Overrides", long, value_delimiter(','))]
pub strategies: Vec<Strategy>,
/// Disable the strategies specified.
/// If a strategy is specified in `--strategies` and `--disable-strategies`,
/// then it will be removed.
#[clap(help_heading = "Overrides", long, value_delimiter(','))]
pub disable_strategies: Vec<Strategy>,
/// Disable symlinking / versioned updates. /// Disable symlinking / versioned updates.
/// ///
/// By default, Binstall will install a binary named `<name>-<version>` in the install path, and /// By default, Binstall will install a binary named `<name>-<version>` in the install path, and
@ -282,6 +295,18 @@ impl Default for RateLimit {
} }
} }
/// Strategy for installing the package
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, ValueEnum)]
pub enum Strategy {
/// Attempt to download official pre-built artifacts using
/// information provided in `Cargo.toml`.
CrateMetaData,
/// Query third-party QuickInstall for the crates.
QuickInstall,
/// Build the crates from source using `cargo-build`.
Compile,
}
pub fn parse() -> Result<Args, BinstallError> { pub fn parse() -> Result<Args, BinstallError> {
// Filter extraneous arg when invoked by cargo // Filter extraneous arg when invoked by cargo
// `cargo run -- --help` gives ["target/debug/cargo-binstall", "--help"] // `cargo run -- --help` gives ["target/debug/cargo-binstall", "--help"]

View file

@ -1,7 +1,8 @@
use std::{fs, path::Path, sync::Arc, time::Duration}; use std::{fs, mem, path::Path, sync::Arc, time::Duration};
use binstalk::{ use binstalk::{
errors::BinstallError, errors::BinstallError,
fetchers::{Fetcher, GhCrateMeta, QuickInstall},
get_desired_targets, get_desired_targets,
helpers::{jobserver_client::LazyJobserverClient, remote::Client, tasks::AutoAbortJoinHandle}, helpers::{jobserver_client::LazyJobserverClient, remote::Client, tasks::AutoAbortJoinHandle},
manifests::{ manifests::{
@ -16,9 +17,61 @@ use log::{debug, error, info, warn, LevelFilter};
use miette::{miette, Result, WrapErr}; use miette::{miette, Result, WrapErr};
use tokio::task::block_in_place; use tokio::task::block_in_place;
use crate::{args::Args, install_path, ui::UIThread}; use crate::{
args::{Args, Strategy},
install_path,
ui::UIThread,
};
pub async fn install_crates(mut args: Args, jobserver_client: LazyJobserverClient) -> Result<()> { pub async fn install_crates(mut args: Args, jobserver_client: LazyJobserverClient) -> Result<()> {
let mut strategies = vec![];
// Remove duplicate strategies
for strategy in mem::take(&mut args.strategies) {
if !strategies.contains(&strategy) {
strategies.push(strategy);
}
}
// Default strategies if empty
if strategies.is_empty() {
strategies = vec![
Strategy::CrateMetaData,
Strategy::QuickInstall,
Strategy::Compile,
];
}
let disable_strategies = mem::take(&mut args.disable_strategies);
let mut strategies: Vec<Strategy> = if !disable_strategies.is_empty() {
strategies
.into_iter()
.filter(|strategy| !disable_strategies.contains(strategy))
.collect()
} else {
strategies
};
if strategies.is_empty() {
return Err(BinstallError::InvalidStrategies(&"No strategy is provided").into());
}
let cargo_install_fallback = *strategies.last().unwrap() == Strategy::Compile;
if cargo_install_fallback {
strategies.pop().unwrap();
}
let resolver: Vec<_> = strategies
.into_iter()
.map(|strategy| match strategy {
Strategy::CrateMetaData => GhCrateMeta::new,
Strategy::QuickInstall => QuickInstall::new,
Strategy::Compile => unreachable!(),
})
.collect();
let cli_overrides = PkgOverride { let cli_overrides = PkgOverride {
pkg_url: args.pkg_url.take(), pkg_url: args.pkg_url.take(),
pkg_fmt: args.pkg_fmt.take(), pkg_fmt: args.pkg_fmt.take(),
@ -138,6 +191,7 @@ pub async fn install_crates(mut args: Args, jobserver_client: LazyJobserverClien
cli_overrides, cli_overrides,
desired_targets, desired_targets,
quiet: args.log_level == LevelFilter::Off, quiet: args.log_level == LevelFilter::Off,
resolver,
}); });
let tasks: Vec<_> = if !args.dry_run && !args.no_confirm { let tasks: Vec<_> = if !args.dry_run && !args.no_confirm {
@ -208,7 +262,13 @@ pub async fn install_crates(mut args: Args, jobserver_client: LazyJobserverClien
) )
.await?; .await?;
ops::install::install(resolution, opts, jobserver_client).await if !cargo_install_fallback
&& matches!(resolution, Resolution::InstallFromSource { .. })
{
Err(BinstallError::NoFallbackToCargoInstall)
} else {
ops::install::install(resolution, opts, jobserver_client).await
}
}) })
}) })
.collect() .collect()

View file

@ -310,6 +310,22 @@ pub enum BinstallError {
#[diagnostic(severity(error), code(binstall::SourceFilePath))] #[diagnostic(severity(error), code(binstall::SourceFilePath))]
EmptySourceFilePath, EmptySourceFilePath,
/// Invalid strategies configured.
///
/// - Code: `binstall::strategies`
/// - Exit: 93
#[error("Invalid strategies configured: {0}")]
#[diagnostic(severity(error), code(binstall::strategies))]
InvalidStrategies(&'static &'static str),
/// Fallback to `cargo-install` is disabled.
///
/// - Code: `binstall::no_fallback_to_cargo_install`
/// - Exit: 94
#[error("Fallback to cargo-install is disabled")]
#[diagnostic(severity(error), code(binstall::no_fallback_to_cargo_install))]
NoFallbackToCargoInstall,
/// A wrapped error providing the context of which crate the error is about. /// A wrapped error providing the context of which crate the error is about.
#[error("for crate {crate_name}")] #[error("for crate {crate_name}")]
CrateContext { CrateContext {
@ -348,6 +364,8 @@ impl BinstallError {
DuplicateSourceFilePath { .. } => 90, DuplicateSourceFilePath { .. } => 90,
InvalidSourceFilePath { .. } => 91, InvalidSourceFilePath { .. } => 91,
EmptySourceFilePath => 92, EmptySourceFilePath => 92,
InvalidStrategies(..) => 93,
NoFallbackToCargoInstall => 94,
CrateContext { error, .. } => error.exit_number(), CrateContext { error, .. } => error.exit_number(),
}; };

View file

@ -1,14 +1,21 @@
//! Concrete Binstall operations. //! Concrete Binstall operations.
use std::path::PathBuf; use std::{path::PathBuf, sync::Arc};
use semver::VersionReq; use semver::VersionReq;
use crate::{manifests::cargo_toml_binstall::PkgOverride, DesiredTargets}; use crate::{
fetchers::{Data, Fetcher},
helpers::remote::Client,
manifests::cargo_toml_binstall::PkgOverride,
DesiredTargets,
};
pub mod install; pub mod install;
pub mod resolve; pub mod resolve;
pub type Resolver = fn(&Client, &Arc<Data>) -> Arc<dyn Fetcher>;
pub struct Options { pub struct Options {
pub no_symlinks: bool, pub no_symlinks: bool,
pub dry_run: bool, pub dry_run: bool,
@ -18,4 +25,5 @@ pub struct Options {
pub cli_overrides: PkgOverride, pub cli_overrides: PkgOverride,
pub desired_targets: DesiredTargets, pub desired_targets: DesiredTargets,
pub quiet: bool, pub quiet: bool,
pub resolver: Vec<Resolver>,
} }

View file

@ -18,7 +18,7 @@ use crate::{
bins, bins,
drivers::fetch_crate_cratesio, drivers::fetch_crate_cratesio,
errors::BinstallError, errors::BinstallError,
fetchers::{Data, Fetcher, GhCrateMeta, QuickInstall}, fetchers::{Data, Fetcher},
helpers::{remote::Client, tasks::AutoAbortJoinHandle}, helpers::{remote::Client, tasks::AutoAbortJoinHandle},
manifests::cargo_toml_binstall::{Meta, PkgMeta}, manifests::cargo_toml_binstall::{Meta, PkgMeta},
}; };
@ -211,7 +211,7 @@ async fn resolve_inner(
meta: target_meta, meta: target_meta,
}) })
}) })
.cartesian_product([GhCrateMeta::new, QuickInstall::new]) .cartesian_product(&opts.resolver)
.map(|(fetcher_data, f)| { .map(|(fetcher_data, f)| {
let fetcher = f(&client, &fetcher_data); let fetcher = f(&client, &fetcher_data);
( (