Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/cli/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ Can also use `MISE_NO_CONFIG=1`
- [`mise generate tool-stub [FLAGS] <OUTPUT>`](/cli/generate/tool-stub.md)
- [`mise implode [--config] [-n --dry-run]`](/cli/implode.md)
- [`mise install [FLAGS] [TOOL@VERSION]…`](/cli/install.md)
- [`mise install-into <TOOL@VERSION> <PATH>`](/cli/install-into.md)
- [`mise install-into [--retry <RETRY>] <TOOL@VERSION> <PATH>`](/cli/install-into.md)
- [`mise latest [-i --installed] <TOOL@VERSION>`](/cli/latest.md)
- [`mise link [-f --force] <TOOL@VERSION> <PATH>`](/cli/link.md)
- [`mise lock [FLAGS] [TOOL]…`](/cli/lock.md)
Expand Down
8 changes: 7 additions & 1 deletion docs/cli/install-into.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# `mise install-into`

- **Usage**: `mise install-into <TOOL@VERSION> <PATH>`
- **Usage**: `mise install-into [--retry <RETRY>] <TOOL@VERSION> <PATH>`
- **Source code**: [`src/cli/install_into.rs`](https://github.com/jdx/mise/blob/main/src/cli/install_into.rs)

Install a tool version to a specific path
Expand All @@ -17,6 +17,12 @@ Tool to install e.g.: node@20

Path to install the tool into

## Flags

### `--retry <RETRY>`

Retry installation if it fails due to transient errors, e.g. network issues

Examples:

```
Expand Down
4 changes: 4 additions & 0 deletions docs/cli/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ Show installation output

This argument will print plugin output such as download, configuration, and compilation output.

### `--retry <RETRY>`

Retry installation if it fails due to transient errors, e.g. network issues

Examples:

```
Expand Down
6 changes: 6 additions & 0 deletions mise.usage.kdl
Original file line number Diff line number Diff line change
Expand Up @@ -452,11 +452,17 @@ cmd install help="Install a tool version" {
flag "-v --verbose" help="Show installation output" var=#true count=#true {
long_help "Show installation output\n\nThis argument will print plugin output such as download, configuration, and compilation output."
}
flag --retry help="Retry installation if it fails due to transient errors, e.g. network issues" {
arg <RETRY>
}
arg "[TOOL@VERSION]…" help="Tool(s) to install e.g.: node@20" required=#false var=#true
}
cmd install-into help="Install a tool version to a specific path" {
long_help "Install a tool version to a specific path\n\nUsed for building a tool to a directory for use outside of mise"
after_long_help "Examples:\n\n # install node@20.0.0 into ./mynode\n $ mise install-into node@20.0.0 ./mynode && ./mynode/bin/node -v\n 20.0.0\n"
flag --retry help="Retry installation if it fails due to transient errors, e.g. network issues" {
arg <RETRY>
}
arg <TOOL@VERSION> help="Tool to install e.g.: node@20"
arg <PATH> help="Path to install the tool into"
}
Expand Down
63 changes: 56 additions & 7 deletions src/backend/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ use std::hash::Hash;
use std::ops::Deref;
use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex};
use std::time::Duration;
use tokio::sync::Mutex as TokioMutex;

