From 4ad65fb637f92f9f567a8f490046beada3d4a538 Mon Sep 17 00:00:00 2001 From: Niki Date: Mon, 26 Oct 2026 11:45:45 +0100 Subject: [PATCH 1/3] test(mise): fail when an env lockfile is missing a requested tool version --- shell/lib/mise_test.bats | 54 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/shell/lib/mise_test.bats b/shell/lib/mise_test.bats index f1ed605c..5ce6de82 100644 --- a/shell/lib/mise_test.bats +++ b/shell/lib/mise_test.bats @@ -62,3 +62,57 @@ write_tool_versions() { assert_success assert_output "20.11.0" } + +# lock_missing_versions TOML LOCK +# +# Prints each `tool@version` requested in the [tools] section of TOML +# that has no matching entry in LOCK. +lock_missing_versions() { + awk ' + FNR == NR { + if ($0 ~ /^\[\[tools\./) { + name = $0 + sub(/^\[\[tools\./, "", name) + sub(/\]\]$/, "", name) + gsub(/"/, "", name) + } else if (name != "" && $0 ~ /^version = "/) { + version = $0 + sub(/^version = "/, "", version) + sub(/"$/, "", version) + locked[name "@" version] = 1 + name = "" + } + next + } + /^\[/ { inTools = ($0 == "[tools]"); next } + inTools && $0 ~ /^[^#].* = "/ { + tool = $0 + sub(/ = .*/, "", tool) + gsub(/"/, "", tool) + version = $0 + sub(/^[^=]*= "/, "", version) + sub(/".*$/, "", version) + if (!((tool "@" version) in locked)) print tool "@" version + } + ' "$2" "$1" +} + +@test "lock_missing_versions reports a requested version missing from the lockfile" { + printf '%s\n' '[tools]' '"github:getoutreach/devenv" = "1.68.0"' 'kubectl = "1.34.7"' >"$REPO_DIR/mise.test.toml" + printf '%s\n' '[[tools."github:getoutreach/devenv"]]' 'version = "1.67.0"' '[[tools.kubectl]]' 'version = "1.34.7"' >"$REPO_DIR/mise.test.lock" + run lock_missing_versions "$REPO_DIR/mise.test.toml" "$REPO_DIR/mise.test.lock" + assert_success + assert_output "github:getoutreach/devenv@1.68.0" +} + +@test "mise.devbase.lock pins every version requested in mise.devbase.toml" { + run lock_missing_versions "$BATS_TEST_DIRNAME/../../mise.devbase.toml" "$BATS_TEST_DIRNAME/../../mise.devbase.lock" + assert_success + assert_output "" +} + +@test "mise.e2e.lock pins every version requested in mise.e2e.toml" { + run lock_missing_versions "$BATS_TEST_DIRNAME/../../mise.e2e.toml" "$BATS_TEST_DIRNAME/../../mise.e2e.lock" + assert_success + assert_output "" +} From a1aac41fcc65947581417897abc4f1f28e670c85 Mon Sep 17 00:00:00 2001 From: Niki Date: Mon, 26 Oct 2026 11:45:45 +0100 Subject: [PATCH 2/3] test(mise): fail when an env lockfile is missing a requested tool version --- shell/lib/mise_test.bats | 54 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/shell/lib/mise_test.bats b/shell/lib/mise_test.bats index f1ed605c..5ce6de82 100644 --- a/shell/lib/mise_test.bats +++ b/shell/lib/mise_test.bats @@ -62,3 +62,57 @@ write_tool_versions() { assert_success assert_output "20.11.0" } + +# lock_missing_versions TOML LOCK +# +# Prints each `tool@version` requested in the [tools] section of TOML +# that has no matching entry in LOCK. +lock_missing_versions() { + awk ' + FNR == NR { + if ($0 ~ /^\[\[tools\./) { + name = $0 + sub(/^\[\[tools\./, "", name) + sub(/\]\]$/, "", name) + gsub(/"/, "", name) + } else if (name != "" && $0 ~ /^version = "/) { + version = $0 + sub(/^version = "/, "", version) + sub(/"$/, "", version) + locked[name "@" version] = 1 + name = "" + } + next + } + /^\[/ { inTools = ($0 == "[tools]"); next } + inTools && $0 ~ /^[^#].* = "/ { + tool = $0 + sub(/ = .*/, "", tool) + gsub(/"/, "", tool) + version = $0 + sub(/^[^=]*= "/, "", version) + sub(/".*$/, "", version) + if (!((tool "@" version) in locked)) print tool "@" version + } + ' "$2" "$1" +} + +@test "lock_missing_versions reports a requested version missing from the lockfile" { + printf '%s\n' '[tools]' '"github:getoutreach/devenv" = "1.68.0"' 'kubectl = "1.34.7"' >"$REPO_DIR/mise.test.toml" + printf '%s\n' '[[tools."github:getoutreach/devenv"]]' 'version = "1.67.0"' '[[tools.kubectl]]' 'version = "1.34.7"' >"$REPO_DIR/mise.test.lock" + run lock_missing_versions "$REPO_DIR/mise.test.toml" "$REPO_DIR/mise.test.lock" + assert_success + assert_output "github:getoutreach/devenv@1.68.0" +} + +@test "mise.devbase.lock pins every version requested in mise.devbase.toml" { + run lock_missing_versions "$BATS_TEST_DIRNAME/../../mise.devbase.toml" "$BATS_TEST_DIRNAME/../../mise.devbase.lock" + assert_success + assert_output "" +} + +@test "mise.e2e.lock pins every version requested in mise.e2e.toml" { + run lock_missing_versions "$BATS_TEST_DIRNAME/../../mise.e2e.toml" "$BATS_TEST_DIRNAME/../../mise.e2e.lock" + assert_success + assert_output "" +} From 81ce59e86c5eeceebcf5f080ae42f9a5424c5e46 Mon Sep 17 00:00:00 2001 From: Niki Date: Thu, 29 Oct 2026 13:43:41 +0100 Subject: [PATCH 3/3] test(mise): check lockfiles with devbase tomlq instead of awk Parses inline-table and array tool entries, and accepts fuzzy and "latest" requests that the lockfile resolves to a full version. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- shell/lib/mise_test.bats | 54 ++++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/shell/lib/mise_test.bats b/shell/lib/mise_test.bats index 5ce6de82..acc8b019 100644 --- a/shell/lib/mise_test.bats +++ b/shell/lib/mise_test.bats @@ -4,6 +4,7 @@ bats_load_library "bats-support/load.bash" bats_load_library "bats-assert/load.bash" load mise.sh +load yq.sh load test_helper.sh setup() { @@ -68,33 +69,15 @@ write_tool_versions() { # Prints each `tool@version` requested in the [tools] section of TOML # that has no matching entry in LOCK. lock_missing_versions() { - awk ' - FNR == NR { - if ($0 ~ /^\[\[tools\./) { - name = $0 - sub(/^\[\[tools\./, "", name) - sub(/\]\]$/, "", name) - gsub(/"/, "", name) - } else if (name != "" && $0 ~ /^version = "/) { - version = $0 - sub(/^version = "/, "", version) - sub(/"$/, "", version) - locked[name "@" version] = 1 - name = "" - } - next - } - /^\[/ { inTools = ($0 == "[tools]"); next } - inTools && $0 ~ /^[^#].* = "/ { - tool = $0 - sub(/ = .*/, "", tool) - gsub(/"/, "", tool) - version = $0 - sub(/^[^=]*= "/, "", version) - sub(/".*$/, "", version) - if (!((tool "@" version) in locked)) print tool "@" version - } - ' "$2" "$1" + local locked + locked="$(tomlq_wrapper -c '(.tools // {}) | map_values([.[].version])' "$2")" + # shellcheck disable=SC2016 # $tool, $req and $locked are jq variables + tomlq_wrapper -r --argjson locked "$locked" ' + (.tools // {}) | to_entries[] | .key as $tool + | (.value | if type == "object" then .version elif type == "array" then .[] else . end) + | select(type == "string") as $req + | select(($locked[$tool] // []) | any(. == $req or startswith($req + ".") or $req == "latest") | not) + | "\($tool)@\($req)"' "$1" } @test "lock_missing_versions reports a requested version missing from the lockfile" { @@ -105,6 +88,23 @@ lock_missing_versions() { assert_output "github:getoutreach/devenv@1.68.0" } +@test "lock_missing_versions checks inline table and array tool entries" { + printf '%s\n' '[tools]' 'node = { version = "20.11.0" }' 'gojq = ["0.12.17", "0.12.18"]' 'kubectl = { version = "1.34.7", os = ["linux"] }' >"$REPO_DIR/mise.test.toml" + printf '%s\n' '[[tools.node]]' 'version = "20.11.0"' '[[tools.gojq]]' 'version = "0.12.17"' >"$REPO_DIR/mise.test.lock" + run lock_missing_versions "$REPO_DIR/mise.test.toml" "$REPO_DIR/mise.test.lock" + assert_success + assert_output "gojq@0.12.18 +kubectl@1.34.7" +} + +@test "lock_missing_versions accepts fuzzy and latest requests resolved in the lockfile" { + printf '%s\n' '[tools]' 'node = "20"' 'shfmt = "latest"' >"$REPO_DIR/mise.test.toml" + printf '%s\n' '[[tools.node]]' 'version = "20.11.0"' '[[tools.shfmt]]' 'version = "3.12.0"' >"$REPO_DIR/mise.test.lock" + run lock_missing_versions "$REPO_DIR/mise.test.toml" "$REPO_DIR/mise.test.lock" + assert_success + assert_output "" +} + @test "mise.devbase.lock pins every version requested in mise.devbase.toml" { run lock_missing_versions "$BATS_TEST_DIRNAME/../../mise.devbase.toml" "$BATS_TEST_DIRNAME/../../mise.devbase.lock" assert_success