diff --git a/e2e/cli/test_exec_lockfile b/e2e/cli/test_exec_lockfile new file mode 100644 index 0000000000..eae6b48c8f --- /dev/null +++ b/e2e/cli/test_exec_lockfile @@ -0,0 +1,63 @@ +#!/usr/bin/env bash + +# `mise x` should only touch the project lockfile when the install resolves +# the same version specifier as the config. CLI overrides +# (`mise x tool@`) carry a different specifier, so pairing the +# config's request with their installed version would produce a nonsensical +# lockfile entry. + +export MISE_LOCKFILE=1 + +cat <mise.toml +[tools] +dummy = "1" +EOF + +mise uninstall dummy --all +mise install dummy@1.0.0 +touch mise.lock +mise lock --platform linux-x64 +assert_contains "cat mise.lock" "1.0.0" + +# `mise x dummy@2.0.0` resolves to 2.0.0 — an explicit CLI override that +# doesn't satisfy `dummy = "1"`. Lockfile must stay at 1.0.0. +mise x dummy@2.0.0 -- dummy +assert_contains "cat mise.lock" "1.0.0" +assert_not_contains "cat mise.lock" "2.0.0" + +# Same for `mise x dummy@latest` — the latest is 2.0.0, and the lockfile +# must not adopt it under the `"1"` request. +mise x dummy@latest -- dummy +assert_contains "cat mise.lock" "1.0.0" +assert_not_contains "cat mise.lock" "2.0.0" + +# `mise x dummy` (no version) carries the config's `"1"` specifier, so it +# isn't an override. Verify it still resolves and runs against the locked +# 1.0.0 (and would be allowed to populate the lockfile entry — exercised by +# the empty-lockfile case below). +mise x dummy -- dummy +assert_contains "cat mise.lock" "1.0.0" + +# Env-var overrides (`MISE__VERSION=...`) are the same class of +# override as CLI args — they carry a `ToolSource::Environment` and a +# version specifier that may not match the config's. The version-match +# check covers them too: with mise.toml `dummy = "1"`, setting +# `MISE_DUMMY_VERSION=2` must not pair the "1" request with a 2.x install. +MISE_DUMMY_VERSION=2 mise install +assert_contains "cat mise.lock" "1.0.0" +assert_not_contains "cat mise.lock" "2.0.0" + +# `mise upgrade` is the supported way to explicitly bump the lockfile and +# *should* update it (it remaps Argument → MiseToml before update_lockfiles). +mise upgrade dummy@1.1.0 +assert_contains "cat mise.lock" "1.1.0" +assert_not_contains "cat mise.lock" "1.0.0" + +# `mise x dummy` against an empty (but existing) lockfile must populate the +# entry — the install satisfies the config's `"1"` request, so propagation +# is correct. +mise uninstall dummy --all +echo "" >mise.lock +mise x dummy -- dummy +assert_contains "cat mise.lock" "dummy" +assert_contains "cat mise.lock" "1.1.0" diff --git a/mise.lock b/mise.lock index a18cf6b48f..977cb389a5 100644 --- a/mise.lock +++ b/mise.lock @@ -583,13 +583,9 @@ checksum = "sha256:795670a80d35b23e7ecf932ede742609f2267314f2df64054b020698fcc8a url = "https://github.com/LuaLS/lua-language-server/releases/download/3.17.1/lua-language-server-3.17.1-win32-x64.zip" [[tools.node]] -version = "25.9.0" +version = "24.15.0" backend = "core:node" -[tools.node."platforms.linux-x64"] -checksum = "sha256:134e55b2408448a219760fe04dc44d6851f9de8a79549021ffd870e9082d9e7b" -url = "https://nodejs.org/dist/v25.9.0/node-v25.9.0-linux-x64.tar.gz" - [[tools."npm:ajv-cli"]] version = "5.0.0" backend = "npm:ajv-cli" diff --git a/src/lockfile.rs b/src/lockfile.rs index 84e9c038d3..e0ecc9d2d0 100644 --- a/src/lockfile.rs +++ b/src/lockfile.rs @@ -989,6 +989,25 @@ pub fn update_lockfiles(config: &Config, ts: &Toolset, new_versions: &[ToolVersi .ok() .map(|(idx, tv)| (idx, tv.request.clone())) { + // Only propagate when the new install resolves the same + // version specifier as the config. `mise x node` (no + // version) is rewritten by `with_default_to_latest` to + // carry the config's version string, so it matches and + // updates the lockfile as expected. `mise x node@latest` + // with mise.toml `node = "24"` carries a "latest" + // specifier that doesn't match — that's an ad-hoc CLI + // override and pairing the "24" request with a 25.x + // install would produce a nonsensical lockfile entry. + if new_version.request.version() != request.version() { + trace!( + "skipping lockfile update for {}@{} in {}: CLI override does not match config request {}", + new_version.short(), + new_version.version, + display_path(&lockfile_path), + request.version(), + ); + continue; + } let mut new_version = new_version.clone(); new_version.request = request; versions.remove(idx);