use crate::cli::args::{BackendArg, ToolVersionType};
use crate::cmd::CmdLineRunner;
use crate::config::{Config, Settings};
use crate::errors::Error;
use crate::file::{display_path, remove_all, remove_all_with_warning};
use crate::install_context::InstallContext;
use crate::lockfile::PlatformInfo;
Expand Down Expand Up @@ -502,13 +504,60 @@ pub trait Backend: Debug + Send + Sync {
let _lock = lock_file::get(&tv.install_path(), ctx.force)?;
self.create_install_dirs(&tv)?;

let old_tv = tv.clone();
let tv = match self.install_version_(&ctx, tv).await {
Ok(tv) => tv,
Err(e) => {
self.cleanup_install_dirs_on_error(&old_tv);
// Pass through the error - it will be wrapped at a higher level
return Err(e);
let mut current_retry = 0;
let tv = loop {
match self.install_version_(&ctx, tv.clone()).await {
Ok(tv) => break tv,
Err(e) => {
self.cleanup_install_dirs_on_error(&tv);

trait IsRetryable {
fn is_retryable(&self) -> bool;
}

impl IsRetryable for eyre::Report {
fn is_retryable(&self) -> bool {
if let Some(reqwest_error) = self.downcast_ref::<reqwest::Error>() {
return reqwest_error.is_request()
&& (reqwest_error.is_connect() || reqwest_error.is_timeout());
}

false
}
}

if current_retry < ctx.retry && e.is_retryable() {
const MAX_BACKOFF_TIME: u64 = 60;

let backoff_time = 2u64.pow(current_retry.into()).min(MAX_BACKOFF_TIME);
current_retry += 1;
ctx.pr.println(format!(
"{}",
Error::InstallFailed {
successful_installations: vec![],
failed_installations: vec![(tv.request.clone(), e)],
}
));
ctx.pr.println(format!(
"Retrying installation … ({current_retry}/{})",
ctx.retry
));

for i in 0..backoff_time {
ctx.pr.set_message(format!(
"install failed (retrying in {} seconds …)",
backoff_time - i
));
tokio::time::sleep(Duration::from_secs(1)).await;
}
ctx.pr.set_message("install".into());

continue;
}

// Pass through the error - it will be wrapped at a higher level
return Err(e);
}
}
};

Expand Down
5 changes: 5 additions & 0 deletions src/cli/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ pub struct Install {
/// This argument will print plugin output such as download, configuration, and compilation output.
#[clap(long, short, action = clap::ArgAction::Count)]
verbose: u8,

/// Retry installation if it fails due to transient errors, e.g. network issues.
#[clap(long, default_value_t = 0)]
retry: u8,
}

impl Install {
Expand Down Expand Up @@ -117,6 +121,7 @@ impl Install {
latest_versions: true,
},
dry_run: self.dry_run,
retry: self.retry,
..Default::default()
}
}
Expand Down
5 changes: 5 additions & 0 deletions src/cli/install_into.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ pub struct InstallInto {
/// Path to install the tool into
#[clap(value_hint = ValueHint::DirPath)]
path: PathBuf,

/// Retry installation if it fails due to transient errors, e.g. network issues.
#[clap(long, default_value_t = 0)]
retry: u8,
}

impl InstallInto {
Expand Down Expand Up @@ -48,6 +52,7 @@ impl InstallInto {
pr: mpr.add(&tv.style()),
force: true,
dry_run: false,
retry: self.retry,
};
tv.install_path = Some(self.path.clone());
backend.install_version(install_ctx, tv).await?;
Expand Down
1 change: 1 addition & 0 deletions src/install_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ pub struct InstallContext {
pub pr: Box<dyn SingleReport>,
pub force: bool,
pub dry_run: bool,
pub retry: u8,
}
3 changes: 3 additions & 0 deletions src/toolset/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ pub struct InstallOptions {
pub auto_install_disable_tools: Option<Vec<String>>,
pub resolve_options: ResolveOptions,
pub dry_run: bool,
pub retry: u8,
}

impl Default for InstallOptions {
Expand All @@ -75,6 +76,7 @@ impl Default for InstallOptions {
auto_install_disable_tools: Settings::get().auto_install_disable_tools.clone(),
resolve_options: Default::default(),
dry_run: false,
retry: 0,
}
}
}
Expand Down Expand Up @@ -454,6 +456,7 @@ impl Toolset {
pr: mpr.add_with_options(&tv.style(), opts.dry_run),
force: opts.force,
dry_run: opts.dry_run,
retry: opts.retry,
};
// Avoid wrapping the backend error here so the error location
// points to the backend implementation (more helpful for debugging).
Expand Down
20 changes: 20 additions & 0 deletions xtasks/fig/src/mise.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1374,6 +1374,15 @@ const completionSpec: Fig.Spec = {
description: "Show installation output",
isRepeatable: true,
},
{
name: "--retry",
description:
"Retry installation if it fails due to transient errors, e.g. network issues",
isRepeatable: false,
args: {
name: "retry",
},
},
],
args: {
name: "tool@version",
Expand All @@ -1387,6 +1396,17 @@ const completionSpec: Fig.Spec = {
{
name: "install-into",
description: "Install a tool version to a specific path",
options: [
{
name: "--retry",
description:
"Retry installation if it fails due to transient errors, e.g. network issues",
isRepeatable: false,
args: {
name: "retry",
},
},
],
args: [
{
name: "tool@version",
Expand Down
Loading