mise 入门:多语言版本管理器
了解 mise 如何按项目管理 Node 和 Python 版本,处理现有版本文件、锁定工具、设置环境变量,并在本地和 CI 中运行一致的构建。
mise 是一个命令行工具,可以按目录安装和切换 Node、Python、Ruby 等运行时的版本,版本信息从项目根目录下的 mise.toml 文件中读取。
如果你的仓库同时包含一个 Vite 前端和一个在构建前生成内容的 Python 脚本,那你很可能在同时使用 nvm 和 pyenv,并寄希望于每位队友的 shell 环境都与 CI 保持一致。而通常总有一方对不上。
如需对比 mise、nvm、pyenv、rustup 和 SDKMAN!,请参阅 5 Version Managers Every Developer Should Know。本文不再罗列功能,而是以一个仓库为例,依次讲解 cd 切换目录、现有的 .nvmrc 文件、队友首次克隆、CI、跨两种运行时的构建任务以及环境变量。
核心要点
- 启用 shell 激活后,进入包含
mise.toml的目录时,该项目固定的运行时版本会被加入 PATH;离开目录后则恢复之前的版本。 - mise 默认忽略
.nvmrc和.python-version,除非通过idiomatic_version_file_enable_tools设置按工具逐一开启。 mise run会自行加载项目的工具和环境变量,并默认安装缺失的工具,因此无需 shell 激活即可在 CI 中使用。jdx/mise-action@v4会安装 mise 以及mise.toml中声明的工具。如果仓库中提交了mise.lock,则会使用--locked安装。[env]表是会被提交的明文内容,用于存放配置,而非密钥。
示例项目:带 Python 构建步骤的 Vite 前端
示例仓库 docs-preview 包含两种运行时、一个配置文件和一个工作流:
docs-preview/
├── content/*.md
├── scripts/build_content.py
├── src/
├── package.json # vite in devDependencies
├── mise.toml
└── .github/workflows/build.yml
该 Python 脚本只使用标准库,负责将 Markdown 文件转换为供前端导入的 JSON:
# scripts/build_content.py
import json, os, pathlib
src = pathlib.Path(os.environ.get("CONTENT_DIR", "content"))
out = pathlib.Path("src/generated/content.json")
out.parent.mkdir(parents=True, exist_ok=True)
items = [{"slug": p.stem, "body": p.read_text(encoding="utf-8")}
for p in sorted(src.glob("*.md"))]
out.write_text(json.dumps(items, indent=2), encoding="utf-8")
print(f"wrote {len(items)} items to {out}")
以下是完整的 mise.toml,后续各节都会引用它:
[tools]
node = "24"
python = "3.14"
[settings]
lockfile = true
[env]
CONTENT_DIR = "content"
VITE_SITE_NAME = "Docs Preview"
[tasks.install]
description = "Install JS dependencies"
run = "npm ci"
[tasks."build:content"]
description = "Convert content files to JSON for the frontend"
run = "python scripts/build_content.py"
[tasks.build]
description = "Generate content, then build the Vite app"
depends = ["install", "build:content"]
run = "npx vite build"
[tools] 表使用的是版本前缀(Node 24 LTS、Python 3.14)。下文将介绍的锁文件会记录它们实际解析到的精确补丁版本。
进入和离开目录时发生了什么
启用 mise 激活后,进入包含 mise.toml 的目录时,该项目固定的运行时会被加入 PATH;离开目录时,则恢复之前生效的版本。只要将 mise activate 写入 shell 启动文件,每次切换目录时 mise 都会自动切换工具和环境变量。
cd ~
node --version # whatever your system or nvm provides
cd ~/code/docs-preview
node --version # v24.x from mise.toml
python3 --version # Python 3.14.x
echo "$CONTENT_DIR" # content
mise 切换版本的时机取决于所用的 shell。根据 shims 文档,bash、zsh、fish 和 xonsh 在执行 cd 后立即切换;其他 shell 则要等到下一次出现提示符时才切换。
固定的版本尚未安装时
在一台全新的机器上,在项目目录中运行 mise install 即可,这是最明确、最可预期的解决方式。任务也能自动处理这种情况:mise tasks 会使用项目的工具和环境变量运行,而默认开启的 auto_install 设置会在调用 mise run 时自动安装缺失的已配置工具。如果依赖 shims,Python 需要特别留意。设置参考文档指出,当 shim 无法解析到 mise 管理的版本时(例如关闭了自动安装),它会回退到 PATH 中其他位置的第一个同名程序。对于 python3 这种操作系统自带的工具,这意味着你固定的版本根本不会运行,而且不会有任何报错。
若只想在某一个终端中固定不同的版本,可以使用 mise shell node@<version>。前提是该 shell 已激活 mise。
现有的 .nvmrc 和 .python-version 文件
mise 不会读取 .nvmrc 或 .python-version,除非通过 idiomatic_version_file_enable_tools 为每个工具单独开启。mise FAQ 确认这类惯用版本文件(idiomatic version files)默认处于关闭状态,这一默认行为自 mise 2025.10.0 起生效。开启方式如下:
[settings]
idiomatic_version_file_enable_tools = ["node", "python"]
配置文档将 package.json 列为 mise 为 node 读取的文件之一,因此开启 node 后也可能会读取该文件。如需跳过某个文件,可在 idiomatic_version_file_disable_files 设置中添加 tool:filename 形式的条目,例如 node:package.json。
当 mise.toml 已固定两种运行时后,.nvmrc 只需为仍在使用 nvm 的队友保留,并确保两个文件中的版本一致。如需回顾 nvm 如何处理该文件,请参阅在 macOS、Linux 和 Windows 上使用 nvm 切换 Node 版本的指南。
团队协作与 CI
笔记本电脑和 CI runner 读取的是同一个 mise.toml,但将工具加入 PATH 的方式不同:
| 笔记本电脑 | GitHub Actions | |
|---|---|---|
| 工具加入 PATH 的方式 | shell 中的激活钩子 | jdx/mise-action 安装 mise 及固定版本的工具 |
| 版本来源 | mise.toml + mise.lock | 相同文件 |
| 构建命令 | mise run build | mise run build |
队友首次克隆仓库:
- 安装 mise:
curl https://mise.run | sh - 将
eval "$(~/.local/bin/mise activate zsh)"(或对应你所用 shell 的命令)添加到 shell 的 rc 文件中。 - 审阅
mise.toml,然后运行mise install。 - 运行
mise run build。
由于配置文件可以执行代码,mise 的信任模型需要格外注意。根据入门指南,在非 CI 且未启用 paranoid 模式的情况下,任何作用于项目的命令(如 mise install、mise exec 或 mise run)都会自动信任当前生效的配置。因此在运行这些命令之前,请先审阅 mise.toml;如需显式信任某个配置,请使用 mise trust。
锁定精确版本。 锁文件 mise.lock 可通过 mise lock 或上文所示的 lockfile = true 设置生成。请将其提交到仓库。使用 mise install --locked 时,锁文件中缺失的条目会直接报错,从而确保 mise 不会悄悄选用新版本。
CI 工作流:
# .github/workflows/build.yml
name: build
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: jdx/mise-action@v4
- run: mise run build
mise-action 步骤会安装 mise 以及 mise.toml 中的工具,因此工作流使用的版本与开发者本地完全一致。默认情况下,它会运行 mise install(install: true)并缓存工具(cache: true)。当存在 mise.lock 时,它会使用 --locked 安装。它还会自动传入 ${{ github.token }}。在笔记本电脑上,如果安装时遇到速率限制,可以设置 GITHUB_TOKEN 或 MISE_GITHUB_TOKEN,详见 GitHub tokens 页面。
出现问题时,可以用以下三个命令缩小排查范围:
| 症状 | 命令 |
|---|---|
| 激活或安装状态异常 | mise doctor |
| 似乎应用了错误的配置文件 | mise config ls |
| 选中了错误的版本 | mise ls --current |
实战任务:跨两种运行时的 mise run build
在 mise.toml 中定义的任务运行时,项目固定的工具和 [env] 变量都已生效。这意味着即使没有 shell 激活,mise run build 在笔记本电脑和 CI 中使用的也是相同的 Node 和 Python。
depends 键会让 build 等待其列出的所有任务完成后再执行,从而确保 Python 步骤在 Vite 读取之前已写好 src/generated/content.json。被列出的任务之间可以并发运行。任务文档说明,前置任务可能并行执行,它们在 depends 中的顺序并不代表执行顺序。在本例中这是安全的,因为 npm ci 写入的是 node_modules,而 Python 脚本写入的是 src/generated,两个步骤互不干扰对方的输出。如果某个步骤需要使用另一个步骤的输出,请让它直接依赖该步骤。
mise tasks # lists install, build:content, build
mise run build # npm ci + Python step, then vite build
根据运行任务指南,默认的并行任务上限为 8 个。
使用 mise 管理项目级环境变量
[env] 表中的值以明文形式存在于已提交的文件中。mise 的环境变量功能属于配置管理,而非密钥管理。在本例中,Python 脚本通过 os.environ 读取 CONTENT_DIR,前端则读取 import.meta.env.VITE_SITE_NAME。
VITE_ 前缀让 [env] 明文值的风险变得具体可见。Vite 环境变量文档指出,所有以 VITE_ 开头的变量都会被打包进客户端 bundle,其值会在构建时直接写入代码。因此,网站一旦上线,mise.toml 中的任何 VITE_ 值都是公开的。
缺失的构建时变量并不会导致构建失败,而是会生成一个请求错误 URL 的 bundle。会话回放(Session Replay)可以从用户视角呈现这一问题:你会看到请求被发往一个 undefined 的 base URL,进而追溯到构建时所处的环境。
什么时候值得使用 mise?
如果项目只需要 Node,nvm 加上 .nvmrc 已经足够,mise 并非必需,nvm 相关指南已对这种配置做了充分介绍。当一个仓库需要固定两种或以上的运行时,并且 CI 必须与每台开发机保持一致时,就值得引入 mise。若想在现有仓库中尝试,只需添加一个包含 [tools] 版本固定的 mise.toml,运行 mise lock,提交这两个文件,然后将 CI 的构建步骤改为 mise run build 即可。
常见问题
既然 mise.toml 会被提交,密钥应该放在哪里?
可以将密钥放在已加入 gitignore 的 mise.local.toml 中,或者在 mise.toml 中以 required = true 声明它们,这样 mise 会检查该变量是否已设置,而无需存储其值。添加 redact = true 可以在捕获的任务输出中遮蔽这些值,但 mise env 仍会以明文打印它们。对于必须提交到仓库的密钥,mise 可以为 mise exec、任务和 shell 激活解密使用 age 加密的 SOPS 文件(实验性功能)。
mise 能读取 asdf 的 .tool-versions 文件吗?
可以。与 .nvmrc 不同,mise 无需任何额外设置即可读取 .tool-versions。在 Unix 系统上,它还可以运行 asdf 的 shell 插件,但并不保证所有 asdf 命令或插件都能正常工作。如果同一目录中的 mise.toml 和 .tool-versions 声明了同一个工具,则以 mise.toml 为准。mise 文档建议不要在 asdf 和 mise 之间共享安装目录,因此切换后请运行 mise install。
为什么编辑器或脚本使用的 Node 版本与终端不同?
编辑器、git hooks 和调度程序通常不会加载交互式 shell 的 rc 文件,因此 mise 的激活钩子根本不会运行。解决方法是将 mise activate zsh --shims(或对应你所用 shell 的命令)添加到编辑器会读取的登录配置文件中,例如 ~/.zprofile,同时在 ~/.zshrc 中保留普通的 mise activate。对于脚本,请改用 mise exec -- command 或 mise run。shim 模式会跳过部分激活功能,例如大多数钩子。