工具版本 pin 基线
对「
vx.toml里的[tools]该怎么写」给出一个可机械校验的答案。 如果你用 vx 维护多个仓库,请统一采用本基线 —— 重点不是规则多精妙,而是每个仓库对同一个问题的答案一致。
为什么需要基线
下面每条规则都来自两类故障。
- 两处 pin 静默冲突。
vx.toml里写rust = "1.95",仓库里同时还有rust-toolchain.toml, 就有了两个真源。只有一个生效,而且没有任何提示告诉你生效的是哪个。 - pin 其实是浮动的。
python = "latest"等于没记录任何版本 —— 昨天跑 CI 用的工具链, 在任何地方都没有被记录下来。
版本写法
| 写法 | 解析结果 |
|---|---|
major.minor | 该 minor 线下最新的补丁版(3.12 → 3.12.x) |
major.minor.patch | 精确版本 |
latest | 最新稳定版 |
完整语法见版本管理。
三类工具
| 类别 | 定义 | 允许写法 | 示例 |
|---|---|---|---|
| 运行时 | 代码运行其上的语言运行时 | major.minor;精确补丁需附原因注释 | python = "3.12" |
| 构建工具 | 不产出发布产物 | latest | uv = "latest" |
| 产物工具 | 其版本会影响发布的产物 | 精确版本,或 major.minor + 原因注释 | maturin = "1.9.6" |
常见归属:运行时 —— python / node / rust;构建工具 —— uv / just / cmake / prek / actionlint / cargo-nextest / cargo-llvm-cov / sccache; 产物工具 —— maturin / msvc / rcedit。
规则
- R1 —— 每个工具只有一个真源。 生态本身已经负责 pin 时(
rust-toolchain.toml),vx.toml不得重复声明。 - R2 —— 运行时禁止浮动。 运行时类别里禁用
latest/stable/nightly/*。 - R3 —— 精确 pin 必须写原因。
major.minor.patch或产物工具的 pin 要有行尾注释, 说明对应的回归或约束。 - R4 —— 不重复写默认值。 某项配置如果等于 vx 的默认值,就删掉它。
- R5 —— 例外写在行内。 偏离本页规则的地方在
vx.toml里写一行注释,不写 wiki。
Rust
Rust 是唯一有「原生 pin 文件」竞争的运行时,因此单列规则。vx 按以下顺序判定归属:
- 从工作目录向上查找
rust-toolchain.toml/rust-toolchain→ 归 rustup。vx 不安装、 不把自身 store 加到PATH、绝不改动 rustup 的默认值。 - 环境变量
RUSTUP_TOOLCHAIN→ 归 rustup。 vx.toml里rust = "rustup-managed"→ 归 rustup。这是「刻意不让 vx 管 Rust」的显式声明。- 以上都没有 → 归 vx,装进自己的 store。即使如此,工具链也通过
RUSTUP_TOOLCHAIN注入子进程环境,绝不改写用户全局的 rustup default。
| 仓库现状 | vx.toml 里该写什么 |
|---|---|
有 rust-toolchain.toml | 什么都不写 —— 那个文件就是 pin |
| 有 rustup,但没有 toolchain 文件 | rust = "rustup-managed" |
| 完全没有 rustup(裸容器、精简 CI 镜像) | rust = "1.95" —— major.minor,绝不写通道名 |
归属顺序与 rustup-managed 哨兵值随 rust-toolchain 归属特性的发布生效。在更老的 vx 上, 请省略 rust 行,只保留 rust-toolchain.toml。
vx.lock —— 提交进版本库
vx.lock覆盖vx.toml:解析时锁定版本优先,所以真正让 CI 可复现的是 lock 文件。提交
vx.lock。 每次改动[tools]都要重新生成(vx lock)—— 过期的 lock 会继续 pin 旧版本,让你对vx.toml的修改看起来「没生效」。CI 门禁:
bashtest -f vx.lock || { echo "vx.lock missing; run 'vx lock'"; exit 1; } vx lock --checkvx lock --check在以下情况失败:工具缺失于 lock、lock 里的resolved_from与vx.toml不一致、lock 里残留vx.toml已删除的工具。但 lock 文件不存在时它返回 0, 所以「文件存在性」必须单独检查。一份提交的 lock 可以服务所有平台:锁定的版本号与平台无关,
platform_urls按目标平台 保存下载地址,platform元数据只记录 lock 是在哪台机器上生成的。
[settings] —— 删掉默认值
| 键 | vx 的行为 | 结论 |
|---|---|---|
auto_install | 默认为 true | 除非要设成 false,否则删除该行 |
cache_duration | 会解析,但解析过程并不消费 | 删除 —— 无效配置 |
parallel_install | 会解析,但解析过程并不消费 | 删除 —— 无效配置 |
cache_duration 与 parallel_install 仍会被解析、迁移,也会被 vx init 写出来,但 resolver 没有任何代码读取它们,只会增加 diff 噪音。真正需要覆盖默认值时才保留 [settings](例如离线 runner 上的 auto_install = false),并注释说明原因。
例外
任何偏离基线的条目都要带行尾注释:
[tools]
python = "3.14.4" # rez 3.x 需要 3.14 的 importlib.metadata 修复;见 issue #1234
rust = "1.90.0" # 内置 CEF 封装的 MSRV;随封装一起升级可机械校验的门禁
# R1:toolchain 文件之外不得重复 pin rust
if test -f rust-toolchain.toml && grep -qE '^[[:space:]]*rust[[:space:]]*=' vx.toml; then
echo "rust is pinned twice (rust-toolchain.toml and vx.toml)"; exit 1
fi
# R2:运行时不得浮动
if grep -qE '^[[:space:]]*(python|node|rust)[[:space:]]*=[[:space:]]*"(latest|stable|nightly|\*)"' vx.toml; then
echo "runtime pinned to a floating channel"; exit 1
fi
# R4:settings 不得重复默认值
if grep -qE '^[[:space:]]*(auto_install[[:space:]]*=[[:space:]]*true|cache_duration|parallel_install)' vx.toml; then
echo "[settings] restates vx defaults"; exit 1
fi
# vx.lock 存在且与 vx.toml 一致
test -f vx.lock || { echo "vx.lock missing; run 'vx lock'"; exit 1; }
vx lock --checkR3 与 R5 是评审期规则:major.minor.patch 或产物工具的 pin 若没有行尾注释,评审不予通过。
落地示例 —— 在 12 个仓库中采用本基线
下表是 2026-09-28 从 dcc-mcp 组织与 loonghao 个人仓库采集的快照。它只是快照,不是契约 的一部分 —— 上面的规则才是契约。
| 仓库 | rust | python | node | uv | [settings] | vx.lock |
|---|---|---|---|---|---|---|
dcc-mcp/dcc-mcp-core | 1.95 + toolchain 文件 | 3.12 | 22 | latest | 有 | 有 |
dcc-mcp/dcc-mcp-photoshop | — | 3.12 | — | latest | 有 | 无 |
dcc-mcp/dcc-mcp-unreal | — | 3.12 | — | — | 无 | 无 |
dcc-mcp/dcc-cua | — | — | 22 | — | 无 | 无 |
loonghao/fpt-cli | stable | 3.12 | 22 | latest | 有 | 有 |
loonghao/shotgrid-mcp-* | — | latest | — | 0.7.12 | 无 | 无 |
loonghao/auroraview | 1.90.0 | 3.11 | — | latest | 有 | 有 |
loonghao/vx | 已省略 | 3.11 | latest | latest | 有 | 有 |
loonghao/msvc-kit | 1.93.1 | — | 22 | — | 有 | 有 |
loonghao/rez-next | 已省略 | 3.14.4 | — | latest | 有 | 无 |
loonghao/transx | — | 3.12 | — | latest | 有 | 有 |
loonghao/rez-lsp-server | — | — | 22 | — | 无 | 无 |
基线在这份快照上要求的变化:
- rust ——
dcc-mcp-core删掉rust = "1.95"(toolchain 文件已经 pin 了);fpt-cli换掉stable;auroraview与msvc-kit把 pin 挪进rust-toolchain.toml, 并删除vx.toml里的那一行。 - python ——
shotgrid-mcp-server不再用latest;rez-next的3.14.4保留, 但补上 R3 要求的原因注释;auroraview与vx的3.11要么升到3.12,要么补注释。 - node ——
vx从latest改为22,与其他仓库一致。 - uv ——
shotgrid-mcp-server的0.7.12改为latest,除非有在案的回归记录。 - 产物工具 ——
dcc-mcp-core与rez-next的maturin从latest改为钉住的 minor, 因为它的版本会进入构建出的 wheel。 [settings]—— 从 8 个只重复默认值的仓库里删除。vx.lock—— 在缺失的 6 个仓库里生成并提交。