use crate::cli::args::ToolArg;
use crate::config::Config;
use crate::hooks::Hooks;
use crate::toolset::{InstallOptions, ResolveOptions, ToolRequest, ToolSource, Toolset};
use crate::ui::multi_progress_report::MultiProgressReport;
use crate::{config, hooks};
use eyre::Result;
use itertools::Itertools;
#[derive(Debug, clap::Args)]
#[clap(visible_alias = "i", verbatim_doc_comment, after_long_help = AFTER_LONG_HELP)]
pub struct Install {
#[clap(value_name = "TOOL@VERSION")]
tool: Option<Vec<ToolArg>>,
#[clap(long, short, requires = "tool")]
force: bool,
#[clap(long, short, env = "MISE_JOBS", verbatim_doc_comment)]
jobs: Option<usize>,
#[clap(long, overrides_with = "jobs")]
raw: bool,
#[clap(long, short, action = clap::ArgAction::Count)]
verbose: u8,
}
impl Install {
pub fn run(self) -> Result<()> {
let config = Config::try_get()?;
match &self.tool {
Some(runtime) => self.install_runtimes(&config, runtime)?,
None => self.install_missing_runtimes(&config)?,
};
Ok(())
}
fn install_runtimes(&self, config: &Config, runtimes: &[ToolArg]) -> Result<()> {
let mpr = MultiProgressReport::get();
let tools = runtimes.iter().map(|ta| ta.ba.short.clone()).collect();
let mut ts = config.get_tool_request_set()?.filter_by_tool(tools).into();
let tool_versions = self.get_requested_tool_versions(&ts, runtimes)?;
let versions = if tool_versions.is_empty() {
warn!("no runtimes to install");
warn!("specify a version with `mise install <PLUGIN>@<VERSION>`");
vec![]
} else {
ts.install_all_versions(tool_versions, &mpr, &self.install_opts())?
};
config::rebuild_shims_and_runtime_symlinks(&versions)?;
Ok(())
}
fn install_opts(&self) -> InstallOptions {
InstallOptions {
force: self.force,
jobs: self.jobs,
raw: self.raw,
missing_args_only: false,
resolve_options: ResolveOptions {
use_locked_version: true,
latest_versions: true,
},
..Default::default()
}
}
fn get_requested_tool_versions(
&self,
ts: &Toolset,
runtimes: &[ToolArg],
) -> Result<Vec<ToolRequest>> {
let mut requests = vec![];
for ta in ToolArg::double_tool_condition(runtimes)? {
match ta.tvr {
Some(tv) => requests.push(tv),
None => {
if ta.tvr.is_none() {
match ts.versions.get(&ta.ba) {
Some(tvl) => {
for tvr in &tvl.requests {
requests.push(tvr.clone());
}
}
None => {
let tvr = ToolRequest::Version {
backend: ta.ba.clone(),
version: "latest".into(),
options: ta.ba.opts(),
source: ToolSource::Argument,
};
requests.push(tvr);
}
}
}
}
}
}
Ok(requests)
}
fn install_missing_runtimes(&self, config: &Config) -> eyre::Result<()> {
let trs = config.get_tool_request_set()?;
let versions = trs.missing_tools().into_iter().cloned().collect_vec();
let versions = if versions.is_empty() {
info!("all runtimes are installed");
hooks::run_one_hook(config.get_toolset()?, Hooks::Postinstall, None);
vec![]
} else {
let mpr = MultiProgressReport::get();
let mut ts = Toolset::from(trs.clone());
ts.install_all_versions(versions, &mpr, &self.install_opts())?
};
config::rebuild_shims_and_runtime_symlinks(&versions)?;
Ok(())
}
}
static AFTER_LONG_HELP: &str = color_print::cstr!(
r#"<bold><underline>Examples:</underline></bold>
$ <bold>mise install node@20.0.0</bold> # install specific node version
$ <bold>mise install node@20</bold> # install fuzzy node version
$ <bold>mise install node</bold> # install version specified in mise.toml
$ <bold>mise install</bold> # installs everything specified in mise.toml
"#
);