From ca0cff26f2683d1c2050e3c98676c937be7dd438 Mon Sep 17 00:00:00 2001 From: jdx <216188+jdx@users.noreply.github.com> Date: Sun, 8 Dec 2024 09:20:47 -0600 Subject: [PATCH] perf: make `ls --offline` default behavior (#3409) This made more sense before the dedicated `mise outdated` command and before backends since now people have a lot more tools generally Fixes #3408 --- docs/cli/index.md | 2 +- docs/cli/ls.md | 4 +++ e2e/cli/test_upgrade | 2 +- man/man1/mise.1 | 2 +- mise.usage.kdl | 3 ++- schema/mise.json | 4 +-- settings.toml | 4 +-- src/backend/asdf.rs | 30 ++++++++++++++--------- src/backend/go.rs | 48 ++++++++++++++++++++---------------- src/backend/mod.rs | 2 +- src/backend/npm.rs | 53 ++++++++++++++++++++++++---------------- src/backend/vfox.rs | 25 +++++++++++-------- src/cli/ls.rs | 10 +++++--- src/cli/mod.rs | 2 +- src/plugins/core/ruby.rs | 35 ++++++++++++++------------ xtasks/fig/src/mise.ts | 9 ++++++- 16 files changed, 142 insertions(+), 93 deletions(-) diff --git a/docs/cli/index.md b/docs/cli/index.md index be1cb4954e..f91c7b84ed 100644 --- a/docs/cli/index.md +++ b/docs/cli/index.md @@ -24,7 +24,7 @@ Set the environment for loading `mise..toml` ### `-j --jobs ` -How many jobs to run in parallel [default: 4] +How many jobs to run in parallel [default: 8] ### `-q --quiet` diff --git a/docs/cli/ls.md b/docs/cli/ls.md index 50422aae37..7f536e9b49 100644 --- a/docs/cli/ls.md +++ b/docs/cli/ls.md @@ -36,6 +36,10 @@ Only show tool versions that are installed (Hides tools defined in mise.toml but Don't fetch information such as outdated versions +### `--outdated` + +Display whether a version is outdated + ### `-J --json` Output in JSON format diff --git a/e2e/cli/test_upgrade b/e2e/cli/test_upgrade index c702d3d2fc..08401eb04a 100644 --- a/e2e/cli/test_upgrade +++ b/e2e/cli/test_upgrade @@ -29,7 +29,7 @@ rm -f .tool-versions echo "tools.dummy = 'prefix:1'" >mise.toml mise uninstall dummy --all mkdir -p "$MISE_DATA_DIR/installs/dummy/1.0.0" -assert "mise ls dummy" "dummy 1.0.0 (outdated) ~/workdir/mise.toml prefix:1" +assert "mise ls dummy --outdated" "dummy 1.0.0 (outdated) ~/workdir/mise.toml prefix:1" assert "mise up -n" "Would uninstall dummy@1.0.0 Would install dummy@1.1.0" mise up diff --git a/man/man1/mise.1 b/man/man1/mise.1 index 738e1ac153..1d9ea93244 100644 --- a/man/man1/mise.1 +++ b/man/man1/mise.1 @@ -24,7 +24,7 @@ Change directory before running command Set the environment for loading `mise..toml` .TP \fB\-j\fR, \fB\-\-jobs\fR=\fIJOBS\fR -How many jobs to run in parallel [default: 4] +How many jobs to run in parallel [default: 8] .RS May also be specified with the \fBMISE_JOBS\fR environment variable. .RE diff --git a/mise.usage.kdl b/mise.usage.kdl index 1675368ad8..4ea96fa98f 100644 --- a/mise.usage.kdl +++ b/mise.usage.kdl @@ -22,7 +22,7 @@ flag "-E --env" help="Set the environment for loading `mise..toml`" var=tru } flag "-f --force" help="Force the operation" hide=true flag "-i --interleave" help="Set the log output verbosity" hide=true -flag "-j --jobs" help="How many jobs to run in parallel [default: 4]" global=true { +flag "-j --jobs" help="How many jobs to run in parallel [default: 8]" global=true { arg "" } flag "--log-level" hide=true global=true { @@ -685,6 +685,7 @@ It's a useful command to get the current state of your tools."# flag "-g --global" help="Only show tool versions currently specified in the global mise.toml" flag "-i --installed" help="Only show tool versions that are installed (Hides tools defined in mise.toml but not installed)" flag "-o --offline" help="Don't fetch information such as outdated versions" + flag "--outdated" help="Display whether a version is outdated" flag "-J --json" help="Output in JSON format" flag "-m --missing" help="Display missing tool versions" flag "--prefix" help="Display versions matching this prefix" { diff --git a/schema/mise.json b/schema/mise.json index 01104004e0..937db82a1c 100644 --- a/schema/mise.json +++ b/schema/mise.json @@ -270,7 +270,7 @@ "type": "string" }, "fetch_remote_versions_timeout": { - "default": "10s", + "default": "5s", "description": "Timeout in seconds for HTTP requests to fetch new tool versions in mise.", "type": "string" }, @@ -338,7 +338,7 @@ } }, "jobs": { - "default": 4, + "default": 8, "description": "How many jobs to run concurrently such as tool installs.", "type": "number" }, diff --git a/settings.toml b/settings.toml index d6f1f3d7ef..be43a9e96e 100644 --- a/settings.toml +++ b/settings.toml @@ -295,7 +295,7 @@ cached. For "slow" commands like `mise ls-remote` or `mise install`: [fetch_remote_versions_timeout] env = "MISE_FETCH_REMOTE_VERSIONS_TIMEOUT" type = "Duration" -default = "10s" +default = "5s" description = "Timeout in seconds for HTTP requests to fetch new tool versions in mise." aliases = ["fetch_remote_version_timeout"] @@ -391,7 +391,7 @@ description = "This is a list of config paths that mise will ignore." env = "MISE_JOBS" type = "Integer" rust_type = "usize" -default = 4 +default = 8 description = "How many jobs to run concurrently such as tool installs." [legacy_version_file] diff --git a/src/backend/asdf.rs b/src/backend/asdf.rs index f07501a4de..2c519544c5 100644 --- a/src/backend/asdf.rs +++ b/src/backend/asdf.rs @@ -17,6 +17,7 @@ use crate::plugins::asdf_plugin::AsdfPlugin; use crate::plugins::mise_plugin_toml::MisePluginToml; use crate::plugins::Script::{Download, ExecEnv, Install, ParseIdiomaticFile}; use crate::plugins::{Plugin, PluginType, Script, ScriptManager}; +use crate::timeout::run_with_timeout; use crate::toolset::{ToolRequest, ToolVersion, Toolset}; use crate::ui::progress_report::SingleReport; use crate::{dirs, env, file}; @@ -236,18 +237,23 @@ impl Backend for AsdfBackend { } fn latest_stable_version(&self) -> Result> { - if !self.plugin.has_latest_stable_script() { - return self.latest_version(Some("latest".into())); - } - self.latest_stable_cache - .get_or_try_init(|| self.plugin.fetch_latest_stable()) - .wrap_err_with(|| { - eyre!( - "Failed fetching latest stable version for plugin {}", - style(&self.name).blue().for_stderr(), - ) - }) - .cloned() + run_with_timeout( + || { + if !self.plugin.has_latest_stable_script() { + return self.latest_version(Some("latest".into())); + } + self.latest_stable_cache + .get_or_try_init(|| self.plugin.fetch_latest_stable()) + .wrap_err_with(|| { + eyre!( + "Failed fetching latest stable version for plugin {}", + style(&self.name).blue().for_stderr(), + ) + }) + .cloned() + }, + SETTINGS.fetch_remote_versions_timeout(), + ) } fn get_aliases(&self) -> Result> { diff --git a/src/backend/go.rs b/src/backend/go.rs index 4d5fa6ea83..f2d5ef4fe8 100644 --- a/src/backend/go.rs +++ b/src/backend/go.rs @@ -6,6 +6,7 @@ use crate::cli::args::BackendArg; use crate::cmd::CmdLineRunner; use crate::config::SETTINGS; use crate::install_context::InstallContext; +use crate::timeout; use crate::toolset::ToolVersion; #[derive(Debug)] @@ -27,29 +28,34 @@ impl Backend for GoBackend { } fn _list_remote_versions(&self) -> eyre::Result> { - let mut mod_path = Some(self.tool_name()); - - while let Some(cur_mod_path) = mod_path { - let res = cmd!("go", "list", "-m", "-versions", "-json", &cur_mod_path) - .full_env(self.dependency_env()?) - .read(); - if let Ok(raw) = res { - let res = serde_json::from_str::(&raw); - if let Ok(mut mod_info) = res { - // remove the leading v from the versions - mod_info.versions = mod_info - .versions - .into_iter() - .map(|v| v.trim_start_matches('v').to_string()) - .collect(); - return Ok(mod_info.versions); + timeout::run_with_timeout( + || { + let mut mod_path = Some(self.tool_name()); + + while let Some(cur_mod_path) = mod_path { + let res = cmd!("go", "list", "-m", "-versions", "-json", &cur_mod_path) + .full_env(self.dependency_env()?) + .read(); + if let Ok(raw) = res { + let res = serde_json::from_str::(&raw); + if let Ok(mut mod_info) = res { + // remove the leading v from the versions + mod_info.versions = mod_info + .versions + .into_iter() + .map(|v| v.trim_start_matches('v').to_string()) + .collect(); + return Ok(mod_info.versions); + } + }; + + mod_path = trim_after_last_slash(cur_mod_path); } - }; - mod_path = trim_after_last_slash(cur_mod_path); - } - - Ok(vec![]) + Ok(vec![]) + }, + SETTINGS.fetch_remote_versions_timeout(), + ) } fn install_version_(&self, ctx: &InstallContext, tv: ToolVersion) -> eyre::Result { diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 7af1687ab6..e233b802ab 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -272,7 +272,7 @@ pub trait Backend: Debug + Send + Sync { let latest = match tv.latest_version() { Ok(latest) => latest, Err(e) => { - debug!( + warn!( "Error getting latest version for {}: {:#}", self.ba().to_string(), e diff --git a/src/backend/npm.rs b/src/backend/npm.rs index 5e2bd641d6..d12cf57768 100644 --- a/src/backend/npm.rs +++ b/src/backend/npm.rs @@ -1,6 +1,3 @@ -use serde_json::Value; -use std::fmt::Debug; - use crate::backend::backend_type::BackendType; use crate::backend::Backend; use crate::cache::{CacheManager, CacheManagerBuilder}; @@ -8,7 +5,10 @@ use crate::cli::args::BackendArg; use crate::cmd::CmdLineRunner; use crate::config::{Config, SETTINGS}; use crate::install_context::InstallContext; +use crate::timeout; use crate::toolset::ToolVersion; +use serde_json::Value; +use std::fmt::Debug; #[derive(Debug)] pub struct NPMBackend { @@ -32,27 +32,38 @@ impl Backend for NPMBackend { } fn _list_remote_versions(&self) -> eyre::Result> { - let raw = cmd!(NPM_PROGRAM, "view", self.tool_name(), "versions", "--json") - .full_env(self.dependency_env()?) - .read()?; - let versions: Vec = serde_json::from_str(&raw)?; - Ok(versions) + timeout::run_with_timeout( + || { + let raw = cmd!(NPM_PROGRAM, "view", self.tool_name(), "versions", "--json") + .full_env(self.dependency_env()?) + .read()?; + let versions: Vec = serde_json::from_str(&raw)?; + Ok(versions) + }, + SETTINGS.fetch_remote_versions_timeout(), + ) } fn latest_stable_version(&self) -> eyre::Result> { - self.latest_version_cache - .get_or_try_init(|| { - let raw = cmd!(NPM_PROGRAM, "view", self.tool_name(), "dist-tags", "--json") - .full_env(self.dependency_env()?) - .read()?; - let dist_tags: Value = serde_json::from_str(&raw)?; - let latest = match dist_tags["latest"] { - Value::String(ref s) => Some(s.clone()), - _ => self.latest_version(Some("latest".into())).unwrap(), - }; - Ok(latest) - }) - .cloned() + timeout::run_with_timeout( + || { + self.latest_version_cache + .get_or_try_init(|| { + let raw = + cmd!(NPM_PROGRAM, "view", self.tool_name(), "dist-tags", "--json") + .full_env(self.dependency_env()?) + .read()?; + let dist_tags: Value = serde_json::from_str(&raw)?; + let latest = match dist_tags["latest"] { + Value::String(ref s) => Some(s.clone()), + _ => self.latest_version(Some("latest".into())).unwrap(), + }; + Ok(latest) + }) + .cloned() + }, + SETTINGS.fetch_remote_versions_timeout(), + ) } fn install_version_(&self, ctx: &InstallContext, tv: ToolVersion) -> eyre::Result { diff --git a/src/backend/vfox.rs b/src/backend/vfox.rs index fdc5f6710e..ade8af2b25 100644 --- a/src/backend/vfox.rs +++ b/src/backend/vfox.rs @@ -1,4 +1,4 @@ -use crate::env; +use crate::{env, timeout}; use heck::ToKebabCase; use std::collections::{BTreeMap, HashMap}; use std::fmt::Debug; @@ -10,7 +10,7 @@ use crate::backend::backend_type::BackendType; use crate::backend::Backend; use crate::cache::{CacheManager, CacheManagerBuilder}; use crate::cli::args::BackendArg; -use crate::config::Config; +use crate::config::{Config, SETTINGS}; use crate::dirs; use crate::install_context::InstallContext; use crate::plugins::vfox_plugin::VfoxPlugin; @@ -41,14 +41,19 @@ impl Backend for VfoxBackend { } fn _list_remote_versions(&self) -> eyre::Result> { - let (vfox, _log_rx) = self.plugin.vfox(); - self.ensure_plugin_installed()?; - let versions = RUNTIME.block_on(vfox.list_available_versions(&self.pathname))?; - Ok(versions - .into_iter() - .rev() - .map(|v| v.version) - .collect::>()) + timeout::run_with_timeout( + || { + let (vfox, _log_rx) = self.plugin.vfox(); + self.ensure_plugin_installed()?; + let versions = RUNTIME.block_on(vfox.list_available_versions(&self.pathname))?; + Ok(versions + .into_iter() + .rev() + .map(|v| v.version) + .collect::>()) + }, + SETTINGS.fetch_remote_versions_timeout(), + ) } fn install_version_( diff --git a/src/cli/ls.rs b/src/cli/ls.rs index 79daaf9f15..8f5de48a0d 100644 --- a/src/cli/ls.rs +++ b/src/cli/ls.rs @@ -52,6 +52,10 @@ pub struct Ls { #[clap(long, short)] offline: bool, + /// Display whether a version is outdated + #[clap(long)] + outdated: bool, + /// Output in JSON format #[clap(long, short = 'J')] json: bool, @@ -291,10 +295,10 @@ impl From<(&Ls, &dyn Backend, &ToolVersion, &ToolSource)> for VersionStatus { } else if !p.is_version_installed(tv, true) { VersionStatus::Missing(tv.version.clone()) } else if !source.is_unknown() { - let outdated = if ls.offline { - false - } else { + let outdated = if ls.outdated { p.is_version_outdated(tv) + } else { + false }; VersionStatus::Active(tv.version.clone(), outdated) } else { diff --git a/src/cli/mod.rs b/src/cli/mod.rs index dca73523ed..38b5557c7b 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -103,7 +103,7 @@ pub struct Cli { /// Set the log output verbosity #[clap(long, short, hide = true, overrides_with = "prefix")] pub interleave: bool, - /// How many jobs to run in parallel [default: 4] + /// How many jobs to run in parallel [default: 8] #[clap(long, short, global = true, env = "MISE_JOBS")] pub jobs: Option, #[clap(long, global = true, hide = true, value_name = "LEVEL", value_enum)] diff --git a/src/plugins/core/ruby.rs b/src/plugins/core/ruby.rs index accbc9da37..b49d643167 100644 --- a/src/plugins/core/ruby.rs +++ b/src/plugins/core/ruby.rs @@ -15,7 +15,7 @@ use crate::install_context::InstallContext; use crate::lock_file::LockFile; use crate::toolset::{ToolVersion, Toolset}; use crate::ui::progress_report::SingleReport; -use crate::{cmd, env, file, plugins}; +use crate::{cmd, env, file, plugins, timeout}; use eyre::{Result, WrapErr}; use itertools::Itertools; use xx::regex; @@ -334,20 +334,25 @@ impl Backend for RubyPlugin { &self.ba } fn _list_remote_versions(&self) -> Result> { - if let Err(err) = self.update_build_tool(None) { - warn!("{err}"); - } - let ruby_build_bin = self.ruby_build_bin(); - let versions = plugins::core::run_fetch_task_with_timeout(move || { - let output = cmd!(ruby_build_bin, "--definitions").read()?; - let versions = output - .split('\n') - .sorted_by_cached_key(|s| regex!(r#"^\d"#).is_match(s)) // show matz ruby first - .map(|s| s.to_string()) - .collect(); - Ok(versions) - })?; - Ok(versions) + timeout::run_with_timeout( + || { + if let Err(err) = self.update_build_tool(None) { + warn!("{err}"); + } + let ruby_build_bin = self.ruby_build_bin(); + let versions = plugins::core::run_fetch_task_with_timeout(move || { + let output = cmd!(ruby_build_bin, "--definitions").read()?; + let versions = output + .split('\n') + .sorted_by_cached_key(|s| regex!(r#"^\d"#).is_match(s)) // show matz ruby first + .map(|s| s.to_string()) + .collect(); + Ok(versions) + })?; + Ok(versions) + }, + SETTINGS.fetch_remote_versions_timeout(), + ) } fn idiomatic_filenames(&self) -> Result> { diff --git a/xtasks/fig/src/mise.ts b/xtasks/fig/src/mise.ts index 7f4506c13d..80b10916c0 100644 --- a/xtasks/fig/src/mise.ts +++ b/xtasks/fig/src/mise.ts @@ -1334,6 +1334,13 @@ const completionSpec: Fig.Spec = { "description": "Don't fetch information such as outdated versions", "isRepeatable": false }, + { + "name": [ + "--outdated" + ], + "description": "Display whether a version is outdated", + "isRepeatable": false + }, { "name": [ "-J", @@ -3164,7 +3171,7 @@ const completionSpec: Fig.Spec = { "-j", "--jobs" ], - "description": "How many jobs to run in parallel [default: 4]", + "description": "How many jobs to run in parallel [default: 8]", "isRepeatable": false, "args": { "name": "jobs",