Skip to content

Instantly share code, notes, and snippets.

@knu
Last active June 27, 2026 12:15
Show Gist options
  • Select an option

  • Save knu/86c3d1a2c4cbcf02b1d6306fdffd8ded to your computer and use it in GitHub Desktop.

Select an option

Save knu/86c3d1a2c4cbcf02b1d6306fdffd8ded to your computer and use it in GitHub Desktop.
Zero-downtime mise upgrade
#!/bin/sh
set -e
config=$HOME/.config/mise/config.toml
usage() {
echo "Usage: $0 [-p config] [tool...]"
}
current_tools() {
printf %s "$outdated" | jq -r \
'to_entries[] | select(.value.current != null) | "\(.key)@\(.value.current)"'
}
latest_tools() {
printf %s "$outdated" | jq -r \
'to_entries[] | "\(.key)@\(.value.latest)"'
}
new_latest_tools() {
latest_tools | while IFS= read -r spec; do
tool=${spec%@*}
version=${spec##*@}
if ! mise ls --json --installed "$tool" |
jq -e --arg version "$version" 'any(.[]; .version == $version)' >/dev/null; then
printf '%s\n' "$spec"
fi
done
}
pin_current_paths() {
[ -n "$current_specs" ] || return 0
# shellcheck disable=SC2016,SC2086
PATH=$(mise exec $current_specs -- sh -c 'printf %s "$PATH"')
export PATH
}
cleanup_new_latest_tools() {
[ -n "$new_latest_specs" ] || return 0
# shellcheck disable=SC2086
mise uninstall -y $new_latest_specs
}
while getopts "p:" opt; do
case $opt in
p)
config=$OPTARG
;;
\?)
usage
exit 1
;;
esac
done
shift $((OPTIND - 1))
config=$(realpath "$config")
config_bak=${config%.toml}.bak.toml
if [ -f "$config_bak" ]; then
echo "$config_bak exists; upgrade seems to be in progress."
exit 1
fi
cd "$(dirname "$config")"
# Get outdated tools.
outdated=$(mise outdated --json "$@")
if [ "$outdated" = '{}' ]; then
echo Nothing to upgrade.
exit
fi
current_specs=$(current_tools)
latest_specs=$(latest_tools)
# Prefer currently installed tool bins while new versions are being installed.
pin_current_paths
new_latest_specs=$(new_latest_tools)
# Pin the tools to the currently installed versions while new versions are being installed.
cp -p "$config" "$config_bak"
if [ -n "$current_specs" ]; then
# shellcheck disable=SC2086
mise use --path "$config" $current_specs
fi
# Install the latest versions. If this fails, remove any partial latest
# installs before restoring the config.
# shellcheck disable=SC2086
if mise install $latest_specs; then
mv "$config_bak" "$config"
else
ret=$?
if cleanup_new_latest_tools; then
mv "$config_bak" "$config"
else
echo "Upgrade cleanup failed; $config remains pinned to the previous versions." >&2
fi
exit "$ret"
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment