From 7a6cf309b0b6fdce46e1aadc0b179057d5947bd6 Mon Sep 17 00:00:00 2001 From: Palo Otcenas Date: Thu, 26 Mar 2026 22:13:46 +0100 Subject: [PATCH 1/9] fix(ci): enable MISE_LOCKED=1 to eliminate GitHub API calls from mise Enable strict lockfile mode for mise in CI so all tools resolve from pre-resolved URLs in lockfiles instead of hitting api.github.com for version resolution. This is safe because: - mise already ignores .tool-versions in CI (MISE_OVERRIDE_TOOL_VERSIONS_FILENAMES=none) - All devbase tools have entries in mise.devbase.lock - The setting only applies to mise install calls, not to mise settings/activate Repos can opt out by setting MISE_LOCKED=0 in their CircleCI project env. Relates-to: DT-5157 --- shell/ci/env/mise.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/shell/ci/env/mise.sh b/shell/ci/env/mise.sh index 3618e8b1..b605a38c 100755 --- a/shell/ci/env/mise.sh +++ b/shell/ci/env/mise.sh @@ -35,6 +35,11 @@ inject_mise_commands() { # Assumes that `gh` has already been set up. ghToken="$(gh auth token)" +# Use strict lockfile mode to avoid GitHub API calls for version resolution. +# All tools resolve from pre-resolved URLs in their lockfile instead of +# hitting api.github.com. Set MISE_LOCKED=0 in CircleCI project env to opt out. +export MISE_LOCKED="${MISE_LOCKED:-1}" + # TODO(malept): feature parity with asdf.sh in the same folder. if [[ -f "$repoDir"/mise.toml ]]; then info_sub "🧑‍🍳 installing tool versions via mise" From 70ac4d401c0bdd55ffed9b0621ec94ccfe4f32ad Mon Sep 17 00:00:00 2001 From: Palo Otcenas Date: Thu, 26 Mar 2026 23:16:42 +0100 Subject: [PATCH 2/9] fix(ci): use MISE_CONFIG_DIR isolation + per-invocation --locked Replace MISE_LOCKED=1 global env var which validated ALL config sources (global, conf.d, .tool-versions) against a single project lockfile. Instead: - Set MISE_CONFIG_DIR to a temp dir during install to hide global config - Pass --locked per-invocation: guarded by mise.lock existence for downstream repos, always-on for devbase (mise.devbase.lock) - Restore MISE_CONFIG_DIR after install for devbase_configure_global_tools --- shell/ci/env/mise.sh | 30 +++++++++++++++++++++++++----- shell/lib/mise.sh | 3 ++- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/shell/ci/env/mise.sh b/shell/ci/env/mise.sh index b605a38c..0a30ed6f 100755 --- a/shell/ci/env/mise.sh +++ b/shell/ci/env/mise.sh @@ -35,10 +35,13 @@ inject_mise_commands() { # Assumes that `gh` has already been set up. ghToken="$(gh auth token)" -# Use strict lockfile mode to avoid GitHub API calls for version resolution. -# All tools resolve from pre-resolved URLs in their lockfile instead of -# hitting api.github.com. Set MISE_LOCKED=0 in CircleCI project env to opt out. -export MISE_LOCKED="${MISE_LOCKED:-1}" +# Isolate from global mise config during tool installation. +# Global tools (from orc setup or pre-built CI image in ~/.config/mise/) +# don't have lockfiles and cause --locked to fail. MISE_CONFIG_DIR redirects +# global config discovery to an empty directory so only project-level and +# devbase-level configs are visible. Restored after install for shim setup. +_mise_original_config_dir="${MISE_CONFIG_DIR:-}" +export MISE_CONFIG_DIR="$(mktemp -d)" # TODO(malept): feature parity with asdf.sh in the same folder. if [[ -f "$repoDir"/mise.toml ]]; then @@ -48,10 +51,27 @@ if [[ -f "$repoDir"/mise.toml ]]; then else info_sub "🧑‍🍳 ignoring .tool-versions (managed by asdf)" fi - MISE_GITHUB_TOKEN="$ghToken" run_mise install --cd "$repoDir" --yes + + # Use --locked when a lockfile exists to prevent GitHub API calls for + # version resolution. Repos without mise.lock fall back to normal install. + locked_flag="" + if [[ -f "$repoDir"/mise.lock ]]; then + locked_flag="--locked" + fi + # shellcheck disable=SC2086 + MISE_GITHUB_TOKEN="$ghToken" run_mise install --cd "$repoDir" $locked_flag --yes fi MISE_GITHUB_TOKEN="$ghToken" devbase_install_mise_tools + +# Restore global config dir for shim setup. +rm -rf "$MISE_CONFIG_DIR" +if [[ -n "$_mise_original_config_dir" ]]; then + MISE_CONFIG_DIR="$_mise_original_config_dir" +else + unset MISE_CONFIG_DIR +fi + devbase_configure_global_tools if [[ -n ${BASH_ENV:-} ]]; then diff --git a/shell/lib/mise.sh b/shell/lib/mise.sh index 19aa8beb..eb2b5d84 100644 --- a/shell/lib/mise.sh +++ b/shell/lib/mise.sh @@ -417,7 +417,8 @@ devbase_install_mise_tools() { if ! mise_version_compatible "2025.10.11"; then mise settings set experimental true fi - devbase_mise install --yes + # devbase always has mise.devbase.lock, so --locked is safe here. + devbase_mise install --yes --locked } # The current version of mise. From 74e77ff4b0180cd9916b3008039c8199b97ad50e Mon Sep 17 00:00:00 2001 From: Palo Otcenas Date: Thu, 26 Mar 2026 23:39:42 +0100 Subject: [PATCH 3/9] fix: scope MISE_CONFIG_DIR to mise binary only, not shims The previous approach exported MISE_CONFIG_DIR globally, which broke wait-for-gh-rate-limit (a mise shim) because the shim couldn't resolve its tool version against the empty config dir. Instead, pass _MISE_INSTALL_CONFIG_DIR through run_mise() and apply MISE_CONFIG_DIR as an inline env var only on the mise binary invocation. Shims and other helper tools still see the real global config. --- shell/ci/env/mise.sh | 23 +++++++++-------------- shell/lib/mise.sh | 13 +++++++++++-- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/shell/ci/env/mise.sh b/shell/ci/env/mise.sh index 0a30ed6f..30b10815 100755 --- a/shell/ci/env/mise.sh +++ b/shell/ci/env/mise.sh @@ -35,13 +35,13 @@ inject_mise_commands() { # Assumes that `gh` has already been set up. ghToken="$(gh auth token)" -# Isolate from global mise config during tool installation. -# Global tools (from orc setup or pre-built CI image in ~/.config/mise/) -# don't have lockfiles and cause --locked to fail. MISE_CONFIG_DIR redirects -# global config discovery to an empty directory so only project-level and -# devbase-level configs are visible. Restored after install for shim setup. -_mise_original_config_dir="${MISE_CONFIG_DIR:-}" -export MISE_CONFIG_DIR="$(mktemp -d)" +# Isolate mise install from global config. Global tools (from orc setup or +# pre-built CI image in ~/.config/mise/) don't have lockfiles and cause +# --locked to fail. _MISE_INSTALL_CONFIG_DIR is picked up by run_mise() and +# applied only to the mise binary invocation, so shims (e.g. +# wait-for-gh-rate-limit) still resolve against the real global config. +_mise_empty_config_dir="$(mktemp -d)" +export _MISE_INSTALL_CONFIG_DIR="$_mise_empty_config_dir" # TODO(malept): feature parity with asdf.sh in the same folder. if [[ -f "$repoDir"/mise.toml ]]; then @@ -64,13 +64,8 @@ fi MISE_GITHUB_TOKEN="$ghToken" devbase_install_mise_tools -# Restore global config dir for shim setup. -rm -rf "$MISE_CONFIG_DIR" -if [[ -n "$_mise_original_config_dir" ]]; then - MISE_CONFIG_DIR="$_mise_original_config_dir" -else - unset MISE_CONFIG_DIR -fi +unset _MISE_INSTALL_CONFIG_DIR +rm -rf "$_mise_empty_config_dir" devbase_configure_global_tools diff --git a/shell/lib/mise.sh b/shell/lib/mise.sh index eb2b5d84..afa4d7ca 100644 --- a/shell/lib/mise.sh +++ b/shell/lib/mise.sh @@ -254,8 +254,17 @@ run_mise() { tool_versions_override="none" fi - MISE_OVERRIDE_TOOL_VERSIONS_FILENAMES="$tool_versions_override" \ - "$mise_path" "$@" + # _MISE_INSTALL_CONFIG_DIR, when set, overrides MISE_CONFIG_DIR only for + # the mise binary — not for helper tools like wait-for-gh-rate-limit + # that run via mise shims and need the real global config. + if [[ -n "${_MISE_INSTALL_CONFIG_DIR:-}" ]]; then + MISE_OVERRIDE_TOOL_VERSIONS_FILENAMES="$tool_versions_override" \ + MISE_CONFIG_DIR="$_MISE_INSTALL_CONFIG_DIR" \ + "$mise_path" "$@" + else + MISE_OVERRIDE_TOOL_VERSIONS_FILENAMES="$tool_versions_override" \ + "$mise_path" "$@" + fi } # If `wait-for-gh-rate-limit` is installed, runs it to wait for From a526e66315db53651c9a42d3da9aec9c0398d240 Mon Sep 17 00:00:00 2001 From: Palo Otcenas Date: Thu, 26 Mar 2026 23:41:57 +0100 Subject: [PATCH 4/9] refactor: remove redundant _mise_empty_config_dir variable --- shell/ci/env/mise.sh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/shell/ci/env/mise.sh b/shell/ci/env/mise.sh index 30b10815..ebeb47d6 100755 --- a/shell/ci/env/mise.sh +++ b/shell/ci/env/mise.sh @@ -40,8 +40,7 @@ ghToken="$(gh auth token)" # --locked to fail. _MISE_INSTALL_CONFIG_DIR is picked up by run_mise() and # applied only to the mise binary invocation, so shims (e.g. # wait-for-gh-rate-limit) still resolve against the real global config. -_mise_empty_config_dir="$(mktemp -d)" -export _MISE_INSTALL_CONFIG_DIR="$_mise_empty_config_dir" +export _MISE_INSTALL_CONFIG_DIR="$(mktemp -d)" # TODO(malept): feature parity with asdf.sh in the same folder. if [[ -f "$repoDir"/mise.toml ]]; then @@ -64,8 +63,8 @@ fi MISE_GITHUB_TOKEN="$ghToken" devbase_install_mise_tools +rm -rf "$_MISE_INSTALL_CONFIG_DIR" unset _MISE_INSTALL_CONFIG_DIR -rm -rf "$_mise_empty_config_dir" devbase_configure_global_tools From a6cb30c0aa73b15443bf17bfe2096f74a79b726e Mon Sep 17 00:00:00 2001 From: Palo Otcenas Date: Thu, 26 Mar 2026 23:51:33 +0100 Subject: [PATCH 5/9] fix: two-pass install to handle go: backend in locked mode go: backend tools (goimports, dlv, goveralls) compile from source via `go install` and cannot produce lockfile URLs. --locked always fails for them. Split devbase tool install into two passes: 1) MISE_DISABLE_BACKENDS=go with --locked for all lockable tools 2) Normal install for go: tools (they use Go module proxy, not GitHub API) --- shell/lib/mise.sh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/shell/lib/mise.sh b/shell/lib/mise.sh index afa4d7ca..7da38e29 100644 --- a/shell/lib/mise.sh +++ b/shell/lib/mise.sh @@ -426,8 +426,12 @@ devbase_install_mise_tools() { if ! mise_version_compatible "2025.10.11"; then mise settings set experimental true fi - # devbase always has mise.devbase.lock, so --locked is safe here. - devbase_mise install --yes --locked + # go: backend tools compile from source and can't produce lockfile URLs, + # so --locked always fails for them. Install in two passes: + # 1) all lockable tools with --locked (no GitHub API calls) + # 2) go: tools without --locked (they use Go module proxy, not GitHub API) + MISE_DISABLE_BACKENDS=go devbase_mise install --yes --locked + devbase_mise install --yes } # The current version of mise. From 2709bb451fa7ce030316d19d71ab921d90b2a177 Mon Sep 17 00:00:00 2001 From: Palo Otcenas Date: Fri, 27 Mar 2026 00:07:17 +0100 Subject: [PATCH 6/9] fix: use MISE_DISABLE_TOOLS for go: backend locked mode workaround MISE_DISABLE_BACKENDS=go doesn't work for explicitly declared tools (mise bug: get() in backend/mod.rs bypasses the disable_backends filter by re-creating backends on demand). Use MISE_DISABLE_TOOLS with dynamically extracted go: tool names from mise.devbase.toml. --- shell/lib/mise.sh | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/shell/lib/mise.sh b/shell/lib/mise.sh index 7da38e29..6f5af73b 100644 --- a/shell/lib/mise.sh +++ b/shell/lib/mise.sh @@ -427,10 +427,17 @@ devbase_install_mise_tools() { mise settings set experimental true fi # go: backend tools compile from source and can't produce lockfile URLs, - # so --locked always fails for them. Install in two passes: - # 1) all lockable tools with --locked (no GitHub API calls) - # 2) go: tools without --locked (they use Go module proxy, not GitHub API) - MISE_DISABLE_BACKENDS=go devbase_mise install --yes --locked + # so --locked always fails for them (mise bug: go backend doesn't override + # supports_lockfile_url). MISE_DISABLE_BACKENDS=go also doesn't work for + # explicitly declared tools (mise bug: get() bypasses the filter). + # Workaround: use MISE_DISABLE_TOOLS with the actual go: tool names. + local devbaseDir + devbaseDir="$(get_devbase_directory)" + local go_tools + go_tools=$(grep '^"go:' "$devbaseDir/mise.devbase.toml" | cut -d'"' -f2 | paste -sd, -) + # Pass 1: all lockable tools with --locked (no GitHub API calls) + MISE_DISABLE_TOOLS="$go_tools" devbase_mise install --yes --locked + # Pass 2: go: tools without --locked (they use Go module proxy, not GitHub API) devbase_mise install --yes } From 37ddba7dcac71e31dab9e3dc482cd1336457d272 Mon Sep 17 00:00:00 2001 From: Palo Otcenas Date: Fri, 27 Mar 2026 00:10:22 +0100 Subject: [PATCH 7/9] fix: declare and assign separately to satisfy shellcheck SC2155 --- shell/ci/env/mise.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/shell/ci/env/mise.sh b/shell/ci/env/mise.sh index ebeb47d6..d5472206 100755 --- a/shell/ci/env/mise.sh +++ b/shell/ci/env/mise.sh @@ -40,7 +40,8 @@ ghToken="$(gh auth token)" # --locked to fail. _MISE_INSTALL_CONFIG_DIR is picked up by run_mise() and # applied only to the mise binary invocation, so shims (e.g. # wait-for-gh-rate-limit) still resolve against the real global config. -export _MISE_INSTALL_CONFIG_DIR="$(mktemp -d)" +_MISE_INSTALL_CONFIG_DIR="$(mktemp -d)" +export _MISE_INSTALL_CONFIG_DIR # TODO(malept): feature parity with asdf.sh in the same folder. if [[ -f "$repoDir"/mise.toml ]]; then From a292453506e76d1300e637cce27ee1b0c1c1494a Mon Sep 17 00:00:00 2001 From: Palo Otcenas Date: Fri, 27 Mar 2026 00:15:03 +0100 Subject: [PATCH 8/9] style: remove unnecessary quotes in [[ -n ]] for shellfmt --- shell/lib/mise.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/lib/mise.sh b/shell/lib/mise.sh index 6f5af73b..5ff5d2d9 100644 --- a/shell/lib/mise.sh +++ b/shell/lib/mise.sh @@ -257,7 +257,7 @@ run_mise() { # _MISE_INSTALL_CONFIG_DIR, when set, overrides MISE_CONFIG_DIR only for # the mise binary — not for helper tools like wait-for-gh-rate-limit # that run via mise shims and need the real global config. - if [[ -n "${_MISE_INSTALL_CONFIG_DIR:-}" ]]; then + if [[ -n ${_MISE_INSTALL_CONFIG_DIR:-} ]]; then MISE_OVERRIDE_TOOL_VERSIONS_FILENAMES="$tool_versions_override" \ MISE_CONFIG_DIR="$_MISE_INSTALL_CONFIG_DIR" \ "$mise_path" "$@" From b9be3991496c5339e9e2d8a90617a1e413b8b33f Mon Sep 17 00:00:00 2001 From: Palo Otcenas Date: Fri, 27 Mar 2026 09:34:55 +0100 Subject: [PATCH 9/9] fix(ci): disable lockfile writes in pass 2 of mise install Pass 2 runs without --locked for go: backend tools (they compile from source and can't use lockfiles). Without MISE_LOCKFILE=false, mise could still write to mise.lock during this pass, causing dirty-tree failures in the pre-release dry run step. Pass 1 (--locked) is already read-only by definition. This ensures neither pass can mutate lockfiles in CI. --- shell/lib/mise.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/shell/lib/mise.sh b/shell/lib/mise.sh index 5ff5d2d9..f17ba15e 100644 --- a/shell/lib/mise.sh +++ b/shell/lib/mise.sh @@ -437,8 +437,9 @@ devbase_install_mise_tools() { go_tools=$(grep '^"go:' "$devbaseDir/mise.devbase.toml" | cut -d'"' -f2 | paste -sd, -) # Pass 1: all lockable tools with --locked (no GitHub API calls) MISE_DISABLE_TOOLS="$go_tools" devbase_mise install --yes --locked - # Pass 2: go: tools without --locked (they use Go module proxy, not GitHub API) - devbase_mise install --yes + # Pass 2: go: tools without --locked (they use Go module proxy, not GitHub API). + # MISE_LOCKFILE=false prevents lockfile writes — CI must never mutate lockfiles. + MISE_LOCKFILE=false devbase_mise install --yes } # The current version of mise.