外观
用 mise 统一管理运行时版本与项目任务
为什么需要它
一台机器上同时有要 Node 18 的旧项目和要 Node 22 的新项目是常态;再算上 Python、Ruby,每种语言各装一个版本管理器(nvm、pyenv、rbenv……)之后,PATH 里堆了三四层 shim,某个命令解析到哪个版本、又是哪个工具在接管,出了问题很难归因。
mise 用一个工具和一种配置文件格式处理所有运行时:项目目录里放一份 mise.toml,声明这个项目要用的 Node、Python 版本,进这个目录版本自动切换,出去恢复成全局默认或别的项目的声明。同一份配置文件里还能顺带管环境变量、收纳常用命令,不用再分别记 .env、Makefile、package.json 的 scripts 段各自的语法。
开始之前
前置:使用 Homebrew 管理工具。
如果机器上已经有 nvm、pyenv、rbenv 之类的版本管理器,这是本文最需要提前交代的一步。 它们的原理是同一件事:把自己的 shim 目录塞进 PATH 靠前的位置,靠 shell 的启动脚本(通常是 .zshrc)里的一段初始化代码接管命令。装上 mise 之后,PATH 里会同时存在两套 shim,谁的初始化代码在 .zshrc 里排得靠后、谁的 shim 目录就更靠前、谁就接管——于是会出现「明明用 mise 切了版本,node -v 却没变」的情况,且现象和真实原因对不上号,容易越查越乱。
处理顺序:
打开
.zshrc(不确定这几个文件各管什么、加载顺序如何,参见zsh 启动文件与加载顺序),把旧工具的初始化行整体注释掉,不要删——保留原文方便一键恢复。常见写法形如:zsh# export NVM_DIR="$HOME/.nvm" # [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"重开一个终端窗口,确认旧工具确实已经不再接管:旧工具的命令(比如
nvm)应该提示未找到,或者which node不再指向旧工具的 shim 目录。确认干净之后,再按下一节把 mise 的激活命令加进去。
不要求立即卸载旧工具——先注释掉初始化行,mise 用顺手了再卸载也不迟;如果 mise 用不惯,把注释去掉、重开终端,就能一步退回原状。
操作步骤
1. 安装
macOS 上有两种常见方式。官方安装脚本是首选,会取到官方发布的二进制文件:
zsh
curl https://mise.run | sh装到 ~/.local/bin/mise。也可以用 Homebrew:
zsh
brew install mise官方文档注明 Homebrew 版是独立构建的产物,不是官方直接分发的二进制,「convenient, but it is not the preferred installation method」——两种方式的最终用法完全一样,选哪个看你更习惯用哪套安装体系维护它。
2. 加进 ~/.zshrc
zsh
echo 'eval "$(~/.local/bin/mise activate zsh)"' >> ~/.zshrc如果是用 Homebrew 装的,把路径换成 $(brew --prefix)/bin/mise(一般是 /opt/homebrew/bin/mise)。
为什么放 .zshrc 而不是 .zshenv: 激活命令要做的事——把 mise 的 shim 目录和当前生效的工具版本接进 PATH、注册每次换目录都要重新求值的 hook——只有交互式 shell 会用到:脚本、cron、ssh 主机 命令 这类非交互场景不需要、也不该背上这份初始化开销。这正是zsh 启动文件与加载顺序里讲的「按场景分文件」的一个具体应用。
写完重开一个终端窗口(或 source ~/.zshrc)让它生效,注意激活行只加一次,重复执行上面这条命令会在 .zshrc 里追加出重复的 hook。
3. 设一个全局默认版本
zsh
mise use --global python@3.12这条命令做两件事:如果本地没有这个版本会先下载安装,再把版本号写进全局配置 ~/.config/mise/config.toml。全局配置是兜底——但前提是全局配置里确实声明了这个工具;如果全局配置也没有声明它,会按下文「常见问题」第一条的 fallthrough 机制,交给 PATH 上排在后面的同名命令接管,不一定是你预期的版本。
4. 在项目目录里声明版本
进到一个项目目录,跑:
zsh
mise use python@3.12.13这会在当前目录生成(或更新)一个 mise.toml:
toml
[tools]
python = "3.12.13"mise use 默认就近找到最合适的配置文件写入——通常是当前目录的 mise.toml;如果目录里还没有任何 mise 配置,会新建一个。
mise use python@3.12 这种只写到次版本号的模糊写法,会被原样记进配置文件,每次实际使用时才解析成当时匹配到的最新版本——也就是说同一份配置在不同时间跑出来的具体版本可能不一样。想把版本钉死,就像上面那样把完整版本号写在 @ 后面。
5. 用 [env] 段管理项目环境变量
同一个 mise.toml 里加一段 [env],声明这个项目需要的环境变量:
toml
[env]
NODE_ENV = "development"激活状态下,进入这个目录时变量自动生效,离开时自动清除,不需要额外的 .env 加载逻辑。
6. 用 [tasks] 段收纳常用命令
toml
[tasks.test]
description = "跑项目测试"
run = "npm test"跑任务:
zsh
mise run test官方文档特别说明执行任务本身不要求先做 shell 激活(「Shell activation is not required」)——mise run 会自己按配置解析好环境再执行,适合写进 CI 脚本。
7. 自查
zsh
mise doctor只读诊断,不会改动任何状态。正常情况下会看到 activated: yes、shims_on_path: yes,以及当前生效的配置文件列表;如果这几项不对,回到第 2 步检查激活命令有没有真的写进 .zshrc 并生效。
检查结果
进出一个声明了版本的项目目录各查一次版本号,输出应当不同。下面是本机的一次真实取证(练习用的临时目录,跑完已删除,不是长期存在的项目):目录里的 mise.toml 声明 python = "3.12.13",目录外没有对应声明:
text
python --version # 目录内 -> Python 3.12.13
python --version # 目录外 -> Python 3.12.7(落到全局默认或其他来源)配合 mise ls 确认某个版本确实已经装好、which python 确认当前接管的是不是 mise 的 shim(路径形如 ~/.local/share/mise/shims/python,而不是某个版本管理器自己的 shim 目录或系统自带路径)。三者对不上时,先怀疑 PATH 顺序或旧工具还没退干净,回到「开始之前」重新过一遍处理顺序。
常见问题
装了 mise,某个没配置版本的目录里命令解析到了一个意料之外的版本,但没有报错。 这是 mise shim 有意为之的设计:找不到当前目录该用哪个版本时,官方文档说明它会「fall back to the first same-named executable found elsewhere on PATH rather than erroring」——直接执行 PATH 上第一个同名的可执行文件,而不是报错退出。对于你确实希望 mise 之外也能用的工具,这是方便;但如果那个「另一个同名可执行文件」是系统自带的、或者是另一个版本管理器装的,就会出现「明明按 mise 的流程配置了,却在悄悄跑一个不相关的版本」,且不会有任何报错提示你。想改成解析失败就直接报错,官方文档给出的做法是在配置里把 not_found_system_fallback 和 not_found_auto_install 都设为 false。
与 nvm/pyenv 共存,切换看起来没生效。 回到「开始之前」一节的处理顺序:先注释掉旧工具在 .zshrc 里的初始化行,重开终端确认旧工具确实交出了接管权,再确认 mise 的激活行生效。
非交互 shell、GUI 应用里版本不对或直接找不到命令。 这是预期行为,不是故障。mise activate 加进 .zshrc 只对交互式 shell 生效;ssh 主机 命令、cron 任务、双击打开的 GUI 应用都不会经过它。需要在这些场景里用到 mise 管理的版本,用 mise exec -- 命令 显式指定,或者在脚本开头自己 eval "$(mise activate bash)"。
信任机制:为什么克隆一个新仓库后 mise 提示要信任配置文件。 mise.toml 里能定义环境变量、能定义任务的 run 命令,这些在你进入目录、或者跑 mise run 时就会被求值、执行——本质上等同于运行了这份配置文件的作者写的代码。所以 mise 要求显式信任每一份来源不是自己写的配置:mise trust 信任、mise trust --untrust 撤销信任。克隆来路不明的仓库时,养成先打开 mise.toml 读一遍内容、确认 [env] 和 [tasks] 里没有可疑命令,再执行 mise trust 的习惯,跟不假思索地执行一个陌生仓库里的安装脚本是同一类风险。
动手练习
- 建两个练习目录,比如
~/mise-demo-a和~/mise-demo-b。 - 在其中一个里跑
mise use python@3.12(或机器上已经装过的另一个版本),在另一个里跑mise use python@3.11(或任意一个和上面不同的版本,前提是这两个版本你都愿意让 mise 装到本地)。 cd进第一个目录跑python --version,再cd进第二个目录跑同一条命令,确认两次输出不同。- 回到不属于任何一个练习目录的地方再跑一次,确认版本落回了全局默认值。
- 练完
cd出这两个目录,rm -rf删掉它们;如果这两个版本本来机器上就没有、是专门为了这次练习装的,也可以用mise uninstall python@版本号一并清理,不留下用不到的运行时。