From a91616cbed90cfed1b4b3c175d89b6beb8441ca6 Mon Sep 17 00:00:00 2001 From: Ryan Hughes Date: Sun, 18 Oct 2026 13:07:05 -0400 Subject: [PATCH 1/3] Install Node lazily through mise instead of during setup --- bin/omarchy-install-dev-env | 8 ++- bin/omarchy-provision-owner | 12 ++-- bin/omarchy-system-factory-reset | 8 --- etc/mise/conf.d/omarchy-tools.toml | 1 + install/user/mise-work.sh | 43 ------------ migrations/1792343099.sh | 10 +++ test/shell.d/default-agent-test.sh | 8 ++- test/shell.d/factory-reset-accounts-test.sh | 6 +- test/shell.d/mise-node-dev-env-test.sh | 49 +++++++++++++ test/shell.d/mise-node-migration-test.sh | 77 +++++++++++++++++++++ test/shell.d/mise-work-path-test.sh | 50 +------------ 11 files changed, 157 insertions(+), 115 deletions(-) create mode 100644 migrations/1792343099.sh create mode 100644 test/shell.d/mise-node-dev-env-test.sh create mode 100644 test/shell.d/mise-node-migration-test.sh diff --git a/bin/omarchy-install-dev-env b/bin/omarchy-install-dev-env index 4d9c8164eac..3bfb9e6086e 100755 --- a/bin/omarchy-install-dev-env +++ b/bin/omarchy-install-dev-env @@ -20,7 +20,13 @@ install_php() { install_node() { echo -e "Installing Node.js...\n" - mise use --global node + # Omarchy declares Node as a default tool. A user who removed the preinstalls, + # like one without Omarchy's declarations, selects it in their own config. + if omarchy-mise-default-tools declares node; then + mise install node + else + mise use --global node + fi } case "$1" in diff --git a/bin/omarchy-provision-owner b/bin/omarchy-provision-owner index fd8fe2f5d99..0e511f840f0 100755 --- a/bin/omarchy-provision-owner +++ b/bin/omarchy-provision-owner @@ -7,11 +7,11 @@ # Runs on tty1 via omarchy-provision-owner.service when /var/lib/omarchy/provisioning/pending # exists — the state a deferred-provisioning ISO install or omarchy-system-factory-reset leaves # behind. Asks for the user (the configurator's Step 2), creates it with the -# groups system setup recorded, finalizes it offline from the stashed Node -# tarball, re-keys LUKS from the throwaway install passphrase to the user's -# password, and hands off to SDDM. A platform whose boot package owns its boot -# chain prepares it, drops the install key's unlock from it and records the -# kept key slot through omarchy-lifecycle-dispatch. +# groups system setup recorded, finalizes it offline, re-keys LUKS from the +# throwaway install passphrase to the user's password, and hands off to SDDM. +# A platform whose boot package owns its boot chain prepares it, drops the +# install key's unlock from it and records the kept key slot through +# omarchy-lifecycle-dispatch. set -euo pipefail @@ -1123,8 +1123,6 @@ reset_limine_config() { } cleanup_oem_state() { - # Keep groups + packages: omarchy-system-factory-reset stages the bundled Node - # tarball from these live copies when the factory snapshot predates it. rm -f "$PROVISIONING_DIR/pending" "$PROVISIONING_DIR/authorized_keys" "$PROVISIONING_DIR/setup-user" "$REKEY_STATE" rm -f /etc/systemd/system/multi-user.target.wants/omarchy-provision-owner.service diff --git a/bin/omarchy-system-factory-reset b/bin/omarchy-system-factory-reset index 1b30e3e3d99..22bab112689 100755 --- a/bin/omarchy-system-factory-reset +++ b/bin/omarchy-system-factory-reset @@ -518,14 +518,6 @@ stage_full_reset() { # reset would restore that account. Scrub it once, in place. sanitize_factory_baseline "$top/@factory" - # Keep the Node tarball reachable for offline first-boot finalization. - if ! compgen -G "$next$PROVISIONING_DIR/packages/node-v*.tar.gz" >/dev/null; then - if compgen -G "$PROVISIONING_DIR/packages/node-v*.tar.gz" >/dev/null; then - install -d -m 755 "$next$PROVISIONING_DIR/packages" - cp "$PROVISIONING_DIR"/packages/node-v*.tar.gz "$next$PROVISIONING_DIR/packages/" - fi - fi - install -d -m 755 "$next$PROVISIONING_DIR" touch "$next$PROVISIONING_DIR/pending" "$next$PROVISIONING_DIR/wipe-pending" diff --git a/etc/mise/conf.d/omarchy-tools.toml b/etc/mise/conf.d/omarchy-tools.toml index fdb0cdbede6..63a271caf4f 100644 --- a/etc/mise/conf.d/omarchy-tools.toml +++ b/etc/mise/conf.d/omarchy-tools.toml @@ -20,6 +20,7 @@ grok = { version = "latest", lazy = true, auto_update = "4h", minimum_release_ag hey-cli = { version = "latest", lazy = true, minimum_release_age = "0s" } hunk = { version = "latest", lazy = true, minimum_release_age = "0s" } muse = { version = "latest", lazy = true, auto_update = "4h", minimum_release_age = "0s" } +node = { version = "latest", lazy = true, minimum_release_age = "0s" } "npm:@moonshot-ai/kimi-code" = { version = "latest", lazy = true, auto_update = "4h", lazy_bins = ["kimi"], minimum_release_age = "0s" } "npm:cf" = { version = "latest", lazy = true, lazy_bins = ["cf"], minimum_release_age = "0s" } oh-my-pi = { version = "latest", lazy = true, auto_update = "4h", minimum_release_age = "0s" } diff --git a/install/user/mise-work.sh b/install/user/mise-work.sh index 17479d0f4be..36fe72e37c4 100644 --- a/install/user/mise-work.sh +++ b/install/user/mise-work.sh @@ -1,46 +1,3 @@ # Setup default work directory (and tries) mkdir -p "$HOME/Work" mkdir -p "$HOME/Work/tries" - -# Offline installs unpack the Node tarball bundled by the ISO: from -# /opt/packages in the ISO chroot, or from the copy staged in provisioning state when -# omarchy-provision-owner finalizes the user at first boot. -case ${OMARCHY_SETUP_CONTEXT:-runtime} in - iso-chroot) NODE_PACKAGE_DIR=/opt/packages ;; - provision-owner) NODE_PACKAGE_DIR=/var/lib/omarchy/provisioning/packages ;; - *) NODE_PACKAGE_DIR="" ;; -esac - -if [[ -n $NODE_PACKAGE_DIR ]]; then - # The ISO bundles the tarball for its own architecture. - case $(uname -m) in - aarch64) NODE_PLATFORM=linux-arm64 ;; - *) NODE_PLATFORM=linux-x64 ;; - esac - NODE_TARBALL=$(find "$NODE_PACKAGE_DIR" -name "node-v*-$NODE_PLATFORM.tar.gz" -type f 2>/dev/null | head -n1) - if [[ -z $NODE_TARBALL ]]; then - if [[ ${OMARCHY_SETUP_CONTEXT:-} == "provision-owner" ]]; then - # A factory snapshot predating the bundled tarball may not have it staged. - # Leave Node to the network rather than failing the whole first boot. - echo "Warning: no bundled Node.js tarball in $NODE_PACKAGE_DIR; trying the network" >&2 - mise use -g node@latest || echo "Warning: Node.js install deferred (no network)" >&2 - else - echo "Error: bundled Node.js tarball missing from $NODE_PACKAGE_DIR" >&2 - exit 1 - fi - else - NODE_VERSION=$(basename "$NODE_TARBALL" | sed "s/node-v\(.*\)-$NODE_PLATFORM.tar.gz/\1/") - NODE_INSTALL_DIR="$HOME/.local/share/mise/installs/node/$NODE_VERSION" - - mkdir -p "$NODE_INSTALL_DIR" - tar -xzf "$NODE_TARBALL" --strip-components=1 -C "$NODE_INSTALL_DIR" - mise use -g node@"$NODE_VERSION" - - # That pinned the exact bundled version, which would exempt Node from - # mise up forever. Loosen it to latest, like an online install gets: - # mise resolves latest to the installed version while offline. - mise config set tools.node latest --file "$HOME/.config/mise/config.toml" - fi -else - mise use -g node@latest -fi diff --git a/migrations/1792343099.sh b/migrations/1792343099.sh new file mode 100644 index 00000000000..9e6dc196bab --- /dev/null +++ b/migrations/1792343099.sh @@ -0,0 +1,10 @@ +echo "Leave Node to Omarchy's mise declaration instead of the user's config" + +# Setup used to write `node = "latest"` into the user's mise config. Node now +# ships as a lazy default in /etc/mise/conf.d, and that entry would outrank it. +# Drop only the exact latest request, since a pinned version is the user's +# choice, and keep the install for the declaration to reuse. After an opt-out +# the entry is what still provides Node, so it stays. +if omarchy-mise-default-tools declares node; then + mise unuse -g --no-prune node@latest +fi diff --git a/test/shell.d/default-agent-test.sh b/test/shell.d/default-agent-test.sh index 7c27c79b778..6bfeb171144 100644 --- a/test/shell.d/default-agent-test.sh +++ b/test/shell.d/default-agent-test.sh @@ -184,9 +184,11 @@ for tool in codex claude copilot opencode pi "\"$kimi_package\""; do fail "user setup declares $tool as a self-updating native lazy tool" done [[ $(grep -c 'auto_update = "4h"' "$lazy_config") == 12 ]] || fail "only the agent CLIs update themselves" -grep -Eq '^uv = \{ version = "latest", lazy = true, minimum_release_age = "0s" \}$' "$lazy_config" || - fail "user setup declares uv as a native lazy tool" -[[ $(grep -c 'lazy = true' "$lazy_config") == 21 ]] || fail "user setup declares every default mise tool as lazy" +for tool in node uv; do + grep -Eq "^$tool = \\{ version = \"latest\", lazy = true, minimum_release_age = \"0s\" \\}$" "$lazy_config" || + fail "user setup declares $tool as a native lazy tool" +done +[[ $(grep -c 'lazy = true' "$lazy_config") == 22 ]] || fail "user setup declares every default mise tool as lazy" grep -Fx "reshim --system" "$mise_history" >/dev/null || fail "user setup builds the system lazy bootstrap shims" pass "user setup configures system lazy tools from registry shorthands" : >"$stub_log" diff --git a/test/shell.d/factory-reset-accounts-test.sh b/test/shell.d/factory-reset-accounts-test.sh index d773f2f6621..1d26fe5bfad 100644 --- a/test/shell.d/factory-reset-accounts-test.sh +++ b/test/shell.d/factory-reset-accounts-test.sh @@ -72,11 +72,9 @@ make_fixture() { local top="$1" root_hash="${2:-original-root-hash}" local factory="$top/@factory" mkdir -p "$top/@" "$factory/etc" "$factory/home/seller" \ - "$factory/usr/bin" "$factory/usr/share/omarchy/install/provisioning" \ - "$factory/var/lib/omarchy/provisioning/packages" + "$factory/usr/bin" "$factory/usr/share/omarchy/install/provisioning" touch "$top/@/old-system" "$factory/home/seller/private-file" \ - "$factory/usr/share/omarchy/install/provisioning/omarchy-provision-owner.service" \ - "$factory/var/lib/omarchy/provisioning/packages/node-v0.tar.gz" + "$factory/usr/share/omarchy/install/provisioning/omarchy-provision-owner.service" printf '#!/bin/bash\n' >"$factory/usr/bin/omarchy-provision-owner" chmod +x "$factory/usr/bin/omarchy-provision-owner" printf 'true\n' >"$factory/read-only" diff --git a/test/shell.d/mise-node-dev-env-test.sh b/test/shell.d/mise-node-dev-env-test.sh new file mode 100644 index 00000000000..aaf23867423 --- /dev/null +++ b/test/shell.d/mise-node-dev-env-test.sh @@ -0,0 +1,49 @@ +#!/bin/bash + +set -euo pipefail + +source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh" + +test_tmp=$(mktemp -d) +trap 'rm -rf "$test_tmp"' EXIT + +mock_bin="$test_tmp/bin" +test_home="$test_tmp/home" +mise_log="$test_tmp/mise" +mkdir -p "$mock_bin" "$test_home" + +cat >"$mock_bin/mise" <<'SH' +#!/bin/bash +printf '%s\n' "$*" >>"$OMARCHY_TEST_MISE_LOG" +SH +chmod +x "$mock_bin/mise" + +export HOME="$test_home" +export OMARCHY_TEST_MISE_LOG="$mise_log" +export PATH="$mock_bin:$ROOT/bin:$PATH" + +mise_config="$test_tmp/etc/mise/config.toml" +mkdir -p "$(dirname "$mise_config")" +cp "$ROOT/etc/mise/conf.d/omarchy-tools.toml" "$mise_config" +export OMARCHY_MISE_CONFIG_PATH="$mise_config" + +bash "$ROOT/bin/omarchy-install-dev-env" node >/dev/null +[[ $(<"$mise_log") == "install node" ]] || fail "Node setup installs the system-configured Node and leaves the user config alone" +pass "Node setup installs Omarchy's declared Node" + +# mise skips Omarchy's declarations for a user who removed the preinstalls, so +# Node goes in their own config, leaving the opt-out in place. +miserc="$test_home/.config/mise/miserc.toml" +mkdir -p "$(dirname "$miserc")" +printf 'ignored_config_paths = ["%s"]\n' "$mise_config" >"$miserc" +: >"$mise_log" +bash "$ROOT/bin/omarchy-install-dev-env" node >/dev/null +[[ $(<"$mise_log") == "use --global node" ]] || fail "Node setup selects Node after Remove Preinstalls" +rm "$miserc" +pass "Node setup selects Node after Remove Preinstalls" + +rm "$mise_config" +: >"$mise_log" +bash "$ROOT/bin/omarchy-install-dev-env" node >/dev/null +[[ $(<"$mise_log") == "use --global node" ]] || fail "Node setup selects Node when no system config declares it" +pass "Node setup selects Node without Omarchy's declarations" diff --git a/test/shell.d/mise-node-migration-test.sh b/test/shell.d/mise-node-migration-test.sh new file mode 100644 index 00000000000..d18be7cd2eb --- /dev/null +++ b/test/shell.d/mise-node-migration-test.sh @@ -0,0 +1,77 @@ +#!/bin/bash + +set -euo pipefail + +source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh" + +require_command mise + +test_tmp=$(mktemp -d) +trap 'rm -rf "$test_tmp"' EXIT +migration="$ROOT/migrations/1792343099.sh" + +# The settings package ships the declarations before migrations run. +system_dir="$test_tmp/etc/mise" +mkdir -p "$system_dir" +cp -r "$ROOT/etc/mise/conf.d" "$system_dir/" + +run_migration() { + local test_home="$1" + + ( + cd "$test_home" + env -i \ + HOME="$test_home" \ + XDG_CACHE_HOME="$test_home/.cache" \ + XDG_CONFIG_HOME="$test_home/.config" \ + XDG_DATA_HOME="$test_home/.local/share" \ + XDG_STATE_HOME="$test_home/.local/state" \ + MISE_SYSTEM_DIR="$system_dir" \ + OMARCHY_MISE_CONFIG_PATH="$system_dir/conf.d/omarchy-tools.toml" \ + PATH="$ROOT/bin:/usr/bin" \ + bash -euo pipefail "$migration" + ) +} + +new_home() { + local test_home="$test_tmp/$1" + mkdir -p "$test_home/.config/mise" + printf '%s' "$2" >"$test_home/.config/mise/config.toml" + echo "$test_home" +} + +legacy_home=$(new_home legacy $'[tools]\nnode = "latest"\nruby = "latest"\n\n[settings.upgrade]\nauto_prune = false\n') +legacy_node="$legacy_home/.local/share/mise/installs/node/26.12.0/bin/node" +mkdir -p "$(dirname "$legacy_node")" +touch "$legacy_node" +run_migration "$legacy_home" >/dev/null +legacy_config="$legacy_home/.config/mise/config.toml" +grep -q '^node' "$legacy_config" && fail "migration drops the node entry setup wrote" +grep -Fx 'ruby = "latest"' "$legacy_config" >/dev/null || fail "migration keeps the user's other tools" +grep -Fx 'auto_prune = false' "$legacy_config" >/dev/null || fail "migration leaves the user's settings alone" +[[ -e $legacy_node ]] || fail "migration keeps the Node install for the declaration to reuse" +cp "$legacy_config" "$test_tmp/legacy-after" +run_migration "$legacy_home" >/dev/null +cmp -s "$test_tmp/legacy-after" "$legacy_config" || fail "migration is idempotent" +pass "migration hands the latest Node over to Omarchy's declaration" + +pinned_home=$(new_home pinned $'[tools]\nnode = "22"\n') +cp "$pinned_home/.config/mise/config.toml" "$test_tmp/pinned-before" +run_migration "$pinned_home" >/dev/null +cmp -s "$test_tmp/pinned-before" "$pinned_home/.config/mise/config.toml" || fail "migration keeps a pinned Node" +pass "migration keeps a Node version the user pinned" + +# After Remove Preinstalls nothing else provides Node. +opted_out_home=$(new_home opted-out $'[tools]\nnode = "latest"\n') +printf 'ignored_config_paths = ["%s"]\n' "$system_dir/conf.d/omarchy-tools.toml" >"$opted_out_home/.config/mise/miserc.toml" +cp "$opted_out_home/.config/mise/config.toml" "$test_tmp/opted-out-before" +run_migration "$opted_out_home" >/dev/null +cmp -s "$test_tmp/opted-out-before" "$opted_out_home/.config/mise/config.toml" || + fail "migration keeps Node for a user who removed the preinstalls" +pass "migration keeps Node after Remove Preinstalls" + +empty_home="$test_tmp/empty" +mkdir -p "$empty_home" +run_migration "$empty_home" >/dev/null || fail "migration succeeds without a user mise config" +[[ ! -e $empty_home/.config/mise/config.toml ]] || fail "migration does not create a user mise config" +pass "migration leaves a missing user config missing" diff --git a/test/shell.d/mise-work-path-test.sh b/test/shell.d/mise-work-path-test.sh index f98fe6b94b6..5fd0390efdf 100644 --- a/test/shell.d/mise-work-path-test.sh +++ b/test/shell.d/mise-work-path-test.sh @@ -97,57 +97,9 @@ env \ [[ -d $install_home/Work/tries ]] || fail "installer creates the work and tries directories" [[ ! -e $install_home/Work/.mise.toml ]] || fail "installer does not create a trusted Work Mise config" -[[ $(<"$install_log") == "use -g node@latest" ]] || fail "installer only invokes Mise for the global Node setup" +[[ ! -e $install_log ]] || fail "installer leaves Node to the lazy system declaration" pass "new installs do not add project bin directories to PATH" -# An offline install unpacks the Node tarball the ISO bundles, and the ISO -# bundles the one for its own architecture. Both are on offer here; each -# machine must take its own. uname and the bundle directory are stand-ins. -bundle="$test_dir/bundle" -mkdir -p "$bundle" "$test_dir/node-src" -for platform in linux-x64 linux-arm64; do - mkdir -p "$test_dir/node-src/node-v24.1.0-$platform/bin" - echo "$platform" >"$test_dir/node-src/node-v24.1.0-$platform/bin/node" - tar -czf "$bundle/node-v24.1.0-$platform.tar.gz" -C "$test_dir/node-src" "node-v24.1.0-$platform" -done -cat >"$test_dir/bin/uname" <<'SH' -#!/bin/bash -[[ $* == -m ]] && { echo "$UNAME_TEST_MACHINE"; exit; } -exec /usr/bin/uname "$@" -SH -cat >"$test_dir/bin/find" <<'SH' -#!/bin/bash -[[ $1 == /opt/packages ]] && { shift; exec /usr/bin/find "$NODE_TEST_BUNDLE" "$@"; } -exec /usr/bin/find "$@" -SH -chmod +x "$test_dir/bin/uname" "$test_dir/bin/find" - -for case in "x86_64 linux-x64" "aarch64 linux-arm64"; do - read -r machine platform <<<"$case" - arch_home="$test_dir/$machine-home" - arch_log="$test_dir/$machine-mise.log" - mkdir -p "$arch_home" - env \ - HOME="$arch_home" \ - MISE_TEST_LOG="$arch_log" \ - NODE_TEST_BUNDLE="$bundle" \ - UNAME_TEST_MACHINE="$machine" \ - OMARCHY_SETUP_CONTEXT=iso-chroot \ - PATH="$test_dir/bin:/usr/bin" \ - bash -euo pipefail -c 'source "$1"' bash "$ROOT/install/user/mise-work.sh" - - installed="$arch_home/.local/share/mise/installs/node/24.1.0/bin/node" - [[ -f $installed && $(<"$installed") == "$platform" ]] || fail "$machine unpacks the $platform Node tarball" - [[ $(head -n1 "$arch_log") == "use -g node@24.1.0" ]] || fail "$machine hands Mise the bundled Node version" -done -[[ ! -e $bundle/node-v24.1.0-linux-x64.tar.gz ]] || rm "$bundle/node-v24.1.0-linux-x64.tar.gz" -if env HOME="$test_dir/missing-home" MISE_TEST_LOG="$test_dir/missing.log" NODE_TEST_BUNDLE="$bundle" \ - UNAME_TEST_MACHINE=x86_64 OMARCHY_SETUP_CONTEXT=iso-chroot PATH="$test_dir/bin:/usr/bin" \ - bash -euo pipefail -c 'source "$1"' bash "$ROOT/install/user/mise-work.sh" 2>/dev/null; then - fail "an x86_64 install accepts another architecture's Node tarball" -fi -pass "offline installs unpack the Node tarball for the machine's architecture" - stock_home="$test_dir/stock-home" stock_config="$stock_home/Work/.mise.toml" stock_project="$stock_home/Work/tries/untrusted-repository" From 02da3adab54846eb9fe605e95d2229f596708b54 Mon Sep 17 00:00:00 2001 From: Ryan Hughes Date: Sun, 18 Oct 2026 13:07:46 -0400 Subject: [PATCH 2/3] Ship mise's upgrade pruning setting instead of writing the user's config --- agents/skills/mise-tools.md | 4 + etc/mise/conf.d/omarchy.toml | 5 ++ install/user/mise.sh | 4 - migrations/1792343225.sh | 11 +++ .../shell.d/mise-auto-prune-migration-test.sh | 81 +++++++++++++++++++ test/shell.d/mise-provision-test.sh | 9 ++- 6 files changed, 108 insertions(+), 6 deletions(-) create mode 100644 migrations/1792343225.sh create mode 100644 test/shell.d/mise-auto-prune-migration-test.sh diff --git a/agents/skills/mise-tools.md b/agents/skills/mise-tools.md index b2178b60795..beb919d257d 100644 --- a/agents/skills/mise-tools.md +++ b/agents/skills/mise-tools.md @@ -2,6 +2,10 @@ Read this before adding or changing a mise-managed tool. Default tools belong in `etc/mise/conf.d/omarchy-tools.toml`, which the settings package installs; `install/user/mise.sh` creates their native mise shims during user setup. +## Leave the user's config alone + +Omarchy does not write `~/.config/mise/config.toml`; it is the user's, for overriding what Omarchy ships. Settings go in `etc/mise/conf.d/omarchy.toml`, which still loads after a user opts out of the default tools, and tools in `omarchy-tools.toml`. A command that installs a default tool runs `mise install ` when `omarchy-mise-default-tools declares `, and `mise use -g` only for a user who opted out. `mise use -g` on a declared tool would copy the declaration's options, `lazy = true` included, into the user's config. + ## Declare a default tool Use a registry shorthand and set `lazy = true` so the first command invocation installs the tool. Follow the existing defaults' release policy: `minimum_release_age = "0s"` makes new releases eligible immediately. diff --git a/etc/mise/conf.d/omarchy.toml b/etc/mise/conf.d/omarchy.toml index c9354a2eda7..bfb886d9278 100644 --- a/etc/mise/conf.d/omarchy.toml +++ b/etc/mise/conf.d/omarchy.toml @@ -1,3 +1,8 @@ +# Upgrades must not delete the version a running process is executing from: +# mise up would prune the old install dir out from under a live session. +[settings] +upgrade.auto_prune = false + # Keep Cursor optional while applying these options whenever mise installs it. # Expose only its launcher so the bundled Node cannot shadow the user's Node. [tool_alias] diff --git a/install/user/mise.sh b/install/user/mise.sh index 202a170ad5b..4256020cbd3 100644 --- a/install/user/mise.sh +++ b/install/user/mise.sh @@ -1,7 +1,3 @@ -# Upgrades must not delete the version a running process is executing from: -# mise up would prune the old install dir out from under a live session. -mise settings set upgrade.auto_prune false - # Build the lazy shims for Omarchy's default tools and the account dispatchers, # both declared by /etc/mise/conf.d. mise reshim --system diff --git a/migrations/1792343225.sh b/migrations/1792343225.sh new file mode 100644 index 00000000000..2a8f15f78fc --- /dev/null +++ b/migrations/1792343225.sh @@ -0,0 +1,11 @@ +echo "Leave mise's upgrade pruning setting to Omarchy's config instead of the user's" + +# Setup and an earlier migration wrote upgrade.auto_prune = false into the +# user's mise config. The settings package ships it in /etc/mise/conf.d now, so +# that copy only clutters a file that is the user's. A different value is the +# user's choice and stays. +mise_config="$HOME/.config/mise/config.toml" + +if [[ -f $mise_config ]] && [[ $(mise config get -f "$mise_config" settings.upgrade.auto_prune 2>/dev/null) == "false" ]]; then + mise settings unset upgrade.auto_prune +fi diff --git a/test/shell.d/mise-auto-prune-migration-test.sh b/test/shell.d/mise-auto-prune-migration-test.sh new file mode 100644 index 00000000000..9883ad2dbac --- /dev/null +++ b/test/shell.d/mise-auto-prune-migration-test.sh @@ -0,0 +1,81 @@ +#!/bin/bash + +set -euo pipefail + +source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh" + +require_command mise + +test_tmp=$(mktemp -d) +trap 'rm -rf "$test_tmp"' EXIT +migration="$ROOT/migrations/1792343225.sh" + +# The settings package ships the setting before migrations run. +system_dir="$test_tmp/etc/mise" +mkdir -p "$system_dir" +cp -r "$ROOT/etc/mise/conf.d" "$system_dir/" + +in_home() { + local test_home="$1" + shift + + ( + cd "$test_home" + env -i \ + HOME="$test_home" \ + XDG_CACHE_HOME="$test_home/.cache" \ + XDG_CONFIG_HOME="$test_home/.config" \ + XDG_DATA_HOME="$test_home/.local/share" \ + XDG_STATE_HOME="$test_home/.local/state" \ + MISE_SYSTEM_DIR="$system_dir" \ + PATH="$ROOT/bin:/usr/bin" \ + "$@" + ) +} + +new_home() { + local test_home="$test_tmp/$1" + mkdir -p "$test_home/.config/mise" + printf '%s' "$2" >"$test_home/.config/mise/config.toml" + echo "$test_home" +} + +legacy_home=$(new_home legacy $'[tools]\nruby = "latest"\n\n[settings]\nminimum_release_age = "0s"\n\n[settings.upgrade]\nauto_prune = false\n') +legacy_config="$legacy_home/.config/mise/config.toml" +in_home "$legacy_home" bash -euo pipefail "$migration" >/dev/null +grep -q 'auto_prune' "$legacy_config" && fail "migration drops the setting setup wrote" +grep -Fx 'ruby = "latest"' "$legacy_config" >/dev/null || fail "migration keeps the user's tools" +grep -Fx 'minimum_release_age = "0s"' "$legacy_config" >/dev/null || fail "migration keeps the user's other settings" +[[ $(in_home "$legacy_home" mise settings get upgrade.auto_prune 2>/dev/null) == "false" ]] || + fail "upgrades still keep versions in use once the user's copy is gone" +cp "$legacy_config" "$test_tmp/legacy-after" +in_home "$legacy_home" bash -euo pipefail "$migration" >/dev/null +cmp -s "$test_tmp/legacy-after" "$legacy_config" || fail "migration is idempotent" +pass "migration hands upgrade pruning over to Omarchy's config" + +dotted_home=$(new_home dotted $'[settings]\nupgrade.auto_prune = false\n') +in_home "$dotted_home" bash -euo pipefail "$migration" >/dev/null +grep -q 'auto_prune' "$dotted_home/.config/mise/config.toml" && fail "migration drops a dotted-key copy of the setting" +pass "migration finds the setting in either TOML form" + +chosen_home=$(new_home chosen $'[settings.upgrade]\nauto_prune = true\n') +cp "$chosen_home/.config/mise/config.toml" "$test_tmp/chosen-before" +in_home "$chosen_home" bash -euo pipefail "$migration" >/dev/null +cmp -s "$test_tmp/chosen-before" "$chosen_home/.config/mise/config.toml" || fail "migration keeps a value the user chose" +pass "migration keeps a pruning choice the user made" + +# A config kept with dotfiles is often a link into the user's repository. +linked_home=$(new_home linked "") +dotfile="$test_tmp/dotfiles-mise.toml" +printf '[settings.upgrade]\nauto_prune = false\n' >"$dotfile" +ln -sf "$dotfile" "$linked_home/.config/mise/config.toml" +in_home "$linked_home" bash -euo pipefail "$migration" >/dev/null +[[ -L $linked_home/.config/mise/config.toml ]] || fail "migration preserves a dotfile symlink" +grep -q 'auto_prune' "$dotfile" && fail "migration edits the dotfile the link points to" +pass "migration edits a linked config in place" + +empty_home="$test_tmp/empty" +mkdir -p "$empty_home" +in_home "$empty_home" bash -euo pipefail "$migration" >/dev/null || fail "migration succeeds without a user mise config" +[[ ! -e $empty_home/.config/mise/config.toml ]] || fail "migration does not create a user mise config" +pass "migration leaves a missing user config missing" diff --git a/test/shell.d/mise-provision-test.sh b/test/shell.d/mise-provision-test.sh index ac063b98116..c2ccd4f1dbc 100644 --- a/test/shell.d/mise-provision-test.sh +++ b/test/shell.d/mise-provision-test.sh @@ -21,6 +21,11 @@ for agent in kimi cursor-agent muse; do [[ ! -e $HOME/.local/bin/$agent ]] || fail "provisioning must not create a handwritten $agent wrapper" done mapfile -d '' -t args <"$OMARCHY_TEST_MISE_LOG" -[[ ${args[*]} == "settings set upgrade.auto_prune false reshim --system reshim" ]] || - fail "provisioning builds the lazy shims without installing packages" +[[ ${args[*]} == "reshim --system reshim" ]] || + fail "provisioning builds the lazy shims without installing packages or writing the user's config" pass "provisioning leaves lazy Kimi, Cursor, and Muse shims to mise" + +# The settings package ships this, so the user's config stays theirs. +grep -Fx 'upgrade.auto_prune = false' "$ROOT/etc/mise/conf.d/omarchy.toml" >/dev/null || + fail "Omarchy's mise config keeps upgrades from pruning versions still in use" +pass "upgrade pruning is off in the package-owned mise config" From 310fd8d8f62e3d375f5c5b3345ca9d1ce3e8c48a Mon Sep 17 00:00:00 2001 From: Ryan Hughes Date: Sun, 18 Oct 2026 16:02:32 -0400 Subject: [PATCH 3/3] Find the user's mise config where mise keeps it --- migrations/1792343225.sh | 5 +++-- test/shell.d/mise-auto-prune-migration-test.sh | 9 +++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/migrations/1792343225.sh b/migrations/1792343225.sh index 2a8f15f78fc..77bedc1a9da 100644 --- a/migrations/1792343225.sh +++ b/migrations/1792343225.sh @@ -3,8 +3,9 @@ echo "Leave mise's upgrade pruning setting to Omarchy's config instead of the us # Setup and an earlier migration wrote upgrade.auto_prune = false into the # user's mise config. The settings package ships it in /etc/mise/conf.d now, so # that copy only clutters a file that is the user's. A different value is the -# user's choice and stays. -mise_config="$HOME/.config/mise/config.toml" +# user's choice and stays. The file is mise's global config, found the way +# mise finds it, since that's where `mise settings set` wrote. +mise_config=${MISE_GLOBAL_CONFIG_FILE:-${MISE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/mise}/config.toml} if [[ -f $mise_config ]] && [[ $(mise config get -f "$mise_config" settings.upgrade.auto_prune 2>/dev/null) == "false" ]]; then mise settings unset upgrade.auto_prune diff --git a/test/shell.d/mise-auto-prune-migration-test.sh b/test/shell.d/mise-auto-prune-migration-test.sh index 9883ad2dbac..45a33cf06e0 100644 --- a/test/shell.d/mise-auto-prune-migration-test.sh +++ b/test/shell.d/mise-auto-prune-migration-test.sh @@ -58,6 +58,15 @@ in_home "$dotted_home" bash -euo pipefail "$migration" >/dev/null grep -q 'auto_prune' "$dotted_home/.config/mise/config.toml" && fail "migration drops a dotted-key copy of the setting" pass "migration finds the setting in either TOML form" +# mise keeps its global config under XDG_CONFIG_HOME, so that's where setup wrote. +xdg_home=$(new_home xdg "") +xdg_config="$xdg_home/xdg/mise/config.toml" +mkdir -p "$(dirname "$xdg_config")" +printf '[settings.upgrade]\nauto_prune = false\n' >"$xdg_config" +in_home "$xdg_home" env XDG_CONFIG_HOME="$xdg_home/xdg" bash -euo pipefail "$migration" >/dev/null +grep -q 'auto_prune' "$xdg_config" && fail "migration drops the setting from a config under XDG_CONFIG_HOME" +pass "migration finds the config where XDG_CONFIG_HOME puts it" + chosen_home=$(new_home chosen $'[settings.upgrade]\nauto_prune = true\n') cp "$chosen_home/.config/mise/config.toml" "$test_tmp/chosen-before" in_home "$chosen_home" bash -euo pipefail "$migration" >/dev/null