diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4614aa140..eb16ea2d2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,6 +15,8 @@ env: CARGO_TERM_COLOR: always CARGO_INCREMENTAL: 0 MISE_TRUSTED_CONFIG_PATHS: ${{ github.workspace }} + MISE_EXPERIMENTAL: 1 + RUST_BACKTRACE: 1 permissions: pull-requests: write @@ -39,15 +41,16 @@ jobs: tool: nextest,cargo-deny,cargo-msrv,cargo-machete,usage-cli - name: Install direnv run: sudo apt-get update; sudo apt-get install direnv + - run: | + cargo build --all-features + echo "$PWD/target/debug" >> "$GITHUB_PATH" - run: cargo nextest run --all-features - env: - RUST_BACKTRACE: "1" + - run: mise run test:shuffle - run: cargo deny check - run: cargo msrv verify - run: cargo machete --with-metadata - run: ./scripts/test-standalone.sh - - run: cargo build --all-features - - run: ./target/debug/mise settings set experimental true + - run: mise settings set experimental true - uses: actions/cache@v4 with: path: | @@ -55,10 +58,10 @@ jobs: ~/.local/share/mise/plugins key: mise-tools-${{ hashFiles('.mise.toml') }} restore-keys: mise-tools - - run: ./target/debug/mise install - - run: ./target/debug/mise run render + - run: mise install + - run: mise run render - if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == 'jdx/mise' - run: ./target/debug/mise run lint-fix && git diff HEAD + run: mise run lint-fix && git diff HEAD - if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == 'jdx/mise' uses: EndBug/add-and-commit@v9 with: @@ -66,7 +69,7 @@ jobs: author_name: mise[bot] author_email: 123107610+mise-en-dev@users.noreply.github.com - uses: actions-rust-lang/rustfmt@v1 - - run: ./target/debug/mise run lint + - run: mise run lint coverage: name: coverage-${{matrix.tranche}} diff --git a/.mise.local.toml b/.mise.local.toml index 6e50f8286..4ad283a0e 100644 --- a/.mise.local.toml +++ b/.mise.local.toml @@ -8,3 +8,6 @@ tasks.c1 = { run = "echo c1 && sleep 0.5 && echo", depends = ["b1"], hide = true [env] FOO=".mise.local.toml" + +[tasks."test:shuffle"] +run = "cargo +nightly test --all-features -- -Z unstable-options --shuffle" diff --git a/Cargo.lock b/Cargo.lock index dc751b8bf..8c489d461 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -219,7 +219,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" dependencies = [ "memchr", - "regex-automata", + "regex-automata 0.4.6", "serde", ] @@ -731,6 +731,27 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "env_filter" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea" +dependencies = [ + "log", +] + +[[package]] +name = "env_logger" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b35839ba51819680ba087cd351788c9a3c476841207e0b8cee0b04722343b9" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "log", +] + [[package]] name = "envmnt" version = "0.10.4" @@ -1045,8 +1066,8 @@ dependencies = [ "aho-corasick", "bstr", "log", - "regex-automata", - "regex-syntax", + "regex-automata 0.4.6", + "regex-syntax 0.8.3", ] [[package]] @@ -1286,7 +1307,7 @@ dependencies = [ "globset", "log", "memchr", - "regex-automata", + "regex-automata 0.4.6", "same-file", "walkdir", "winapi-util", @@ -1488,6 +1509,15 @@ version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + [[package]] name = "memchr" version = "2.7.2" @@ -1626,6 +1656,7 @@ dependencies = [ "tera", "terminal_size", "test-case", + "test-log", "thiserror", "toml", "toml_edit", @@ -1685,6 +1716,16 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num-conv" version = "0.1.0" @@ -1794,6 +1835,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "owo-colors" version = "3.5.0" @@ -2108,8 +2155,17 @@ checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" dependencies = [ "aho-corasick", "memchr", - "regex-automata", - "regex-syntax", + "regex-automata 0.4.6", + "regex-syntax 0.8.3", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", ] [[package]] @@ -2120,9 +2176,15 @@ checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.8.3", ] +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + [[package]] name = "regex-syntax" version = "0.8.3" @@ -2784,6 +2846,28 @@ dependencies = [ "test-case-core", ] +[[package]] +name = "test-log" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dffced63c2b5c7be278154d76b479f9f9920ed34e7574201407f0b14e2bbb93" +dependencies = [ + "env_logger", + "test-log-macros", + "tracing-subscriber", +] + +[[package]] +name = "test-log-macros" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5999e24eaa32083191ba4e425deb75cdf25efefabe5aaccb7446dd0d4122a3f5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.63", +] + [[package]] name = "thiserror" version = "1.0.60" @@ -2980,15 +3064,32 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + [[package]] name = "tracing-subscriber" version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", "sharded-slab", "thread_local", + "tracing", "tracing-core", + "tracing-log", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 6e4f667f2..7dd243d77 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,15 +12,15 @@ license = "MIT" keywords = ["mise"] categories = ["command-line-utilities"] include = [ - "src/**/*.rs", - "src/plugins/core/assets/**", - "src/assets/**", - "/completions/*", - "/Cargo.lock", - "/LICENSE", - "/README.md", - "/build.rs", - "/zipsign.pub", + "src/**/*.rs", + "src/plugins/core/assets/**", + "src/assets/**", + "/completions/*", + "/Cargo.lock", + "/LICENSE", + "/README.md", + "/build.rs", + "/zipsign.pub", ] rust-version = "1.76.0" build = "build.rs" @@ -43,8 +43,8 @@ lto = true base64 = "0.22.1" calm_io = "0.1.1" chrono = { version = "0.4.38", default-features = false, features = [ - "std", - "clock", + "std", + "clock", ] } ci_info = "0.14.14" clap = { version = "4.5.4", features = ["env", "derive", "string"] } @@ -87,16 +87,16 @@ regex = "1.10.4" # mise error sending request for url (https://api.github.com/repos/jdx/mise/releases): error trying to connect: invalid URL, scheme is not http # mise error trying to connect: invalid URL, scheme is not http reqwest = { version = "<0.12", default-features = false, features = [ - "blocking", - "json", - "gzip", + "blocking", + "json", + "gzip", ] } rmp-serde = "1.3.0" # TODO: fix issue with rustls and "No signature verification implemented for Plain(None) files" self_update = { version = "<0.40.0", default-features = false, features = [ - "archive-tar", - "compression-flate2", - "signatures", + "archive-tar", + "compression-flate2", + "signatures", ] } serde = "1.0.199" serde_derive = "1.0.199" @@ -137,6 +137,7 @@ insta = { version = "1.38.0", features = ["filters", "json"] } predicates = "3.1.0" pretty_assertions = "1.4.0" test-case = "3.3.1" +test-log = "0.2" [features] default = ["native-tls"] diff --git a/src/cli/alias/get.rs b/src/cli/alias/get.rs index ef2768ec3..054433453 100644 --- a/src/cli/alias/get.rs +++ b/src/cli/alias/get.rs @@ -41,11 +41,11 @@ static AFTER_LONG_HELP: &str = color_print::cstr!( #[cfg(test)] mod tests { - use crate::test::reset_config; + use crate::test::reset; #[test] fn test_alias_get() { - reset_config(); + reset(); let stdout = assert_cli!("alias", "get", "tiny", "my/alias"); assert_snapshot!(stdout, @r###" 3.0 @@ -54,12 +54,14 @@ mod tests { #[test] fn test_alias_get_plugin_unknown() { + reset(); let err = assert_cli_err!("alias", "get", "unknown", "unknown"); assert_snapshot!(err, @"Unknown plugin: unknown"); } #[test] fn test_alias_get_alias_unknown() { + reset(); let err = assert_cli_err!("alias", "get", "tiny", "unknown"); assert_snapshot!(err, @"Unknown alias: unknown"); } diff --git a/src/cli/alias/ls.rs b/src/cli/alias/ls.rs index 98b52b384..cc230c017 100644 --- a/src/cli/alias/ls.rs +++ b/src/cli/alias/ls.rs @@ -70,8 +70,12 @@ static AFTER_LONG_HELP: &str = color_print::cstr!( #[cfg(test)] mod tests { + use crate::test::reset; + use test_log::test; + #[test] fn test_alias_ls() { + reset(); assert_cli_snapshot!("aliases", @r###" java lts 21 node lts 20 @@ -92,6 +96,7 @@ mod tests { #[test] fn test_alias_ls_filter() { + reset(); assert_cli_snapshot!("aliases", "ls", "tiny", @r###" tiny lts 3.1.0 tiny lts-prev 2.0.0 diff --git a/src/cli/alias/set.rs b/src/cli/alias/set.rs index b45b8de7d..a60a538b4 100644 --- a/src/cli/alias/set.rs +++ b/src/cli/alias/set.rs @@ -35,11 +35,11 @@ static AFTER_LONG_HELP: &str = color_print::cstr!( #[cfg(test)] pub mod tests { - use crate::test::reset_config; + use crate::test::reset; #[test] fn test_alias_set() { - reset_config(); + reset(); assert_cli!("alias", "set", "tiny", "my/alias", "3.0"); assert_cli_snapshot!("aliases", @r###" @@ -58,6 +58,6 @@ pub mod tests { tiny lts-prev 2.0.0 tiny my/alias 3.0 "###); - reset_config(); + reset(); } } diff --git a/src/cli/alias/unset.rs b/src/cli/alias/unset.rs index 6c385eb36..3763ccd01 100644 --- a/src/cli/alias/unset.rs +++ b/src/cli/alias/unset.rs @@ -33,11 +33,11 @@ static AFTER_LONG_HELP: &str = color_print::cstr!( #[cfg(test)] mod tests { - use crate::test::reset_config; + use crate::test::reset; #[test] fn test_alias_unset() { - reset_config(); + reset(); assert_cli!("alias", "unset", "tiny", "my/alias"); assert_cli_snapshot!("aliases", @r###" @@ -56,6 +56,6 @@ mod tests { tiny lts-prev 2.0.0 "###); - reset_config(); + reset(); } } diff --git a/src/cli/asdf.rs b/src/cli/asdf.rs index 88aeb95bf..9027281c9 100644 --- a/src/cli/asdf.rs +++ b/src/cli/asdf.rs @@ -71,8 +71,11 @@ fn list_versions(config: &Config, args: &[String]) -> Result<()> { #[cfg(test)] mod tests { + use crate::test::reset; + #[test] fn test_fake_asdf_list() { + reset(); assert_cli!("uninstall", "--all", "tiny"); assert_cli!("install", "tiny@1", "tiny@2"); assert_cli!("asdf", "install", "tiny"); @@ -85,6 +88,9 @@ mod tests { #[test] fn test_fake_asdf_other() { + reset(); + assert_cli!("uninstall", "--all", "tiny"); + assert_cli!("install", "tiny@3.1.0"); assert_cli_snapshot!("asdf", "current", "tiny", @r###" 3.1.0 "###); @@ -92,11 +98,13 @@ mod tests { #[test] fn test_fake_asdf_reshim() { + reset(); assert_cli_snapshot!("asdf", "reshim"); } #[test] fn test_fake_asdf_install() { + reset(); // on alpine this shows a warning, use assert_cli! to just get stdout assert_snapshot!(assert_cli!("asdf", "install", "tiny")); } diff --git a/src/cli/bin_paths.rs b/src/cli/bin_paths.rs index 2d6d318df..f30894fcb 100644 --- a/src/cli/bin_paths.rs +++ b/src/cli/bin_paths.rs @@ -22,8 +22,11 @@ impl BinPaths { #[cfg(test)] mod tests { + use crate::test::reset; + #[test] fn test_bin_paths() { + reset(); assert_cli!("i"); assert_cli_snapshot!("bin-paths", @r###" ~/data/installs/tiny/3/bin diff --git a/src/cli/config/ls.rs b/src/cli/config/ls.rs index 8802dbdb8..4114d82df 100644 --- a/src/cli/config/ls.rs +++ b/src/cli/config/ls.rs @@ -72,8 +72,11 @@ static AFTER_LONG_HELP: &str = color_print::cstr!( #[cfg(test)] mod tests { + use crate::test::reset; + #[test] fn test_config_ls() { + reset(); assert_cli_snapshot!("cfg", "--no-headers", @r###" ~/cwd/.test-tool-versions tiny ~/.test-tool-versions tiny, dummy diff --git a/src/cli/current.rs b/src/cli/current.rs index de44a0613..17c55da63 100644 --- a/src/cli/current.rs +++ b/src/cli/current.rs @@ -114,10 +114,13 @@ static AFTER_LONG_HELP: &str = color_print::cstr!( #[cfg(test)] mod tests { + use crate::test::reset; use std::env; + use test_log::test; #[test] fn test_current() { + reset(); assert_cli_snapshot!("current", @r###" tiny 3.1.0 dummy ref:master @@ -126,12 +129,14 @@ mod tests { #[test] fn test_current_with_runtimes() { + reset(); assert_cli_snapshot!("current", "tiny", @"3.1.0"); } #[test] fn test_current_missing() { - assert_cli!("uninstall", "dummy@1.0.1"); + reset(); + assert_cli!("uninstall", "--all", "dummy"); env::set_var("MISE_DUMMY_VERSION", "1.1.0"); assert_cli_snapshot!("current", @r###" diff --git a/src/cli/deactivate.rs b/src/cli/deactivate.rs index 27b5c2312..ba3209082 100644 --- a/src/cli/deactivate.rs +++ b/src/cli/deactivate.rs @@ -51,9 +51,11 @@ static AFTER_LONG_HELP: &str = color_print::cstr!( mod tests { use crate::config::Config; use crate::env; + use crate::test::reset; #[test] fn test_deactivate() { + reset(); let _config = Config::try_get().unwrap(); // hack: prevents error parsing __MISE_DIFF let err = assert_cli_err!("deactivate"); assert_snapshot!(err); diff --git a/src/cli/direnv/envrc.rs b/src/cli/direnv/envrc.rs index 61b5eecae..67c50e4c0 100644 --- a/src/cli/direnv/envrc.rs +++ b/src/cli/direnv/envrc.rs @@ -56,11 +56,12 @@ impl Envrc { #[cfg(test)] mod tests { + use crate::test::reset; use crate::{dirs, file}; #[test] fn test_direnv_envrc() { - assert_cli!("install"); + reset(); let stdout = assert_cli!("direnv", "envrc"); let envrc = file::read_to_string(stdout.trim()).unwrap(); let envrc = envrc.replace(dirs::HOME.to_string_lossy().as_ref(), "~"); diff --git a/src/cli/direnv/exec.rs b/src/cli/direnv/exec.rs index d8fb71427..22c9fd20f 100644 --- a/src/cli/direnv/exec.rs +++ b/src/cli/direnv/exec.rs @@ -51,9 +51,11 @@ fn env_cmd() -> Expression { #[cfg(test)] mod tests { use crate::cli::tests::grep; + use crate::test::reset; #[test] fn test_direnv_exec() { + reset(); let stdout = assert_cli!("direnv", "exec"); assert_str_eq!(grep(stdout, "JDXCODE_TINY="), "JDXCODE_TINY=3.1.0"); } diff --git a/src/cli/env.rs b/src/cli/env.rs index 576bff583..bec2fdbf1 100644 --- a/src/cli/env.rs +++ b/src/cli/env.rs @@ -73,9 +73,11 @@ mod tests { use crate::cli::tests::grep; use crate::dirs; + use crate::test::reset; #[test] fn test_env() { + reset(); let stdout = assert_cli!("env", "-s", "bash"); assert!(stdout.contains( dirs::DATA @@ -87,6 +89,7 @@ mod tests { #[test] fn test_env_with_runtime_arg() { + reset(); assert_cli!("install", "tiny@3.0"); let stdout = assert_cli!("env", "tiny@3.0", "-s", "bash"); @@ -100,6 +103,7 @@ mod tests { #[test] fn test_env_alias() { + reset(); assert_cli!("plugin", "add", "tiny"); assert_cli!("install", "tiny@my/alias"); let stdout = assert_cli!("env", "tiny@my/alias", "-s", "bash"); @@ -113,12 +117,14 @@ mod tests { #[test] fn test_env_tiny() { + reset(); let stdout = assert_cli!("env", "tiny@2", "tiny@1", "tiny@3", "-s", "bash"); assert_str_eq!(grep(stdout, "JDXCODE"), "export JDXCODE_TINY=2.1.0"); } #[test] fn test_env_default_shell() { + reset(); env::set_var("SHELL", ""); let stdout = assert_cli!("env"); assert!(stdout.contains("export PATH=")); @@ -126,6 +132,7 @@ mod tests { #[test] fn test_env_json() { + reset(); assert_cli_snapshot!("env", "-J"); } } diff --git a/src/cli/exec.rs b/src/cli/exec.rs index 1ec63639a..f9f906731 100644 --- a/src/cli/exec.rs +++ b/src/cli/exec.rs @@ -139,21 +139,26 @@ static AFTER_LONG_HELP: &str = color_print::cstr!( #[cfg(test)] mod tests { + use crate::test::reset; use std::env; + use test_log::test; #[test] fn test_exec_ok() { + reset(); assert_cli!("exec", "--", "echo"); } #[test] fn test_exec_fail() { + reset(); let err = assert_cli_err!("exec", "--", "exit", "1"); assert_snapshot!(err); } #[test] fn test_exec_cd() { + reset(); let cwd = env::current_dir().unwrap(); assert_cli!("exec", "-C", "/tmp", "--", "pwd"); env::set_current_dir(cwd).unwrap(); diff --git a/src/cli/global.rs b/src/cli/global.rs index 5c274cd31..a98d286fb 100644 --- a/src/cli/global.rs +++ b/src/cli/global.rs @@ -76,10 +76,12 @@ static AFTER_LONG_HELP: &str = color_print::cstr!( #[cfg(test)] mod tests { + use crate::test::reset; use crate::{dirs, file}; #[test] fn test_global() { + reset(); let cf_path = dirs::HOME.join(".test-tool-versions"); let orig = file::read_to_string(&cf_path).ok(); let _ = file::remove_file(&cf_path); diff --git a/src/cli/hook_env.rs b/src/cli/hook_env.rs index a1b61b945..a91bdb910 100644 --- a/src/cli/hook_env.rs +++ b/src/cli/hook_env.rs @@ -182,8 +182,12 @@ fn patch_to_status(patch: EnvDiffOperation) -> String { #[cfg(test)] mod tests { + use crate::test::reset; + use test_log::test; + #[test] fn test_hook_env() { + reset(); assert_cli!("hook-env", "--status", "-s", "fish"); } } diff --git a/src/cli/install.rs b/src/cli/install.rs index 0511e3e41..7758e2558 100644 --- a/src/cli/install.rs +++ b/src/cli/install.rs @@ -143,25 +143,30 @@ static AFTER_LONG_HELP: &str = color_print::cstr!( #[cfg(test)] mod tests { use crate::dirs; + use crate::test::reset; #[test] fn test_install_force() { + reset(); assert_cli!("install", "-f", "tiny"); } #[test] fn test_install_asdf_style() { + reset(); assert_cli!("install", "tiny", "2"); } #[test] fn test_install_with_alias() { + reset(); assert_cli!("install", "-f", "tiny@my/alias"); assert_cli_snapshot!("where", "tiny@my/alias"); } #[test] fn test_install_ref() { + reset(); assert_cli!("install", "-f", "dummy@ref:master"); assert_cli!("global", "dummy@ref:master"); let output = assert_cli!("where", "dummy"); @@ -174,6 +179,7 @@ mod tests { #[test] fn test_install_nothing() { + reset(); // this doesn't do anything since dummy isn't specified assert_cli_snapshot!("install", "dummy"); } diff --git a/src/cli/link.rs b/src/cli/link.rs index 7f2b7a8d8..8304d4680 100644 --- a/src/cli/link.rs +++ b/src/cli/link.rs @@ -80,9 +80,14 @@ static AFTER_LONG_HELP: &str = color_print::cstr!( #[cfg(test)] mod tests { use crate::file::create_dir_all; + use crate::test::reset; + use test_log::test; #[test] fn test_link() { + reset(); + assert_cli!("install", "tiny@1.0.1", "tiny@2.1.0"); + assert_cli!("install", "tiny@3.0.1", "tiny@3.1.0"); create_dir_all("../data/tmp/tiny").unwrap(); assert_cli!("link", "tiny@9.8.7", "../data/tmp/tiny"); assert_cli_snapshot!("ls", "tiny", @r###" diff --git a/src/cli/local.rs b/src/cli/local.rs index 663444838..1631512fe 100644 --- a/src/cli/local.rs +++ b/src/cli/local.rs @@ -161,8 +161,8 @@ mod tests { use std::panic; use crate::cli::tests::grep; - use crate::test::reset_config; - use crate::{dirs, env, file}; + use crate::test::reset; + use crate::{dirs, forge}; #[test] fn test_local_remove() { @@ -223,6 +223,8 @@ mod tests { run_test(|| { assert_cli_snapshot!("local", "--path"); assert_cli_snapshot!("local", "tiny@2", "tiny@1", "tiny@3"); + assert_cli!("install"); + forge::reset(); assert_cli_snapshot!("bin-paths"); }); } @@ -304,15 +306,9 @@ mod tests { where T: FnOnce() + panic::UnwindSafe, { - let _ = file::remove_file(env::current_dir().unwrap().join(".test.mise.toml")); - let cf_path = env::current_dir().unwrap().join(".test-tool-versions"); - let orig = file::read_to_string(&cf_path).unwrap(); - + reset(); + assert_cli!("install"); let result = panic::catch_unwind(test); - - file::write(cf_path, orig).unwrap(); - assert!(result.is_ok()); - reset_config(); } } diff --git a/src/cli/ls.rs b/src/cli/ls.rs index bae5b67bd..0c127ce1e 100644 --- a/src/cli/ls.rs +++ b/src/cli/ls.rs @@ -383,9 +383,11 @@ static AFTER_LONG_HELP: &str = color_print::cstr!( mod tests { use crate::dirs; use crate::file::remove_all; + use crate::test::reset; #[test] fn test_ls() { + reset(); let _ = remove_all(*dirs::INSTALLS); assert_cli!("install"); assert_cli_snapshot!("list", @r###" @@ -422,6 +424,7 @@ mod tests { #[test] fn test_ls_current() { + reset(); assert_cli_snapshot!("ls", "-c", @r###" dummy ref:master ~/.test-tool-versions ref:master tiny 3.1.0 ~/cwd/.test-tool-versions 3 @@ -430,6 +433,7 @@ mod tests { #[test] fn test_ls_json() { + reset(); let _ = remove_all(*dirs::INSTALLS); assert_cli!("install"); assert_cli_snapshot!("ls", "--json"); @@ -438,6 +442,7 @@ mod tests { #[test] fn test_ls_parseable() { + reset(); let _ = remove_all(*dirs::INSTALLS); assert_cli!("install"); assert_cli_snapshot!("ls", "--parseable", @r###" @@ -455,18 +460,21 @@ mod tests { #[test] fn test_ls_missing() { + reset(); assert_cli!("install"); assert_cli_snapshot!("ls", "--missing", @""); } #[test] fn test_ls_missing_plugin() { + reset(); let err = assert_cli_err!("ls", "missing-plugin"); assert_str_eq!(err.to_string(), r#"missing-plugin is not installed"#); } #[test] fn test_ls_prefix() { + reset(); assert_cli!("install"); assert_cli_snapshot!("ls", "--plugin=tiny", "--prefix=3", @"tiny 3.1.0 ~/cwd/.test-tool-versions 3"); } diff --git a/src/cli/outdated.rs b/src/cli/outdated.rs index 2afce7d0d..0c724714b 100644 --- a/src/cli/outdated.rs +++ b/src/cli/outdated.rs @@ -147,20 +147,25 @@ static AFTER_LONG_HELP: &str = color_print::cstr!( #[cfg(test)] mod tests { - use crate::test::change_installed_version; + use crate::test::{change_installed_version, reset}; #[test] - fn test_current() { + fn test_outdated() { + reset(); + assert_cli!("prune"); + assert_cli!("install"); assert_cli_snapshot!("outdated"); } #[test] - fn test_current_with_runtimes() { + fn test_outdated_with_runtimes() { + reset(); assert_cli_snapshot!("outdated", "tiny"); } #[test] - fn test_current_json() { + fn test_outdated_json() { + reset(); change_installed_version("tiny", "3.1.0", "3.0.0"); assert_cli_snapshot!("outdated", "tiny", "--json"); change_installed_version("tiny", "3.0.0", "3.1.0"); diff --git a/src/cli/plugins/install.rs b/src/cli/plugins/install.rs index 6d8a8c659..f07db3358 100644 --- a/src/cli/plugins/install.rs +++ b/src/cli/plugins/install.rs @@ -167,14 +167,19 @@ static AFTER_LONG_HELP: &str = color_print::cstr!( #[cfg(test)] mod tests { + use crate::test::reset; + use test_log::test; + #[test] fn test_plugin_install_invalid_url() { + reset(); let err = assert_cli_err!("plugin", "add", "tiny*"); assert_snapshot!(err, @"No repository found for plugin tiny*"); } #[test] fn test_plugin_install_core_plugin() { + reset(); let err = assert_cli_err!("plugin", "add", "node"); assert_snapshot!(err, @"node is a core plugin and does not need to be installed"); } diff --git a/src/cli/plugins/link.rs b/src/cli/plugins/link.rs index bd39379b8..9d1701997 100644 --- a/src/cli/plugins/link.rs +++ b/src/cli/plugins/link.rs @@ -79,8 +79,12 @@ static AFTER_LONG_HELP: &str = color_print::cstr!( #[cfg(test)] mod tests { + use crate::test::reset; + use test_log::test; + #[test] fn test_plugin_link() { + reset(); assert_cli_snapshot!("plugin", "link", "-f", "tiny-link", "../data/plugins/tiny", @""); assert_cli_snapshot!("plugins", "ls", @r###" dummy diff --git a/src/cli/prune.rs b/src/cli/prune.rs index d088be087..be840c35d 100644 --- a/src/cli/prune.rs +++ b/src/cli/prune.rs @@ -117,8 +117,12 @@ static AFTER_LONG_HELP: &str = color_print::cstr!( #[cfg(test)] mod tests { + use crate::test::reset; + use test_log::test; + #[test] fn test_prune() { + reset(); assert_cli!("prune", "--dry-run"); assert_cli!("prune", "tiny"); assert_cli!("prune"); diff --git a/src/cli/render_help.rs b/src/cli/render_help.rs index 2099e6cfe..396e65553 100644 --- a/src/cli/render_help.rs +++ b/src/cli/render_help.rs @@ -131,6 +131,5 @@ mod tests { assert_cli!("render-help"); let readme = fs::read_to_string("docs/cli-reference.md").unwrap(); assert!(readme.contains("# Commands")); - file::remove_file("docs/cli-reference.md").unwrap(); } } diff --git a/src/cli/run.rs b/src/cli/run.rs index d45f101de..1bc77c25e 100644 --- a/src/cli/run.rs +++ b/src/cli/run.rs @@ -586,9 +586,11 @@ fn format_duration(dur: std::time::Duration) -> String { #[cfg(test)] mod tests { use crate::file; + use crate::test::reset; #[test] fn test_task_run() { + reset(); file::remove_all("test-build-output.txt").unwrap(); assert_cli_snapshot!( "r", diff --git a/src/cli/set.rs b/src/cli/set.rs index 024526c00..9b19edbea 100644 --- a/src/cli/set.rs +++ b/src/cli/set.rs @@ -128,6 +128,7 @@ static AFTER_LONG_HELP: &str = color_print::cstr!( mod tests { use std::path::PathBuf; + use crate::test::reset; use crate::{env, file}; fn remove_config_file(filename: &str) -> PathBuf { @@ -138,6 +139,7 @@ mod tests { #[test] fn test_show_env_vars() { + reset(); assert_cli_snapshot!("env-vars"); } diff --git a/src/cli/settings/get.rs b/src/cli/settings/get.rs index 9cf36b00e..d0209d3bb 100644 --- a/src/cli/settings/get.rs +++ b/src/cli/settings/get.rs @@ -46,11 +46,11 @@ static AFTER_LONG_HELP: &str = color_print::cstr!( #[cfg(test)] mod tests { - use crate::test::reset_config; + use crate::test::reset; #[test] fn test_settings_get() { - reset_config(); + reset(); assert_cli_snapshot!("settings", "get", "legacy_version_file", @"true"); assert_cli_snapshot!("settings", "get", "status.missing_tools", @r###""if_other_versions_installed""###); } diff --git a/src/cli/settings/ls.rs b/src/cli/settings/ls.rs index aed45f45c..1d471c498 100644 --- a/src/cli/settings/ls.rs +++ b/src/cli/settings/ls.rs @@ -53,11 +53,11 @@ static AFTER_LONG_HELP: &str = color_print::cstr!( #[cfg(test)] mod tests { - use crate::test::reset_config; + use crate::test::reset; #[test] fn test_settings_ls() { - reset_config(); + reset(); assert_cli_snapshot!("settings", @r###" activate_aggressive = false all_compile = false @@ -99,7 +99,7 @@ mod tests { #[test] fn test_settings_ls_keys() { - reset_config(); + reset(); assert_cli_snapshot!("settings", "--keys", @r###" activate_aggressive all_compile diff --git a/src/cli/settings/set.rs b/src/cli/settings/set.rs index 7d17a64db..7ea843985 100644 --- a/src/cli/settings/set.rs +++ b/src/cli/settings/set.rs @@ -107,11 +107,11 @@ static AFTER_LONG_HELP: &str = color_print::cstr!( #[cfg(test)] pub mod tests { - use crate::test::reset_config; + use crate::test::reset; #[test] fn test_settings_set() { - reset_config(); + reset(); assert_cli!("settings", "set", "legacy_version_file", "0"); assert_cli!("settings", "set", "always_keep_download", "y"); assert_cli!("settings", "set", "status.missing_tools", "never"); @@ -159,6 +159,6 @@ pub mod tests { show_env = false show_tools = false "###); - reset_config(); + reset(); } } diff --git a/src/cli/settings/unset.rs b/src/cli/settings/unset.rs index 9d5acb951..9de68e9ff 100644 --- a/src/cli/settings/unset.rs +++ b/src/cli/settings/unset.rs @@ -32,11 +32,11 @@ static AFTER_LONG_HELP: &str = color_print::cstr!( #[cfg(test)] mod tests { - use crate::test::reset_config; + use crate::test::reset; #[test] fn test_settings_unset() { - reset_config(); + reset(); assert_cli!("settings", "unset", "legacy_version_file"); @@ -78,6 +78,6 @@ mod tests { show_tools = false "###); - reset_config(); + reset(); } } diff --git a/src/cli/shell.rs b/src/cli/shell.rs index d3cd6e861..de8e4856e 100644 --- a/src/cli/shell.rs +++ b/src/cli/shell.rs @@ -93,10 +93,12 @@ static AFTER_LONG_HELP: &str = color_print::cstr!( #[cfg(test)] mod tests { + use crate::test::reset; use std::env; #[test] fn test_shell() { + reset(); let err = assert_cli_err!("shell", "tiny@1.0.1"); assert_snapshot!(err); env::set_var("__MISE_DIFF", ""); diff --git a/src/cli/snapshots/mise__cli__outdated__tests__current.snap b/src/cli/snapshots/mise__cli__outdated__tests__outdated.snap similarity index 100% rename from src/cli/snapshots/mise__cli__outdated__tests__current.snap rename to src/cli/snapshots/mise__cli__outdated__tests__outdated.snap diff --git a/src/cli/snapshots/mise__cli__outdated__tests__current_json.snap b/src/cli/snapshots/mise__cli__outdated__tests__outdated_json.snap similarity index 100% rename from src/cli/snapshots/mise__cli__outdated__tests__current_json.snap rename to src/cli/snapshots/mise__cli__outdated__tests__outdated_json.snap diff --git a/src/cli/snapshots/mise__cli__outdated__tests__current_with_runtimes.snap b/src/cli/snapshots/mise__cli__outdated__tests__outdated_with_runtimes.snap similarity index 100% rename from src/cli/snapshots/mise__cli__outdated__tests__current_with_runtimes.snap rename to src/cli/snapshots/mise__cli__outdated__tests__outdated_with_runtimes.snap diff --git a/src/cli/tasks/deps.rs b/src/cli/tasks/deps.rs index e5297bc1b..a9f3869dc 100644 --- a/src/cli/tasks/deps.rs +++ b/src/cli/tasks/deps.rs @@ -161,8 +161,11 @@ static AFTER_LONG_HELP: &str = color_print::cstr!( #[cfg(test)] mod tests { + use crate::test::reset; + #[test] fn test_tasks_deps_tree() { + reset(); assert_cli_snapshot!("tasks", "deps", @r###" configtask filetask @@ -176,6 +179,7 @@ mod tests { #[test] fn test_tasks_deps_tree_args() { + reset(); assert_cli_snapshot!("tasks", "deps", "filetask", "lint", "test", @r###" filetask ├── test @@ -188,6 +192,7 @@ mod tests { #[test] fn test_tasks_deps_dot() { + reset(); assert_cli_snapshot!("tasks", "deps", "--dot", @r###" digraph { 0 [ label = "configtask"] diff --git a/src/cli/tasks/ls.rs b/src/cli/tasks/ls.rs index bb41d803f..002b172ab 100644 --- a/src/cli/tasks/ls.rs +++ b/src/cli/tasks/ls.rs @@ -167,8 +167,12 @@ static AFTER_LONG_HELP: &str = color_print::cstr!( #[cfg(test)] mod tests { + use crate::test::reset; + use test_log::test; + #[test] fn test_task_ls() { + reset(); assert_cli_snapshot!("t", "--no-headers", @r###" configtask ~/config/config.toml filetask This is a test build script ~/cwd/.mise/tasks/filetask @@ -179,11 +183,13 @@ mod tests { #[test] fn test_task_ls_json() { + reset(); assert_cli_snapshot!("t", "--json"); } #[test] fn test_task_ls_json_extended() { + reset(); assert_cli_snapshot!("t", "--json", "-x"); } } diff --git a/src/cli/trust.rs b/src/cli/trust.rs index 08c648587..8404e2cee 100644 --- a/src/cli/trust.rs +++ b/src/cli/trust.rs @@ -5,7 +5,7 @@ use clap::ValueHint; use eyre::Result; use crate::config; -use crate::config::{config_file, DEFAULT_CONFIG_FILENAMES}; +use crate::config::{config_file, Settings, DEFAULT_CONFIG_FILENAMES}; use crate::dirs::TRUSTED_CONFIGS; use crate::file::remove_file; @@ -59,16 +59,29 @@ impl Trust { Ok(()) } fn untrust(&self) -> Result<()> { + let settings = Settings::get(); let path = match &self.config_file { Some(filename) => PathBuf::from(filename), None => match self.get_next_trusted() { Some(path) => path, - None => bail!("No trusted config files found."), + None => { + warn!("No trusted config files found."); + return Ok(()); + } }, }; let path = path.canonicalize()?; config_file::untrust(&path)?; info!("untrusted {}", path.display()); + + let trusted_via_settings = settings + .trusted_config_paths + .iter() + .any(|p| path.starts_with(p)); + if trusted_via_settings { + warn!("{path:?} is trusted via settings so it will still be trusted."); + } + Ok(()) } fn trust(&self) -> Result<()> { @@ -76,7 +89,10 @@ impl Trust { Some(filename) => PathBuf::from(filename), None => match self.get_next_untrusted() { Some(path) => path, - None => bail!("No untrusted config files found."), + None => { + warn!("No untrusted config files found."); + return Ok(()); + } }, }; let path = path.canonicalize()?; @@ -109,8 +125,12 @@ static AFTER_LONG_HELP: &str = color_print::cstr!( #[cfg(test)] mod tests { + use crate::test::reset; + #[test] fn test_trust() { + reset(); + assert_cli!("trust", "--untrust"); assert_cli_snapshot!("trust"); assert_cli_snapshot!("trust", "--untrust"); assert_cli_snapshot!("trust", ".test-tool-versions"); diff --git a/src/cli/upgrade.rs b/src/cli/upgrade.rs index 9e3fba286..5739b0a37 100644 --- a/src/cli/upgrade.rs +++ b/src/cli/upgrade.rs @@ -148,11 +148,11 @@ type OutputVec = Vec<(Arc, ToolVersion, String)>; #[cfg(test)] pub mod tests { use crate::dirs; - use crate::test::{change_installed_version, reset_config}; + use crate::test::{change_installed_version, reset}; #[test] fn test_upgrade() { - reset_config(); + reset(); change_installed_version("tiny", "3.1.0", "3.0.0"); assert_cli_snapshot!("upgrade", "--dry-run"); assert_cli_snapshot!("upgrade"); diff --git a/src/cli/use.rs b/src/cli/use.rs index 2dc202c02..af0158d0e 100644 --- a/src/cli/use.rs +++ b/src/cli/use.rs @@ -216,10 +216,12 @@ static AFTER_LONG_HELP: &str = color_print::cstr!( #[cfg(test)] mod tests { + use crate::test::reset; use crate::{dirs, env, file}; #[test] fn test_use_local() { + reset(); let cf_path = env::current_dir().unwrap().join(".test.mise.toml"); file::write(&cf_path, "").unwrap(); @@ -250,6 +252,7 @@ mod tests { #[test] fn test_use_local_tool_versions() { + reset(); let cf_path = env::current_dir().unwrap().join(".test-tool-versions"); file::write(&cf_path, "").unwrap(); @@ -261,6 +264,7 @@ mod tests { #[test] fn test_use_global() { + reset(); let cf_path = dirs::CONFIG.join("config.toml"); let orig = file::read_to_string(&cf_path).unwrap(); diff --git a/src/cli/where.rs b/src/cli/where.rs index 09dd3516b..f95ba1870 100644 --- a/src/cli/where.rs +++ b/src/cli/where.rs @@ -84,9 +84,12 @@ static AFTER_LONG_HELP: &str = color_print::cstr!( #[cfg(test)] mod tests { use crate::dirs; + use crate::test::reset; + use test_log::test; #[test] fn test_where() { + reset(); assert_cli!("install"); let stdout = assert_cli!("where", "tiny"); assert_str_eq!( @@ -97,6 +100,7 @@ mod tests { #[test] fn test_where_asdf_style() { + reset(); assert_cli!("install", "tiny@2", "tiny@3"); assert_cli_snapshot!("where", "tiny", "2"); assert_cli_snapshot!("where", "tiny", "3"); @@ -104,6 +108,7 @@ mod tests { #[test] fn test_where_alias() { + reset(); assert_cli!("install", "tiny@my/alias"); let stdout = assert_cli!("where", "tiny@my/alias"); assert_str_eq!( @@ -115,6 +120,7 @@ mod tests { #[test] fn test_where_not_found() { + reset(); let err = assert_cli_err!("where", "tiny@1111"); assert_snapshot!(err, @"tiny@1111 not installed"); } diff --git a/src/cli/which.rs b/src/cli/which.rs index 63163f7cd..d5c93dd18 100644 --- a/src/cli/which.rs +++ b/src/cli/which.rs @@ -83,8 +83,11 @@ static AFTER_LONG_HELP: &str = color_print::cstr!( #[cfg(test)] mod tests { + use crate::test::reset; + #[test] fn test_which() { + reset(); assert_cli!("use", "dummy@1.0.0"); assert_cli_snapshot!("which", "dummy"); assert_cli!("use", "dummy@ref:master"); @@ -94,6 +97,7 @@ mod tests { #[test] fn test_which_plugin() { + reset(); assert_cli!("use", "dummy@1.0.0"); assert_cli_snapshot!("which", "--plugin", "dummy"); assert_cli!("use", "dummy@ref:master"); @@ -103,6 +107,7 @@ mod tests { #[test] fn test_which_version() { + reset(); assert_cli!("use", "dummy@1.0.0"); assert_cli_snapshot!("which", "--version", "dummy"); assert_cli!("use", "dummy@ref:master"); @@ -112,6 +117,7 @@ mod tests { #[test] fn test_which_tool() { + reset(); assert_cli!("install", "dummy@1.0.1"); assert_cli_snapshot!("which", "dummy", "--tool=dummy@1.0.1"); } diff --git a/src/cmd.rs b/src/cmd.rs index 981fdee3d..cdc4fb832 100644 --- a/src/cmd.rs +++ b/src/cmd.rs @@ -10,7 +10,9 @@ use std::thread; use color_eyre::Result; use duct::{Expression, IntoExecutablePath}; use eyre::Context; +#[cfg(not(any(test, target_os = "windows")))] use signal_hook::consts::{SIGHUP, SIGINT, SIGQUIT, SIGTERM, SIGUSR1, SIGUSR2}; +#[cfg(not(any(test, target_os = "windows")))] use signal_hook::iterator::Signals; use crate::config::{Config, Settings}; @@ -301,7 +303,9 @@ impl<'a> CmdLineRunner<'a> { stdin.write_all(text.as_bytes()).unwrap(); }); } + #[cfg(not(any(test, target_os = "windows")))] let mut sighandle = None; + #[cfg(not(any(test, target_os = "windows")))] if self.pass_signals { let mut signals = Signals::new([SIGINT, SIGTERM, SIGTERM, SIGHUP, SIGQUIT, SIGUSR1, SIGUSR2])?; @@ -313,9 +317,11 @@ impl<'a> CmdLineRunner<'a> { } }); } + #[cfg(not(any(test, target_os = "windows")))] let id = cp.id(); thread::spawn(move || { let status = cp.wait().unwrap(); + #[cfg(not(any(test, target_os = "windows")))] if let Some(sighandle) = sighandle { sighandle.close(); } @@ -336,6 +342,7 @@ impl<'a> CmdLineRunner<'a> { ChildProcessOutput::ExitStatus(s) => { status = Some(s); } + #[cfg(not(any(test, target_os = "windows")))] ChildProcessOutput::Signal(sig) => { if sig != SIGINT { cmd!("kill", format!("-{sig}"), id.to_string()).run()?; @@ -430,6 +437,7 @@ enum ChildProcessOutput { Stdout(String), Stderr(String), ExitStatus(ExitStatus), + #[cfg(not(any(test, target_os = "windows")))] Signal(i32), } diff --git a/src/config/config_file/mise_toml.rs b/src/config/config_file/mise_toml.rs index af35fc2d5..7b5dc50e5 100644 --- a/src/config/config_file/mise_toml.rs +++ b/src/config/config_file/mise_toml.rs @@ -865,7 +865,8 @@ where mod tests { use dirs::CWD; - use crate::test::replace_path; + use crate::test::{replace_path, reset}; + use test_log::test; use super::*; @@ -883,6 +884,7 @@ mod tests { #[test] fn test_env() { + reset(); let p = CWD.as_ref().unwrap().join(".test.mise.toml"); file::write( &p, @@ -907,6 +909,7 @@ mod tests { #[test] fn test_env_array_valid() { + reset(); let env = parse_env(formatdoc! {r#" [[env]] foo="bar" @@ -923,6 +926,7 @@ mod tests { #[test] fn test_path_dirs() { + reset(); let env = parse_env(formatdoc! {r#" env_path=["/foo", "./bar"] [env] @@ -979,6 +983,7 @@ mod tests { #[test] fn test_env_file() { + reset(); let env = parse_env(formatdoc! {r#" env_file = ".env" "#}); @@ -1012,6 +1017,7 @@ mod tests { #[test] fn test_set_alias() { + reset(); let p = CWD.as_ref().unwrap().join(".test.mise.toml"); file::write( &p, @@ -1037,6 +1043,7 @@ mod tests { #[test] fn test_remove_alias() { + reset(); let p = CWD.as_ref().unwrap().join(".test.mise.toml"); file::write( &p, @@ -1066,6 +1073,7 @@ mod tests { #[test] fn test_replace_versions() { + reset(); let p = PathBuf::from("/tmp/.mise.toml"); file::write( &p, @@ -1089,6 +1097,7 @@ mod tests { #[test] fn test_remove_plugin() { + reset(); let p = PathBuf::from("/tmp/.mise.toml"); file::write( &p, @@ -1110,6 +1119,7 @@ mod tests { #[test] fn test_fail_with_unknown_key() { + reset(); let _ = toml::from_str::(&formatdoc! {r#" invalid_key = true "#}) @@ -1118,6 +1128,7 @@ mod tests { #[test] fn test_env_entries() { + reset(); let toml = formatdoc! {r#" [env] foo1="1" @@ -1139,6 +1150,7 @@ mod tests { #[test] fn test_env_arr() { + reset(); let toml = formatdoc! {r#" [[env]] foo1="1" diff --git a/src/config/config_file/mod.rs b/src/config/config_file/mod.rs index acf48f18a..898e89cf6 100644 --- a/src/config/config_file/mod.rs +++ b/src/config/config_file/mod.rs @@ -213,8 +213,9 @@ pub fn trust_check(path: &Path) -> eyre::Result<()> { Err(UntrustedConfig())? } +static IS_TRUSTED: Lazy>> = Lazy::new(|| Mutex::new(HashSet::new())); + pub fn is_trusted(path: &Path) -> bool { - static IS_TRUSTED: Lazy>> = Lazy::new(|| Mutex::new(HashSet::new())); let mut cached = IS_TRUSTED.lock().unwrap(); if cached.contains(path) { return true; @@ -340,6 +341,12 @@ pub struct TaskConfig { pub includes: Option>, } +#[cfg(test)] +pub fn reset() { + let mut cached = IS_TRUSTED.lock().unwrap(); + cached.clear(); +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/config/config_file/tool_versions.rs b/src/config/config_file/tool_versions.rs index 8bc94269a..9a61e211a 100644 --- a/src/config/config_file/tool_versions.rs +++ b/src/config/config_file/tool_versions.rs @@ -208,11 +208,14 @@ pub(crate) mod tests { use pretty_assertions::assert_eq; use crate::env; + use crate::test::reset; use super::*; + use test_log::test; #[test] fn test_parse() { + reset(); let tv = ToolVersions::from_file( env::current_dir() .unwrap() @@ -229,6 +232,7 @@ pub(crate) mod tests { #[test] fn test_parse_comments() { + reset(); let orig = indoc! {" # intro comment python 3.11.0 3.10.0 # some comment # more comment @@ -243,6 +247,7 @@ pub(crate) mod tests { #[test] fn test_parse_colon() { + reset(); let orig = indoc! {" ruby: 3.0.5 "}; @@ -255,6 +260,7 @@ pub(crate) mod tests { #[test] fn test_parse_tera() { + reset(); let orig = indoc! {" ruby {{'3.0.5'}} python {{exec(command='echo 3.11.0')}} @@ -271,6 +277,7 @@ pub(crate) mod tests { #[test] fn test_from_toolset() { + reset(); let orig = indoc! {" ruby: 3.0.5 3.1 "}; diff --git a/src/config/env_directive.rs b/src/config/env_directive.rs index 435495924..d5bf00c83 100644 --- a/src/config/env_directive.rs +++ b/src/config/env_directive.rs @@ -237,12 +237,14 @@ impl EnvResults { #[cfg(test)] mod tests { - use crate::test::replace_path; + use crate::test::{replace_path, reset}; use super::*; + use test_log::test; #[test] fn test_env_path() { + reset(); let mut env = HashMap::new(); env.insert("A".to_string(), "1".to_string()); env.insert("B".to_string(), "2".to_string()); @@ -284,6 +286,7 @@ mod tests { #[test] fn test_venv_path() { + reset(); let env = HashMap::new(); let results = EnvResults::resolve( &env, diff --git a/src/config/mod.rs b/src/config/mod.rs index ca5ca5473..c88b194d4 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -655,9 +655,11 @@ fn default_task_includes() -> Vec { #[cfg(test)] mod tests { use super::*; + use crate::test::reset; #[test] fn test_load() { + reset(); let config = Config::load().unwrap(); assert_debug_snapshot!(config); } diff --git a/src/env.rs b/src/env.rs index 2dac26471..39875b0a6 100644 --- a/src/env.rs +++ b/src/env.rs @@ -386,9 +386,12 @@ pub fn is_activated() -> bool { #[cfg(test)] mod tests { use super::*; + use crate::test::reset; + use test_log::test; #[test] fn test_apply_patches() { + reset(); let mut env = HashMap::new(); env.insert("foo".into(), "bar".into()); env.insert("baz".into(), "qux".into()); @@ -405,6 +408,7 @@ mod tests { #[test] fn test_var_path() { + reset(); set_var("MISE_TEST_PATH", "/foo/bar"); assert_eq!( var_path("MISE_TEST_PATH").unwrap(), diff --git a/src/file.rs b/src/file.rs index 19a97baa4..5d3e0c108 100644 --- a/src/file.rs +++ b/src/file.rs @@ -382,12 +382,14 @@ pub fn unzip(archive: &Path, dest: &Path) -> Result<()> { #[cfg(test)] mod tests { + use crate::test::reset; use std::ops::Deref; use super::*; #[test] fn test_find_up() { + reset(); let path = &env::current_dir().unwrap(); let filenames = vec![".miserc", ".mise.toml", ".test-tool-versions"] .into_iter() diff --git a/src/hash.rs b/src/hash.rs index 95d601387..da464401a 100644 --- a/src/hash.rs +++ b/src/hash.rs @@ -73,6 +73,8 @@ pub fn parse_shasums(text: &str) -> HashMap { #[cfg(test)] mod tests { use super::*; + use crate::test::reset; + use test_log::test; #[test] fn test_hash_to_str() { @@ -81,6 +83,7 @@ mod tests { #[test] fn test_hash_sha256() { + reset(); let path = Path::new(".test-tool-versions"); let hash = file_hash_sha256(path).unwrap(); assert_snapshot!(hash); diff --git a/src/hook_env.rs b/src/hook_env.rs index 5202cc62f..000348082 100644 --- a/src/hook_env.rs +++ b/src/hook_env.rs @@ -166,12 +166,15 @@ pub fn build_env_commands(shell: &dyn Shell, patches: &EnvDiffPatches) -> String #[cfg(test)] mod tests { + use crate::test::reset; use std::time::UNIX_EPOCH; use super::*; + use test_log::test; #[test] fn test_have_config_files_been_modified() { + reset(); let files = BTreeSet::new(); let watches = HookEnvWatches { files: BTreeMap::new(), @@ -198,6 +201,7 @@ mod tests { #[test] fn test_serialize_watches_empty() { + reset(); let watches = HookEnvWatches { files: BTreeMap::new(), env_var_hash: "".into(), @@ -209,6 +213,7 @@ mod tests { #[test] fn test_serialize_watches() { + reset(); let serialized = serialize_watches(&HookEnvWatches { files: BTreeMap::from([("foo".into(), UNIX_EPOCH)]), env_var_hash: "testing-123".into(), diff --git a/src/plugins/mod.rs b/src/plugins/mod.rs index 0a2a36271..937434d7c 100644 --- a/src/plugins/mod.rs +++ b/src/plugins/mod.rs @@ -51,9 +51,12 @@ pub fn list_external() -> ForgeList { mod tests { use super::*; use crate::forge::Forge; + use crate::test::reset; + use test_log::test; #[test] fn test_exact_match() { + reset(); assert_cli!("plugin", "add", "tiny"); let plugin = ExternalPlugin::new(String::from("tiny")); let version = plugin @@ -67,6 +70,7 @@ mod tests { #[test] fn test_latest_stable() { + reset(); let plugin = ExternalPlugin::new(String::from("dummy")); let version = plugin.latest_version(None).unwrap().unwrap(); assert_str_eq!(version, "2.0.0"); diff --git a/src/runtime_symlinks.rs b/src/runtime_symlinks.rs index 00cf9d821..c0137fdd0 100644 --- a/src/runtime_symlinks.rs +++ b/src/runtime_symlinks.rs @@ -114,11 +114,14 @@ pub fn is_runtime_symlink(path: &Path) -> bool { #[cfg(test)] mod tests { use crate::plugins::ExternalPlugin; + use crate::test::reset; use super::*; #[test] fn test_list_symlinks() { + reset(); + assert_cli!("install", "tiny@2"); let config = Config::load().unwrap(); let plugin = ExternalPlugin::new(String::from("tiny")); let plugin = Arc::new(plugin); diff --git a/src/shorthands.rs b/src/shorthands.rs index e66c53f04..685adf44a 100644 --- a/src/shorthands.rs +++ b/src/shorthands.rs @@ -50,12 +50,15 @@ fn parse_shorthands_file(mut f: PathBuf) -> Result { #[cfg(test)] mod tests { + use crate::test::reset; use std::ops::Deref; use super::*; + use test_log::test; #[test] fn test_get_shorthands() { + reset(); Settings::reset(None); let mut settings = Settings::get().deref().clone(); settings.shorthands_file = Some("../fixtures/shorthands.toml".into()); @@ -70,6 +73,7 @@ mod tests { #[test] fn test_get_shorthands_missing_file() { + reset(); Settings::reset(None); let mut settings = Settings::get().deref().clone(); settings.shorthands_file = Some("test/fixtures/missing.toml".into()); diff --git a/src/snapshots/mise__runtime_symlinks__tests__list_symlinks.snap b/src/snapshots/mise__runtime_symlinks__tests__list_symlinks.snap index e126dd02a..3b3e427d0 100644 --- a/src/snapshots/mise__runtime_symlinks__tests__list_symlinks.snap +++ b/src/snapshots/mise__runtime_symlinks__tests__list_symlinks.snap @@ -5,8 +5,6 @@ expression: symlinks { "latest": "./3.1.0", "lts": "./3.1.0", - "1": "./1.0.1", - "1.0": "./1.0.1", "2": "./2.1.0", "2.1": "./2.1.0", "3": "./3.1.0", diff --git a/src/task.rs b/src/task.rs index a55e4e6cf..1ad895b05 100644 --- a/src/task.rs +++ b/src/task.rs @@ -372,11 +372,13 @@ mod tests { use std::path::Path; use crate::task::Task; + use crate::test::reset; use super::{config_root, name_from_path}; #[test] fn test_from_path() { + reset(); let test_cases = [(".mise/tasks/filetask", "filetask", vec!["ft"])]; for (path, name, aliases) in test_cases { @@ -388,6 +390,7 @@ mod tests { #[test] fn test_name_from_path() { + reset(); let test_cases = [ (("/.mise/tasks", "/.mise/tasks/a"), "a"), (("/.mise/tasks", "/.mise/tasks/a/b"), "a:b"), @@ -403,6 +406,7 @@ mod tests { #[test] fn test_name_from_path_invalid() { + reset(); let test_cases = [("/some/other/dir", "/.mise/tasks/a")]; for (root, path) in test_cases { @@ -412,6 +416,7 @@ mod tests { #[test] fn test_config_root() { + reset(); let test_cases = [ ("/base", Some(Path::new("/"))), ("/base/.mise/tasks", Some(Path::new("/base"))), diff --git a/src/test.rs b/src/test.rs index e33c05556..bad7b1cac 100644 --- a/src/test.rs +++ b/src/test.rs @@ -4,12 +4,49 @@ use std::path::PathBuf; use color_eyre::{Help, SectionExt}; use crate::cli::Cli; -use crate::config::Config; +use crate::config::{config_file, Config}; use crate::output::tests::{STDERR, STDOUT}; use crate::{dirs, env, file, forge}; +#[macro_export] +macro_rules! assert_cli_snapshot { + ($($args:expr),+, @$snapshot:literal) => { + let args = &vec!["mise".into(), $($args.into()),+]; + let (stdout, stderr) = $crate::test::cli_run(args).unwrap(); + let output = [stdout, stderr].join("\n").trim().to_string(); + insta::assert_snapshot!(output, @$snapshot); + }; + ($($args:expr),+) => { + let args = &vec!["mise".into(), $($args.into()),+]; + let (stdout, stderr) = $crate::test::cli_run(args).unwrap(); + let output = [stdout, stderr].join("\n").trim().to_string(); + insta::assert_snapshot!(output); + }; +} + +#[macro_export] +macro_rules! assert_cli { + ($($args:expr),+) => {{ + let args = &vec!["mise".into(), $($args.into()),+]; + $crate::test::cli_run(args).unwrap(); + let output = $crate::output::tests::STDOUT.lock().unwrap().join("\n"); + console::strip_ansi_codes(&output).trim().to_string() + }}; +} + +#[macro_export] +macro_rules! assert_cli_err { + ($($args:expr),+) => {{ + let args = &vec!["mise".into(), $($args.into()),+]; + $crate::test::cli_run(args).unwrap_err() + }}; +} + #[ctor::ctor] fn init() { + if env::var("RUST_LOG").is_err() { + env::set_var("RUST_LOG", "debug") + } console::set_colors_enabled(false); console::set_colors_enabled_stderr(false); if env::var("__MISE_DIFF").is_ok() { @@ -20,7 +57,6 @@ fn init() { "HOME", PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("test"), ); - env::set_current_dir(env::HOME.join("cwd")).unwrap(); env::remove_var("MISE_TRUSTED_CONFIG_PATHS"); env::remove_var("MISE_DISABLE_TOOLS"); env::set_var("NO_COLOR", "1"); @@ -33,11 +69,13 @@ fn init() { env::set_var("MISE_DEFAULT_TOOL_VERSIONS_FILENAME", ".test-tool-versions"); env::set_var("MISE_DEFAULT_CONFIG_FILENAME", ".test.mise.toml"); //env::set_var("TERM", "dumb"); - reset_config(); - forge::reset(); } -pub fn reset_config() { +pub fn reset() { + env::set_current_dir(env::HOME.join("cwd")).unwrap(); + env::remove_var("MISE_FAILURE"); + file::remove_all(&*dirs::TRUSTED_CONFIGS).unwrap(); + file::remove_all(&*dirs::TRACKED_CONFIGS).unwrap(); file::write( env::HOME.join(".test-tool-versions"), indoc! {r#" @@ -86,6 +124,8 @@ pub fn reset_config() { ) .unwrap(); let _ = file::remove_file(".test.mise.toml"); + assert_cli!("prune"); + assert_cli!("install"); } pub fn replace_path(input: &str) -> String { @@ -103,6 +143,7 @@ pub fn replace_path(input: &str) -> String { pub fn cli_run(args: &Vec) -> eyre::Result<(String, String)> { Config::reset(); forge::reset(); + config_file::reset(); env::ARGS.write().unwrap().clone_from(args); STDOUT.lock().unwrap().clear(); STDERR.lock().unwrap().clear(); @@ -138,37 +179,3 @@ macro_rules! with_settings { ]}, {$body}) }} } - -#[macro_export] -macro_rules! assert_cli_snapshot { - ($($args:expr),+, @$snapshot:literal) => { - let args = &vec!["mise".into(), $($args.into()),+]; - let (stdout, stderr) = $crate::test::cli_run(args).unwrap(); - let output = [stdout, stderr].join("\n").trim().to_string(); - insta::assert_snapshot!(output, @$snapshot); - }; - ($($args:expr),+) => { - let args = &vec!["mise".into(), $($args.into()),+]; - let (stdout, stderr) = $crate::test::cli_run(args).unwrap(); - let output = [stdout, stderr].join("\n").trim().to_string(); - insta::assert_snapshot!(output); - }; -} - -#[macro_export] -macro_rules! assert_cli { - ($($args:expr),+) => {{ - let args = &vec!["mise".into(), $($args.into()),+]; - $crate::test::cli_run(args).unwrap(); - let output = $crate::output::tests::STDOUT.lock().unwrap().join("\n"); - console::strip_ansi_codes(&output).trim().to_string() - }}; -} - -#[macro_export] -macro_rules! assert_cli_err { - ($($args:expr),+) => {{ - let args = &vec!["mise".into(), $($args.into()),+]; - $crate::test::cli_run(args).unwrap_err() - }}; -} diff --git a/src/toolset/mod.rs b/src/toolset/mod.rs index ae26c0cb4..e669b8048 100644 --- a/src/toolset/mod.rs +++ b/src/toolset/mod.rs @@ -235,11 +235,14 @@ impl Toolset { .collect::>>>() .map(|x| x.into_iter().flatten().collect()) })?; + trace!("install: resolving"); if let Err(err) = self.resolve() { debug!("error resolving versions after install: {err:#}"); } + trace!("install: reshimming"); shims::reshim(self)?; runtime_symlinks::rebuild(config)?; + trace!("install: done"); Ok(installed) } diff --git a/src/toolset/tool_version_list.rs b/src/toolset/tool_version_list.rs index d8e87a9e8..da7f3442b 100644 --- a/src/toolset/tool_version_list.rs +++ b/src/toolset/tool_version_list.rs @@ -39,12 +39,14 @@ impl ToolVersionList { #[cfg(test)] mod tests { + use crate::test::reset; use crate::{dirs, env, file}; use super::*; #[test] fn test_tool_version_list() { + reset(); let fa: ForgeArg = "tiny".into(); let mut tvl = ToolVersionList::new(fa.clone(), ToolSource::Argument); tvl.requests.push(ToolRequest::new(fa, "latest").unwrap()); @@ -54,6 +56,7 @@ mod tests { #[test] fn test_tool_version_list_failure() { + reset(); forge::reset(); env::set_var("MISE_FAILURE", "1"); file::remove_all(dirs::CACHE.join("dummy")).unwrap(); @@ -62,6 +65,5 @@ mod tests { tvl.requests.push(ToolRequest::new(fa, "latest").unwrap()); let _ = tvl.resolve(true); assert_eq!(tvl.versions.len(), 0); - env::remove_var("MISE_FAILURE"); } } diff --git a/src/ui/ctrlc_stub.rs b/src/ui/ctrlc_stub.rs new file mode 100644 index 000000000..94fdc6a77 --- /dev/null +++ b/src/ui/ctrlc_stub.rs @@ -0,0 +1,8 @@ +#[must_use] +#[derive(Debug)] +pub struct HandleGuard(); + +/// ensures cursor is displayed on ctrl-c +pub fn handle_ctrlc() -> eyre::Result> { + Ok(Some(HandleGuard())) +} diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 08ace35bc..cae7ba540 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -1,5 +1,6 @@ pub use prompt::confirm; +#[cfg_attr(any(test, target_os = "windows"), path = "ctrlc_stub.rs")] pub mod ctrlc; pub mod multi_progress_report; pub mod progress_report; diff --git a/test/cwd/docs/cli-reference.md b/test/cwd/docs/cli-reference.md new file mode 100644 index 000000000..1437df40f --- /dev/null +++ b/test/cwd/docs/cli-reference.md @@ -0,0 +1,1761 @@ + + +# Commands + +## `mise activate [OPTIONS] [SHELL_TYPE]` + +```text +Initializes mise in the current shell session + +This should go into your shell's rc file. +Otherwise, it will only take effect in the current session. +(e.g. ~/.zshrc, ~/.bashrc) + +This is only intended to be used in interactive sessions, not scripts. +mise is only capable of updating PATH when the prompt is displayed to the user. +For non-interactive use-cases, use shims instead. + +Typically this can be added with something like the following: + + echo 'eval "$(mise activate)"' >> ~/.zshrc + +However, this requires that "mise" is in your PATH. If it is not, you need to +specify the full path like this: + + echo 'eval "$(/path/to/mise activate)"' >> ~/.zshrc + +Customize status output with `status` settings. + +Usage: activate [OPTIONS] [SHELL_TYPE] + +Arguments: + [SHELL_TYPE] + Shell type to generate the script for + + [possible values: bash, fish, nu, xonsh, zsh] + +Options: + --shims + Use shims instead of modifying PATH + Effectively the same as: + PATH="$HOME/.local/share/mise/shims:$PATH" + + -q, --quiet + Suppress non-error messages + +Examples: + + $ eval "$(mise activate bash)" + $ eval "$(mise activate zsh)" + $ mise activate fish | source + $ execx($(mise activate xonsh)) +``` + +## `mise alias get ` + +```text +Show an alias for a plugin + +This is the contents of an alias. entry in ~/.config/mise/config.toml + +Usage: alias get + +Arguments: + + The plugin to show the alias for + + + The alias to show + +Examples: + $ mise alias get node lts-hydrogen + 20.0.0 +``` + +## `mise alias ls [OPTIONS] [PLUGIN]` + +**Aliases:** `list` + +```text +List aliases +Shows the aliases that can be specified. +These can come from user config or from plugins in `bin/list-aliases`. + +For user config, aliases are defined like the following in `~/.config/mise/config.toml`: + + [alias.node] + lts = "20.0.0" + +Usage: alias ls [OPTIONS] [PLUGIN] + +Arguments: + [PLUGIN] + Show aliases for + +Options: + --no-header + Don't show table header + +Examples: + + $ mise aliases + node lts-hydrogen 20.0.0 +``` + +## `mise alias set ` + +**Aliases:** `add, create` + +```text +Add/update an alias for a plugin + +This modifies the contents of ~/.config/mise/config.toml + +Usage: alias set + +Arguments: + + The plugin to set the alias for + + + The alias to set + + + The value to set the alias to + +Examples: + + $ mise alias set node lts-hydrogen 18.0.0 +``` + +## `mise alias unset ` + +**Aliases:** `del, delete, remove, rm` + +```text +Clears an alias for a plugin + +This modifies the contents of ~/.config/mise/config.toml + +Usage: alias unset + +Arguments: + + The plugin to remove the alias from + + + The alias to remove + +Examples: + + $ mise alias unset node lts-hydrogen +``` + +## `mise backends ls` + +**Aliases:** `list` + +```text +List built-in backends + +Usage: backends ls + +Examples: + + $ mise backends ls + cargo + go + npm + pipx + ubi +``` + +## `mise bin-paths` + +```text +List all the active runtime bin paths + +Usage: bin-paths +``` + +## `mise cache clear [PLUGIN]...` + +**Aliases:** `c` + +```text +Deletes all cache files in mise + +Usage: cache clear [PLUGIN]... + +Arguments: + [PLUGIN]... + Plugin(s) to clear cache for e.g.: node, python +``` + +## `mise completion [SHELL]` + +```text +Generate shell completions + +Usage: completion [SHELL] + +Arguments: + [SHELL] + Shell type to generate completions for + + [possible values: bash, fish, zsh] + +Examples: + + $ mise completion bash > /etc/bash_completion.d/mise + $ mise completion zsh > /usr/local/share/zsh/site-functions/_mise + $ mise completion fish > ~/.config/fish/completions/mise.fish +``` + +## `mise config ls [OPTIONS]` + +```text +[experimental] List config files currently in use + +Usage: config ls [OPTIONS] + +Options: + --no-header + Do not print table header + +Examples: + + $ mise config ls +``` + +## `mise config generate [OPTIONS]` + +**Aliases:** `g` + +```text +[experimental] Generate an .mise.toml file + +Usage: config generate [OPTIONS] + +Options: + -o, --output + Output to file instead of stdout + +Examples: + + $ mise cf generate > .mise.toml + $ mise cf generate --output=.mise.toml +``` + +## `mise current [PLUGIN]` + +```text +Shows current active and installed runtime versions + +This is similar to `mise ls --current`, but this only shows the runtime +and/or version. It's designed to fit into scripts more easily. + +Usage: current [PLUGIN] + +Arguments: + [PLUGIN] + Plugin to show versions of e.g.: ruby, node, cargo:eza, npm:prettier, etc + +Examples: + # outputs `.tool-versions` compatible format + $ mise current + python 3.11.0 3.10.0 + shfmt 3.6.0 + shellcheck 0.9.0 + node 20.0.0 + + $ mise current node + 20.0.0 + + # can output multiple versions + $ mise current python + 3.11.0 3.10.0 +``` + +## `mise deactivate` + +```text +Disable mise for current shell session + +This can be used to temporarily disable mise in a shell session. + +Usage: deactivate + +Examples: + + $ mise deactivate bash + $ mise deactivate zsh + $ mise deactivate fish + $ execx($(mise deactivate xonsh)) +``` + +## `mise direnv activate` + +```text +Output direnv function to use mise inside direnv + +See https://mise.jdx.dev/direnv.html for more information + +Because this generates the legacy files based on currently installed plugins, +you should run this command after installing new plugins. Otherwise +direnv may not know to update environment variables when legacy file versions change. + +Usage: direnv activate + +Examples: + + $ mise direnv activate > ~/.config/direnv/lib/use_mise.sh + $ echo 'use mise' > .envrc + $ direnv allow +``` + +## `mise doctor` + +**Aliases:** `dr` + +```text +Check mise installation for possible problems + +Usage: doctor + +Examples: + + $ mise doctor + [WARN] plugin node is not installed +``` + +## `mise env [OPTIONS] [TOOL@VERSION]...` + +**Aliases:** `e` + +```text +Exports env vars to activate mise a single time + +Use this if you don't want to permanently install mise. It's not necessary to +use this if you have `mise activate` in your shell rc file. + +Usage: env [OPTIONS] [TOOL@VERSION]... + +Arguments: + [TOOL@VERSION]... + Tool(s) to use + +Options: + -J, --json + Output in JSON format + + -s, --shell + Shell type to generate environment variables for + + [possible values: bash, fish, nu, xonsh, zsh] + +Examples: + + $ eval "$(mise env -s bash)" + $ eval "$(mise env -s zsh)" + $ mise env -s fish | source + $ execx($(mise env -s xonsh)) +``` + +## `mise exec [OPTIONS] [TOOL@VERSION]... [-- ...]` + +**Aliases:** `x` + +```text +Execute a command with tool(s) set + +use this to avoid modifying the shell session or running ad-hoc commands with mise tools set. + +Tools will be loaded from .mise.toml/.tool-versions, though they can be overridden with args +Note that only the plugin specified will be overridden, so if a `.tool-versions` file +includes "node 20" but you run `mise exec python@3.11`; it will still load node@20. + +The "--" separates runtimes from the commands to pass along to the subprocess. + +Usage: exec [OPTIONS] [TOOL@VERSION]... [-- ...] + +Arguments: + [TOOL@VERSION]... + Tool(s) to start e.g.: node@20 python@3.10 + + [COMMAND]... + Command string to execute (same as --command) + +Options: + -c, --command + Command string to execute + + -j, --jobs + Number of jobs to run in parallel + [default: 4] + + [env: MISE_JOBS=] + + --raw + Directly pipe stdin/stdout/stderr from plugin to user Sets --jobs=1 + +Examples: + + $ mise exec node@20 -- node ./app.js # launch app.js using node-20.x + $ mise x node@20 -- node ./app.js # shorter alias + + # Specify command as a string: + $ mise exec node@20 python@3.11 --command "node -v && python -V" + + # Run a command in a different directory: + $ mise x -C /path/to/project node@20 -- node ./app.js +``` + +## `mise implode [OPTIONS]` + +```text +Removes mise CLI and all related data + +Skips config directory by default. + +Usage: implode [OPTIONS] + +Options: + --config + Also remove config directory + + -n, --dry-run + List directories that would be removed without actually removing them +``` + +## `mise install [OPTIONS] [TOOL@VERSION]...` + +**Aliases:** `i` + +```text +Install a tool version + +Installs a tool version to `~/.local/share/mise/installs//` +Installing alone will not activate the tools so they won't be in PATH. +To install and/or activate in one command, use `mise use` which will create a `.mise.toml` file +in the current directory to activate this tool when inside the directory. +Alternatively, run `mise exec @ -- ` to execute a tool without creating config files. + +Tools will be installed in parallel. To disable, set `--jobs=1` or `MISE_JOBS=1` + +Usage: install [OPTIONS] [TOOL@VERSION]... + +Arguments: + [TOOL@VERSION]... + Tool(s) to install e.g.: node@20 + +Options: + -f, --force + Force reinstall even if already installed + + -j, --jobs + Number of jobs to run in parallel + [default: 4] + + [env: MISE_JOBS=] + + --raw + Directly pipe stdin/stdout/stderr from plugin to user Sets --jobs=1 + + -v, --verbose... + Show installation output + + This argument will print plugin output such as download, configuration, and compilation output. + +Examples: + + $ mise install node@20.0.0 # install specific node version + $ mise install node@20 # install fuzzy node version + $ mise install node # install version specified in .tool-versions or .mise.toml + $ mise install # installs everything specified in .tool-versions or .mise.toml +``` + +## `mise latest [OPTIONS] ` + +```text +Gets the latest available version for a plugin + +Usage: latest [OPTIONS] + +Arguments: + + Tool to get the latest version of + +Options: + -i, --installed + Show latest installed instead of available version + +Examples: + + $ mise latest node@20 # get the latest version of node 20 + 20.0.0 + + $ mise latest node # get the latest stable version of node + 20.0.0 +``` + +## `mise link [OPTIONS] ` + +**Aliases:** `ln` + +```text +Symlinks a tool version into mise + +Use this for adding installs either custom compiled outside +mise or built with a different tool. + +Usage: link [OPTIONS] + +Arguments: + + Tool name and version to create a symlink for + + + The local path to the tool version + e.g.: ~/.nvm/versions/node/v20.0.0 + +Options: + -f, --force + Overwrite an existing tool version if it exists + +Examples: + # build node-20.0.0 with node-build and link it into mise + $ node-build 20.0.0 ~/.nodes/20.0.0 + $ mise link node@20.0.0 ~/.nodes/20.0.0 + + # have mise use the python version provided by Homebrew + $ brew install node + $ mise link node@brew $(brew --prefix node) + $ mise use node@brew +``` + +## `mise ls [OPTIONS] [PLUGIN]...` + +**Aliases:** `list` + +```text +List installed and active tool versions + +This command lists tools that mise "knows about". +These may be tools that are currently installed, or those +that are in a config file (active) but may or may not be installed. + +It's a useful command to get the current state of your tools. + +Usage: ls [OPTIONS] [PLUGIN]... + +Arguments: + [PLUGIN]... + Only show tool versions from [PLUGIN] + +Options: + -c, --current + Only show tool versions currently specified in a .tool-versions/.mise.toml + + -g, --global + Only show tool versions currently specified in a the global .tool-versions/.mise.toml + + -i, --installed + Only show tool versions that are installed (Hides tools defined in .tool-versions/.mise.toml but not installed) + + -J, --json + Output in JSON format + + -m, --missing + Display missing tool versions + + --prefix + Display versions matching this prefix + + --no-header + Don't display headers + +Examples: + + $ mise ls + node 20.0.0 ~/src/myapp/.tool-versions latest + python 3.11.0 ~/.tool-versions 3.10 + python 3.10.0 + + $ mise ls --current + node 20.0.0 ~/src/myapp/.tool-versions 20 + python 3.11.0 ~/.tool-versions 3.11.0 + + $ mise ls --json + { + "node": [ + { + "version": "20.0.0", + "install_path": "/Users/jdx/.mise/installs/node/20.0.0", + "source": { + "type": ".mise.toml", + "path": "/Users/jdx/.mise.toml" + } + } + ], + "python": [...] + } +``` + +## `mise ls-remote [OPTIONS] [TOOL@VERSION] [PREFIX]` + +```text +List runtime versions available for install + +note that the results are cached for 24 hours +run `mise cache clean` to clear the cache and get fresh results + +Usage: ls-remote [OPTIONS] [TOOL@VERSION] [PREFIX] + +Arguments: + [TOOL@VERSION] + Plugin to get versions for + + [PREFIX] + The version prefix to use when querying the latest version + same as the first argument after the "@" + +Options: + --all + Show all installed plugins and versions + +Examples: + + $ mise ls-remote node + 18.0.0 + 20.0.0 + + $ mise ls-remote node@20 + 20.0.0 + 20.1.0 + + $ mise ls-remote node 20 + 20.0.0 + 20.1.0 +``` + +## `mise outdated [OPTIONS] [TOOL@VERSION]...` + +```text +Shows outdated tool versions + +Usage: outdated [OPTIONS] [TOOL@VERSION]... + +Arguments: + [TOOL@VERSION]... + Tool(s) to show outdated versions for + e.g.: node@20 python@3.10 + If not specified, all tools in global and local configs will be shown + +Options: + -J, --json + Output in JSON format + +Examples: + + $ mise outdated + Plugin Requested Current Latest + python 3.11 3.11.0 3.11.1 + node 20 20.0.0 20.1.0 + + $ mise outdated node + Plugin Requested Current Latest + node 20 20.0.0 20.1.0 + + $ mise outdated --json + {"python": {"requested": "3.11", "current": "3.11.0", "latest": "3.11.1"}, ...} +``` + +## `mise plugins install [OPTIONS] [NEW_PLUGIN] [GIT_URL]` + +**Aliases:** `a, add, i` + +```text +Install a plugin + +note that mise automatically can install plugins when you install a tool +e.g.: `mise install node@20` will autoinstall the node plugin + +This behavior can be modified in ~/.config/mise/config.toml + +Usage: plugins install [OPTIONS] [NEW_PLUGIN] [GIT_URL] + +Arguments: + [NEW_PLUGIN] + The name of the plugin to install + e.g.: node, ruby + Can specify multiple plugins: `mise plugins install node ruby python` + + [GIT_URL] + The git url of the plugin + +Options: + -f, --force + Reinstall even if plugin exists + + -a, --all + Install all missing plugins + This will only install plugins that have matching shorthands. + i.e.: they don't need the full git repo url + + -v, --verbose... + Show installation output + +Examples: + # install the node via shorthand + $ mise plugins install node + + # install the node plugin using a specific git url + $ mise plugins install node https://github.com/mise-plugins/rtx-nodejs.git + + # install the node plugin using the git url only + # (node is inferred from the url) + $ mise plugins install https://github.com/mise-plugins/rtx-nodejs.git + + # install the node plugin using a specific ref + $ mise plugins install node https://github.com/mise-plugins/rtx-nodejs.git#v1.0.0 +``` + +## `mise plugins link [OPTIONS] [PATH]` + +**Aliases:** `ln` + +```text +Symlinks a plugin into mise + +This is used for developing a plugin. + +Usage: plugins link [OPTIONS] [PATH] + +Arguments: + + The name of the plugin + e.g.: node, ruby + + [PATH] + The local path to the plugin + e.g.: ./mise-node + +Options: + -f, --force + Overwrite existing plugin + +Examples: + # essentially just `ln -s ./mise-node ~/.local/share/mise/plugins/node` + $ mise plugins link node ./mise-node + + # infer plugin name as "node" + $ mise plugins link ./mise-node +``` + +## `mise plugins ls [OPTIONS]` + +**Aliases:** `list` + +```text +List installed plugins + +Can also show remotely available plugins to install. + +Usage: plugins ls [OPTIONS] + +Options: + -c, --core + The built-in plugins only + Normally these are not shown + + --user + List installed plugins + + This is the default behavior but can be used with --core + to show core and user plugins + + -u, --urls + Show the git url for each plugin + e.g.: https://github.com/asdf-vm/asdf-nodejs.git + +Examples: + + $ mise plugins ls + node + ruby + + $ mise plugins ls --urls + node https://github.com/asdf-vm/asdf-nodejs.git + ruby https://github.com/asdf-vm/asdf-ruby.git +``` + +## `mise plugins ls-remote [OPTIONS]` + +**Aliases:** `list-all, list-remote` + +```text +List all available remote plugins + +The full list is here: https://github.com/jdx/mise/blob/main/src/default_shorthands.rs + +Examples: + $ mise plugins ls-remote + + +Usage: plugins ls-remote [OPTIONS] + +Options: + -u, --urls + Show the git url for each plugin e.g.: https://github.com/mise-plugins/rtx-nodejs.git + + --only-names + Only show the name of each plugin by default it will show a "*" next to installed plugins +``` + +## `mise plugins uninstall [OPTIONS] [PLUGIN]...` + +**Aliases:** `remove, rm` + +```text +Removes a plugin + +Usage: plugins uninstall [OPTIONS] [PLUGIN]... + +Arguments: + [PLUGIN]... + Plugin(s) to remove + +Options: + -p, --purge + Also remove the plugin's installs, downloads, and cache + + -a, --all + Remove all plugins + +Examples: + + $ mise uninstall node +``` + +## `mise plugins update [OPTIONS] [PLUGIN]...` + +**Aliases:** `up, upgrade` + +```text +Updates a plugin to the latest version + +note: this updates the plugin itself, not the runtime versions + +Usage: plugins update [OPTIONS] [PLUGIN]... + +Arguments: + [PLUGIN]... + Plugin(s) to update + +Options: + -j, --jobs + Number of jobs to run in parallel + Default: 4 + +Examples: + + $ mise plugins update # update all plugins + $ mise plugins update node # update only node + $ mise plugins update node#beta # specify a ref +``` + +## `mise prune [OPTIONS] [PLUGIN]...` + +```text +Delete unused versions of tools + +mise tracks which config files have been used in ~/.local/share/mise/tracked_config_files +Versions which are no longer the latest specified in any of those configs are deleted. +Versions installed only with environment variables (`MISE__VERSION`) will be deleted, +as will versions only referenced on the command line (`mise exec @`). + +Usage: prune [OPTIONS] [PLUGIN]... + +Arguments: + [PLUGIN]... + Prune only versions from this plugin(s) + +Options: + -n, --dry-run + Do not actually delete anything + + --configs + Prune only tracked and trusted configuration links that point to non-existent configurations + + --tools + Prune only unused versions of tools + +Examples: + + $ mise prune --dry-run + rm -rf ~/.local/share/mise/versions/node/20.0.0 + rm -rf ~/.local/share/mise/versions/node/20.0.1 +``` + +## `mise reshim` + +```text +rebuilds the shim farm + +This creates new shims in ~/.local/share/mise/shims for CLIs that have been added. +mise will try to do this automatically for commands like `npm i -g` but there are +other ways to install things (like using yarn or pnpm for node) that mise does +not know about and so it will be necessary to call this explicitly. + +If you think mise should automatically call this for a particular command, please +open an issue on the mise repo. You can also setup a shell function to reshim +automatically (it's really fast so you don't need to worry about overhead): + +npm() { + command npm "$@" + mise reshim +} + +Usage: reshim + +Examples: + + $ mise reshim + $ ~/.local/share/mise/shims/node -v + v20.0.0 +``` + +## `mise run [OPTIONS] [TASK] [ARGS]...` + +**Aliases:** `r` + +```text +[experimental] Run a tasks + +This command will run a tasks, or multiple tasks in parallel. +Tasks may have dependencies on other tasks or on source files. +If source is configured on a tasks, it will only run if the source +files have changed. + +Tasks can be defined in .mise.toml or as standalone scripts. +In .mise.toml, tasks take this form: + + [tasks.build] + run = "npm run build" + sources = ["src/**/*.ts"] + outputs = ["dist/**/*.js"] + +Alternatively, tasks can be defined as standalone scripts. +These must be located in the `.mise/tasks` directory. +The name of the script will be the name of the tasks. + + $ cat .mise/tasks/build< + Change to this directory before executing the command + + -n, --dry-run + Don't actually run the tasks(s), just print them in order of execution + + -f, --force + Force the tasks to run even if outputs are up to date + + -p, --prefix + Print stdout/stderr by line, prefixed with the tasks's label + Defaults to true if --jobs > 1 + Configure with `task_output` config or `MISE_TASK_OUTPUT` env var + + -i, --interleave + Print directly to stdout/stderr instead of by line + Defaults to true if --jobs == 1 + Configure with `task_output` config or `MISE_TASK_OUTPUT` env var + + -t, --tool + Tool(s) to also add e.g.: node@20 python@3.10 + + -j, --jobs + Number of tasks to run in parallel + [default: 4] + Configure with `jobs` config or `MISE_JOBS` env var + + [env: MISE_JOBS=] + + -r, --raw + Read/write directly to stdin/stdout/stderr instead of by line + Configure with `raw` config or `MISE_RAW` env var + + --timings + Shows elapsed time after each tasks + +Examples: + + # Runs the "lint" tasks. This needs to either be defined in .mise.toml + # or as a standalone script. See the project README for more information. + $ mise run lint + + # Forces the "build" tasks to run even if its sources are up-to-date. + $ mise run build --force + + # Run "test" with stdin/stdout/stderr all connected to the current terminal. + # This forces `--jobs=1` to prevent interleaving of output. + $ mise run test --raw + + # Runs the "lint", "test", and "check" tasks in parallel. + $ mise run lint ::: test ::: check + + # Execute multiple tasks each with their own arguments. + $ mise tasks cmd1 arg1 arg2 ::: cmd2 arg1 arg2 +``` + +## `mise self-update [OPTIONS] [VERSION]` + +```text +Updates mise itself + +Uses the GitHub Releases API to find the latest release and binary +By default, this will also update any installed plugins + +Usage: self-update [OPTIONS] [VERSION] + +Arguments: + [VERSION] + Update to a specific version + +Options: + -f, --force + Update even if already up to date + + --no-plugins + Disable auto-updating plugins + + -y, --yes + Skip confirmation prompt +``` + +## `mise set [OPTIONS] [ENV_VARS]...` + +```text +Manage environment variables + +By default this command modifies ".mise.toml" in the current directory. + +Usage: set [OPTIONS] [ENV_VARS]... + +Arguments: + [ENV_VARS]... + Environment variable(s) to set + e.g.: NODE_ENV=production + +Options: + --file + The TOML file to update + + Defaults to MISE_DEFAULT_CONFIG_FILENAME environment variable, or ".mise.toml". + + -g, --global + Set the environment variable in the global config file + +Examples: + + $ mise set NODE_ENV=production + + $ mise set NODE_ENV + production + + $ mise set + key value source + NODE_ENV production ~/.config/mise/config.toml +``` + +## `mise settings get ` + +```text +Show a current setting + +This is the contents of a single entry in ~/.config/mise/config.toml + +Note that aliases are also stored in this file +but managed separately with `mise aliases get` + +Usage: settings get + +Arguments: + + The setting to show + +Examples: + + $ mise settings get legacy_version_file + true +``` + +## `mise settings ls [OPTIONS]` + +**Aliases:** `list` + +```text +Show current settings + +This is the contents of ~/.config/mise/config.toml + +Note that aliases are also stored in this file +but managed separately with `mise aliases` + +Usage: settings ls [OPTIONS] + +Options: + --keys + Only display key names for each setting + +Examples: + + $ mise settings + legacy_version_file = false +``` + +## `mise settings set ` + +**Aliases:** `add, create` + +```text +Add/update a setting + +This modifies the contents of ~/.config/mise/config.toml + +Usage: settings set + +Arguments: + + The setting to set + + + The value to set + +Examples: + + $ mise settings set legacy_version_file true +``` + +## `mise settings unset ` + +**Aliases:** `del, delete, remove, rm` + +```text +Clears a setting + +This modifies the contents of ~/.config/mise/config.toml + +Usage: settings unset + +Arguments: + + The setting to remove + +Examples: + + $ mise settings unset legacy_version_file +``` + +## `mise shell [OPTIONS] [TOOL@VERSION]...` + +**Aliases:** `sh` + +```text +Sets a tool version for the current session + +Only works in a session where mise is already activated. + +This works by setting environment variables for the current shell session +such as `MISE_NODE_VERSION=20` which is "eval"ed as a shell function created +by `mise activate`. + +Usage: shell [OPTIONS] [TOOL@VERSION]... + +Arguments: + [TOOL@VERSION]... + Tool(s) to use + +Options: + -j, --jobs + Number of jobs to run in parallel + [default: 4] + + [env: MISE_JOBS=] + + --raw + Directly pipe stdin/stdout/stderr from plugin to user Sets --jobs=1 + + -u, --unset + Removes a previously set version + +Examples: + + $ mise shell node@20 + $ node -v + v20.0.0 +``` + +## `mise sync node <--brew|--nvm|--nodenv>` + +```text +Symlinks all tool versions from an external tool into mise + +For example, use this to import all Homebrew node installs into mise + +Usage: sync node <--brew|--nvm|--nodenv> + +Options: + --brew + Get tool versions from Homebrew + + --nvm + Get tool versions from nvm + + --nodenv + Get tool versions from nodenv + +Examples: + + $ brew install node@18 node@20 + $ mise sync node --brew + $ mise use -g node@18 - uses Homebrew-provided node +``` + +## `mise sync python --pyenv` + +```text +Symlinks all tool versions from an external tool into mise + +For example, use this to import all pyenv installs into mise + +Usage: sync python --pyenv + +Options: + --pyenv + Get tool versions from pyenv + +Examples: + + $ pyenv install 3.11.0 + $ mise sync python --pyenv + $ mise use -g python@3.11.0 - uses pyenv-provided python +``` + +## `mise tasks deps [OPTIONS] [TASKS]...` + +```text +[experimental] Display a tree visualization of a dependency graph + +Usage: tasks deps [OPTIONS] [TASKS]... + +Arguments: + [TASKS]... + Tasks to show dependencies for + Can specify multiple tasks by separating with spaces + e.g.: mise tasks deps lint test check + +Options: + --hidden + Show hidden tasks + + --dot + Display dependencies in DOT format + +Examples: + + # Show dependencies for all tasks + $ mise tasks deps + + # Show dependencies for the "lint", "test" and "check" tasks + $ mise tasks deps lint test check + + # Show dependencies in DOT format + $ mise tasks deps --dot +``` + +## `mise tasks edit [OPTIONS] ` + +```text +[experimental] Edit a tasks with $EDITOR + +The tasks will be created as a standalone script if it does not already exist. + +Usage: tasks edit [OPTIONS] + +Arguments: + + Tasks to edit + +Options: + -p, --path + Display the path to the tasks instead of editing it + +Examples: + + $ mise tasks edit build + $ mise tasks edit test +``` + +## `mise tasks ls [OPTIONS]` + +```text +[experimental] List available tasks to execute +These may be included from the config file or from the project's .mise/tasks directory +mise will merge all tasks from all parent directories into this list. + +So if you have global tasks in ~/.config/mise/tasks/* and project-specific tasks in +~/myproject/.mise/tasks/*, then they'll both be available but the project-specific +tasks will override the global ones if they have the same name. + +Usage: tasks ls [OPTIONS] + +Options: + --no-header + Do not print table header + + -x, --extended + Show all columns + + --hidden + Show hidden tasks + + --sort + Sort by column. Default is name. + + [possible values: name, alias, description, source] + + --sort-order + Sort order. Default is asc. + + [possible values: asc, desc] + + -J, --json + Output in JSON format + +Examples: + + $ mise tasks ls +``` + +## `mise tasks run [OPTIONS] [TASK] [ARGS]...` + +**Aliases:** `r` + +```text +[experimental] Run a tasks + +This command will run a tasks, or multiple tasks in parallel. +Tasks may have dependencies on other tasks or on source files. +If source is configured on a tasks, it will only run if the source +files have changed. + +Tasks can be defined in .mise.toml or as standalone scripts. +In .mise.toml, tasks take this form: + + [tasks.build] + run = "npm run build" + sources = ["src/**/*.ts"] + outputs = ["dist/**/*.js"] + +Alternatively, tasks can be defined as standalone scripts. +These must be located in the `.mise/tasks` directory. +The name of the script will be the name of the tasks. + + $ cat .mise/tasks/build< + Change to this directory before executing the command + + -n, --dry-run + Don't actually run the tasks(s), just print them in order of execution + + -f, --force + Force the tasks to run even if outputs are up to date + + -p, --prefix + Print stdout/stderr by line, prefixed with the tasks's label + Defaults to true if --jobs > 1 + Configure with `task_output` config or `MISE_TASK_OUTPUT` env var + + -i, --interleave + Print directly to stdout/stderr instead of by line + Defaults to true if --jobs == 1 + Configure with `task_output` config or `MISE_TASK_OUTPUT` env var + + -t, --tool + Tool(s) to also add e.g.: node@20 python@3.10 + + -j, --jobs + Number of tasks to run in parallel + [default: 4] + Configure with `jobs` config or `MISE_JOBS` env var + + [env: MISE_JOBS=] + + -r, --raw + Read/write directly to stdin/stdout/stderr instead of by line + Configure with `raw` config or `MISE_RAW` env var + + --timings + Shows elapsed time after each tasks + +Examples: + + # Runs the "lint" tasks. This needs to either be defined in .mise.toml + # or as a standalone script. See the project README for more information. + $ mise run lint + + # Forces the "build" tasks to run even if its sources are up-to-date. + $ mise run build --force + + # Run "test" with stdin/stdout/stderr all connected to the current terminal. + # This forces `--jobs=1` to prevent interleaving of output. + $ mise run test --raw + + # Runs the "lint", "test", and "check" tasks in parallel. + $ mise run lint ::: test ::: check + + # Execute multiple tasks each with their own arguments. + $ mise tasks cmd1 arg1 arg2 ::: cmd2 arg1 arg2 +``` + +## `mise trust [OPTIONS] [CONFIG_FILE]` + +```text +Marks a config file as trusted + +This means mise will parse the file with potentially dangerous +features enabled. + +This includes: +- environment variables +- templates +- `path:` plugin versions + +Usage: trust [OPTIONS] [CONFIG_FILE] + +Arguments: + [CONFIG_FILE] + The config file to trust + +Options: + -a, --all + Trust all config files in the current directory and its parents + + --untrust + No longer trust this config + +Examples: + # trusts ~/some_dir/.mise.toml + $ mise trust ~/some_dir/.mise.toml + + # trusts .mise.toml in the current or parent directory + $ mise trust +``` + +## `mise uninstall [OPTIONS] [INSTALLED_TOOL@VERSION]...` + +**Aliases:** `remove, rm` + +```text +Removes runtime versions + +Usage: uninstall [OPTIONS] [INSTALLED_TOOL@VERSION]... + +Arguments: + [INSTALLED_TOOL@VERSION]... + Tool(s) to remove + +Options: + -a, --all + Delete all installed versions + + -n, --dry-run + Do not actually delete anything + +Examples: + + $ mise uninstall node@18.0.0 # will uninstall specific version + $ mise uninstall node # will uninstall current node version + $ mise uninstall --all node@18.0.0 # will uninstall all node versions +``` + +## `mise unset [OPTIONS] [KEYS]...` + +```text +Remove environment variable(s) from the config file + +By default this command modifies ".mise.toml" in the current directory. + +Usage: unset [OPTIONS] [KEYS]... + +Arguments: + [KEYS]... + Environment variable(s) to remove + e.g.: NODE_ENV + +Options: + -f, --file + Specify a file to use instead of ".mise.toml" + + -g, --global + Use the global config file +``` + +## `mise upgrade [OPTIONS] [TOOL@VERSION]...` + +**Aliases:** `up` + +```text +Upgrades outdated tool versions + +Usage: upgrade [OPTIONS] [TOOL@VERSION]... + +Arguments: + [TOOL@VERSION]... + Tool(s) to upgrade + e.g.: node@20 python@3.10 + If not specified, all current tools will be upgraded + +Options: + -n, --dry-run + Just print what would be done, don't actually do it + + -j, --jobs + Number of jobs to run in parallel + [default: 4] + + [env: MISE_JOBS=] + + -i, --interactive + Display multiselect menu to choose which tools to upgrade + + --raw + Directly pipe stdin/stdout/stderr from plugin to user Sets --jobs=1 +``` + +## `mise usage` + +```text +Generate a usage CLI spec + +See https://usage.jdx.dev for more information + +Usage: usage +``` + +## `mise use [OPTIONS] [TOOL@VERSION]...` + +**Aliases:** `u` + +```text +Install tool version and add it to config + +This will install the tool if it is not already installed. +By default, this will use an `.mise.toml` file in the current directory. +Use the --global flag to use the global config file instead. +This replaces asdf's `local` and `global` commands, however those are still available in mise. + +Usage: use [OPTIONS] [TOOL@VERSION]... + +Arguments: + [TOOL@VERSION]... + Tool(s) to add to config file + e.g.: node@20, cargo:ripgrep@latest npm:prettier@3 + If no version is specified, it will default to @latest + +Options: + -f, --force + Force reinstall even if already installed + + --fuzzy + Save fuzzy version to config file + e.g.: `mise use --fuzzy node@20` will save 20 as the version + this is the default behavior unless MISE_ASDF_COMPAT=1 + + -g, --global + Use the global config file (~/.config/mise/config.toml) instead of the local one + + -e, --env + Modify an environment-specific config file like .mise..toml + + -j, --jobs + Number of jobs to run in parallel + [default: 4] + + [env: MISE_JOBS=] + + --raw + Directly pipe stdin/stdout/stderr from plugin to user Sets --jobs=1 + + --remove + Remove the plugin(s) from config file + + -p, --path + Specify a path to a config file or directory If a directory is specified, it will look for .mise.toml (default) or .tool-versions + + --pin + Save exact version to config file + e.g.: `mise use --pin node@20` will save 20.0.0 as the version + Set MISE_ASDF_COMPAT=1 to make this the default behavior + +Examples: + + # set the current version of node to 20.x in .mise.toml of current directory + # will write the fuzzy version (e.g.: 20) + $ mise use node@20 + + # set the current version of node to 20.x in ~/.config/mise/config.toml + # will write the precise version (e.g.: 20.0.0) + $ mise use -g --pin node@20 + + # sets .mise.local.toml (which is intended not to be committed to a project) + $ mise use --env local node@20 + + # sets .mise.staging.toml (which is used if MISE_ENV=staging) + $ mise use --env staging node@20 +``` + +## `mise version` + +```text +Show mise version + +Usage: version +``` + +## `mise watch [OPTIONS] [ARGS]...` + +**Aliases:** `w` + +```text +[experimental] Run a tasks watching for changes + +Usage: watch [OPTIONS] [ARGS]... + +Arguments: + [ARGS]... + Extra arguments + +Options: + -t, --task + Tasks to run + + [default: default] + + -g, --glob + Files to watch + Defaults to sources from the tasks(s) + +Examples: + $ mise watch -t build + Runs the "build" tasks. Will re-run the tasks when any of its sources change. + Uses "sources" from the tasks definition to determine which files to watch. + + $ mise watch -t build --glob src/**/*.rs + Runs the "build" tasks but specify the files to watch with a glob pattern. + This overrides the "sources" from the tasks definition. + + $ mise run -t build --clear + Extra arguments are passed to watchexec. See `watchexec --help` for details. +``` + +## `mise where ` + +```text +Display the installation path for a runtime + +Must be installed. + +Usage: where + +Arguments: + + Tool(s) to look up + e.g.: ruby@3 + if "@" is specified, it will show the latest installed version + that matches the prefix + otherwise, it will show the current, active installed version + +Examples: + # Show the latest installed version of node + # If it is is not installed, errors + $ mise where node@20 + /home/jdx/.local/share/mise/installs/node/20.0.0 + + # Show the current, active install directory of node + # Errors if node is not referenced in any .tool-version file + $ mise where node + /home/jdx/.local/share/mise/installs/node/20.0.0 +``` + +## `mise which [OPTIONS] ` + +```text +Shows the path that a bin name points to + +Usage: which [OPTIONS] + +Arguments: + + The bin name to look up + +Options: + --plugin + Show the plugin name instead of the path + + --version + Show the version instead of the path + + -t, --tool + Use a specific tool@version + e.g.: `mise which npm --tool=node@20` + +Examples: + + $ mise which node + /home/username/.local/share/mise/installs/node/20.0.0/bin/node + $ mise which node --plugin + node + $ mise which node --version + 20.0.0 +``` + +