From 8a71ad808e53f74b90f3ebe146ae63193509f0a8 Mon Sep 17 00:00:00 2001 From: jdx <216188+jdx@users.noreply.github.com> Date: Thu, 19 Dec 2024 10:39:12 -0600 Subject: [PATCH 1/2] feat: `mise install-into` Fixes #1039 --- docs/cli/index.md | 1 + docs/cli/install-into.md | 26 ++++++++++++++++ e2e/cli/test_install_into | 4 +++ man/man1/mise.1 | 3 ++ mise.usage.kdl | 13 ++++++++ src/backend/mod.rs | 5 +++- src/cli/install_into.rs | 60 +++++++++++++++++++++++++++++++++++++ src/cli/mod.rs | 3 ++ src/toolset/tool_version.rs | 5 ++++ xtasks/fig/src/mise.ts | 22 ++++++++++++++ 10 files changed, 141 insertions(+), 1 deletion(-) create mode 100644 docs/cli/install-into.md create mode 100644 e2e/cli/test_install_into create mode 100644 src/cli/install_into.rs diff --git a/docs/cli/index.md b/docs/cli/index.md index bb6aae3663..4fb51407ce 100644 --- a/docs/cli/index.md +++ b/docs/cli/index.md @@ -90,6 +90,7 @@ Can also use `MISE_NO_CONFIG=1` - [`mise generate task-docs [FLAGS]`](/cli/generate/task-docs.md) - [`mise implode [--config] [-n --dry-run]`](/cli/implode.md) - [`mise install [FLAGS] [TOOL@VERSION]...`](/cli/install.md) +- [`mise install-into `](/cli/install-into.md) - [`mise latest [-i --installed] `](/cli/latest.md) - [`mise link [-f --force] `](/cli/link.md) - [`mise ls [FLAGS] [PLUGIN]...`](/cli/ls.md) diff --git a/docs/cli/install-into.md b/docs/cli/install-into.md new file mode 100644 index 0000000000..118d170cd4 --- /dev/null +++ b/docs/cli/install-into.md @@ -0,0 +1,26 @@ +# `mise install-into` + +- **Usage**: `mise install-into ` +- **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 + +Used for building a tool to a directory for use outside of mise + +## Arguments + +### `` + +Tool to install e.g.: node@20 + +### `` + +Path to install the tool into + +Examples: + +``` +# install node@20.0.0 into ./mynode +$ mise install-into node@20.0.0 ./mynode && ./mynode/bin/node -v +20.0.0 +``` diff --git a/e2e/cli/test_install_into b/e2e/cli/test_install_into new file mode 100644 index 0000000000..e4d96e2fd9 --- /dev/null +++ b/e2e/cli/test_install_into @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +assert "mise install-into node@22.0.0 ./mynode" +assert "./mynode/bin/node -v" "v22.0.0" diff --git a/man/man1/mise.1 b/man/man1/mise.1 index 3ff874ed08..9eadeadae9 100644 --- a/man/man1/mise.1 +++ b/man/man1/mise.1 @@ -109,6 +109,9 @@ Removes mise CLI and all related data mise\-install(1) Install a tool version .TP +mise\-install\-into(1) +Install a tool version to a specific path +.TP mise\-latest(1) Gets the latest available version for a plugin .TP diff --git a/mise.usage.kdl b/mise.usage.kdl index 54fa2be6df..42b3432884 100644 --- a/mise.usage.kdl +++ b/mise.usage.kdl @@ -632,6 +632,19 @@ Tools will be installed in parallel. To disable, set `--jobs=1` or `MISE_JOBS=1` } 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 r"Install a tool version to a specific path + +Used for building a tool to a directory for use outside of mise" + after_long_help r"Examples: + + # install node@20.0.0 into ./mynode + $ mise install-into node@20.0.0 ./mynode && ./mynode/bin/node -v + 20.0.0 +" + arg "" help="Tool to install e.g.: node@20" + arg "" help="Path to install the tool into" +} cmd "latest" help="Gets the latest available version for a plugin" { long_help r"Gets the latest available version for a plugin diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 7a2ecec9af..44bd5bcd6f 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -417,7 +417,10 @@ pub trait Backend: Debug + Send + Sync { } }; - install_state::write_backend_meta(self.ba())?; + if tv.install_path().starts_with(&*dirs::INSTALLS) { + // this will be false only for `install-into` + install_state::write_backend_meta(self.ba())?; + } self.cleanup_install_dirs(&tv); // attempt to touch all the .tool-version files to trigger updates in hook-env diff --git a/src/cli/install_into.rs b/src/cli/install_into.rs new file mode 100644 index 0000000000..826ef8cdb3 --- /dev/null +++ b/src/cli/install_into.rs @@ -0,0 +1,60 @@ +use crate::cli::args::ToolArg; +use crate::config::Config; +use crate::install_context::InstallContext; +use crate::toolset::ToolsetBuilder; +use crate::ui::multi_progress_report::MultiProgressReport; +use clap::ValueHint; +use eyre::{eyre, Result}; +use std::path::PathBuf; + +/// Install a tool version to a specific path +/// +/// Used for building a tool to a directory for use outside of mise +#[derive(Debug, clap::Args)] +#[clap(verbatim_doc_comment, after_long_help = AFTER_LONG_HELP)] +pub struct InstallInto { + /// Tool to install + /// e.g.: node@20 + #[clap(value_name = "TOOL@VERSION")] + tool: ToolArg, + + /// Path to install the tool into + #[clap(value_hint = ValueHint::DirPath)] + path: PathBuf, +} + +impl InstallInto { + pub fn run(self) -> Result<()> { + let config = Config::get(); + let ts = ToolsetBuilder::new() + .with_args(&[self.tool.clone()]) + .build(&config)?; + let mut tv = ts + .versions + .get(&self.tool.ba) + .ok_or_else(|| eyre!("Tool not found"))? + .versions + .first() + .unwrap() + .clone(); + let backend = tv.backend()?; + let mpr = MultiProgressReport::get(); + let install_ctx = InstallContext { + ts: &ts, + pr: mpr.add(&tv.style()), + force: true, + }; + tv.install_path = Some(self.path.clone()); + backend.install_version(install_ctx, tv)?; + Ok(()) + } +} + +static AFTER_LONG_HELP: &str = color_print::cstr!( + r#"Examples: + + # install node@20.0.0 into ./mynode + $ mise install-into node@20.0.0 ./mynode && ./mynode/bin/node -v + 20.0.0 +"# +); diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 1d2d8caf17..5f1e84084a 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -32,6 +32,7 @@ mod hook_env; mod hook_not_found; mod implode; mod install; +mod install_into; mod latest; mod link; mod local; @@ -204,6 +205,7 @@ pub enum Commands { HookNotFound(hook_not_found::HookNotFound), Implode(implode::Implode), Install(install::Install), + InstallInto(install_into::InstallInto), Latest(latest::Latest), Link(link::Link), Local(local::Local), @@ -267,6 +269,7 @@ impl Commands { Self::HookNotFound(cmd) => cmd.run(), Self::Implode(cmd) => cmd.run(), Self::Install(cmd) => cmd.run(), + Self::InstallInto(cmd) => cmd.run(), Self::Latest(cmd) => cmd.run(), Self::Link(cmd) => cmd.run(), Self::Local(cmd) => cmd.run(), diff --git a/src/toolset/tool_version.rs b/src/toolset/tool_version.rs index 869810896c..6b3760df8a 100644 --- a/src/toolset/tool_version.rs +++ b/src/toolset/tool_version.rs @@ -23,6 +23,7 @@ pub struct ToolVersion { pub request: ToolRequest, pub version: String, pub checksums: BTreeMap, + pub install_path: Option, } impl ToolVersion { @@ -31,6 +32,7 @@ impl ToolVersion { request, version, checksums: Default::default(), + install_path: None, } } @@ -77,6 +79,9 @@ impl ToolVersion { } pub fn install_path(&self) -> PathBuf { + if let Some(p) = &self.install_path { + return p.clone(); + } let pathname = match &self.request { ToolRequest::Path { path: p, .. } => p.to_string_lossy().to_string(), _ => self.tv_pathname(), diff --git a/xtasks/fig/src/mise.ts b/xtasks/fig/src/mise.ts index c9004810fb..ecfef56e0c 100644 --- a/xtasks/fig/src/mise.ts +++ b/xtasks/fig/src/mise.ts @@ -1324,6 +1324,28 @@ const completionSpec: Fig.Spec = { } ] }, + { + "name": [ + "install-into" + ], + "description": "Install a tool version to a specific path", + "args": [ + { + "name": "tool@version", + "description": "Tool to install e.g.: node@20", + "isOptional": false, + "isVariadic": false, + "generators": toolVersionGenerator + }, + { + "name": "path", + "description": "Path to install the tool into", + "isOptional": false, + "isVariadic": false, + "template": "filepaths" + } + ] + }, { "name": [ "latest" From 24e4bfd4cdecc642bed7e0924dd269e34b4d589f Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Thu, 19 Dec 2024 16:52:36 +0000 Subject: [PATCH 2/2] [autofix.ci] apply automated fixes --- docs/.vitepress/cli_commands.ts | 3 +++ src/backend/mod.rs | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/.vitepress/cli_commands.ts b/docs/.vitepress/cli_commands.ts index fd0fcf80be..28a0294949 100644 --- a/docs/.vitepress/cli_commands.ts +++ b/docs/.vitepress/cli_commands.ts @@ -145,6 +145,9 @@ export const commands: { [key: string]: Command } = { install: { hide: false, }, + "install-into": { + hide: false, + }, latest: { hide: false, }, diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 44bd5bcd6f..5dd2ae9de7 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -417,7 +417,7 @@ pub trait Backend: Debug + Send + Sync { } }; - if tv.install_path().starts_with(&*dirs::INSTALLS) { + if tv.install_path().starts_with(*dirs::INSTALLS) { // this will be false only for `install-into` install_state::write_backend_meta(self.ba())?; }