三行配置钉死整个团队的工具版本

2026-08-09

先把状态说清楚,再谈配置。

本文描述的是 OpenCut 重写版仓库(OpenCut-app/OpenCut)当前的代码结构与官方 README 声明的路线图。该版本尚未发布,Editor API、插件体系、MCP server、无头模式等均为官方声明的计划,我们没有见到可用实现。核对日 2026-08-09。

也就是说,下面要讲的东西跟「用 OpenCut 剪视频」没有关系。今天线上 opencut.app 跑的仍然是旧版 classic(仓库 OpenCut-app/opencut-classic,已归档、不再维护);重写版按 README 的说法会先住在 new.opencut.app,直到它准备好接管。本文看的只是重写主仓这一个仓库里的工程配置——那部分是实打实躺在磁盘上的文件,跟功能有没有做完是两码事。

一、要解决的是哪个老问题

团队里最容易反复扯皮的一类问题,不是业务逻辑,是「你那边能跑我这边不能跑」。同一份代码,A 用的构建工具是一个版本,B 装的是另一个,CI 镜像里又是第三个,出了差异谁也说不清是代码问题还是环境问题。常见的缓解手段是在 README 里写一句「请使用 xx 版本以上」,然后靠自觉。

OpenCut 重写主仓的做法是把这件事下沉成仓库里的一个文件。仓库根有一份 .prototools,全文如下:

# proto pins tool versions workspace-wide.
# Every developer and CI machine gets the exact same versions automatically.
moon = "2.3.3"
bun  = "1.3.11"
rust = "1.97.0"

去掉两行注释,正文只有三行。注释那句话把意图写得很直白:proto 在整个工作区范围内钉住工具版本,每个开发者和每台 CI 机器都自动拿到完全相同的版本

三个工具各管一摊:moon 是任务运行与缓存层,bun 用于 web 与 api 两个 TypeScript 应用,rust 用于桌面端。桌面端的依赖钉在另一处——仓库根 Cargo.toml[workspace.dependencies] 里写着 gpui = "0.2.2",workspace 版本是 0.1.0edition = "2024"。顺带一提,那份 Cargo.tomlmembers'crates/*' 这一行是被注释掉的,目前 workspace 只有 apps/desktop 一个成员。这是我们读文件读到的事实,不引申任何进度判断。

值得注意的是版本号写法:三个都是精确到修订号的定值,不是 ^2.3 这类范围。范围写法的意思是「差不多就行」,定值的意思是「必须一样」。这两种语义解决的不是同一个问题。

二、完整命令:从零到本地能起服务

以下步骤全部来自重写主仓 README。Windows 与类 Unix 分开写,别混用。

第一步,装 proto。

Linux、macOS、WSL:

bash <(curl -fsSL https://moonrepo.dev/install/proto.sh)

Windows PowerShell:

irm https://moonrepo.dev/install/proto.ps1 | iex

README 还专门给了 Windows 上的一个坑位处置:如果 shim 跑不起来,执行

Set-ExecutionPolicy -Scope CurrentUser RemoteSigned

这条命令的作用是为当前用户允许运行本地脚本。它改的是执行策略,不是某个项目的配置,动手前请自己确认这在你所在环境的合规范围内。

第二步,在仓库根安装被钉住的工具。

proto use

这一条就是把 .prototools 里那三行落成本机实际可用的工具。注意执行位置是仓库根——配置在仓库里,命令也要在仓库里跑,这是整套做法成立的前提。

第三步,用 moon 起任务。

moon run web:dev       # localhost:5173
moon run api:dev       # localhost:8787
moon run desktop:dev   # 见 apps/desktop/README.md

两个端口记牢:重写版 web 是 5173,api 是 8787。classic 那套是 3000。如果你两个仓库都 clone 了,端口是最快分辨自己在跟哪个代码库打交道的标志。api 那个 8787 是因为 apps/api/ 是个 Cloudflare Worker,目录里只有 src/index.tswrangler.jsoncmoon.ymlpackage.json 四个文件——我们没有读 index.ts 的实现,所以它对外提供什么接口,本文不作任何描述。

以上命令均按 README 原文抄录并按其步骤顺序组合,我们没有安装 proto/moon,没有跑过任何 moon 任务,也没有构建过桌面端,请以官方文档与各命令 --help 的实际输出为准。

三、版本钉死之后,任务本身也得可复现

只钉工具版本还不够。工具一样,但每个人跑的命令不一样、缓存策略不一样,照样会分叉。重写主仓把任务定义也写进了仓库,apps/web/moon.yml 的要点是这样:

language: 'typescript'
layer: 'application'

tasks:
  dev:
    command: 'bun run dev'
    options:
      runInCI: false  # dev server: skipped in CI, never cached
  build:
    command: 'bun run build'
    inputs: ['src/**/*', 'public/**/*', 'vite.config.ts', 'tsconfig.json', 'package.json']
    outputs: ['dist']
  test:
    command: 'bun run test'
    inputs: ['src/**/*', 'tsconfig.json']
  deploy:
    command: 'bun run deploy'
    deps: ['~:build']

这份文件里有三处是真正干活的:

inputs 决定缓存与增量的粒度。 build 声明的输入是 src/**/*public/**/* 和三个配置文件。含义很实际:改 README、改 changelog/ 里的文档,都不在这个集合里,因此不会让 build 失效。反过来,如果你新增了一类会影响构建结果的文件却忘了加进 inputs,缓存就可能给你一个过期产物——这是这类配置最典型的翻车方式,也是 review 时最该盯的一行。

outputs: ['dist'] 让产物本身可被复用。 声明了输出位置,缓存命中时才有东西可以还原。

deploydeps: ['~:build'] 声明依赖。 部署依赖构建这件事被写成了配置,而不是靠人记得「先 build 再 deploy」。人会忘,配置不会。

dev 任务的 runInCI: false 是同一个思路的反向用法:开发服务器是长驻进程,在 CI 里跑它没有意义,所以直接声明不在 CI 中执行。重写主仓仓库根的 moon.yml 里还有个 upload-logos 任务,它读 .env.local(缺失就报错退出)、要求 R2_BUCKET 变量,把 brand/marks/*.svgwrangler r2 object put 传到 R2,选项是 cache: falserunInCI: false——带副作用、依赖本机凭据的任务不该被缓存,也不该在 CI 里自动跑,这个组合的取舍写得很清楚。

四、产出物长什么样

这一节只写有依据的部分。我们没有执行过上述任何命令,所以不描述终端输出的具体文案、日志行数或界面提示。

从配置能确定的是:web:build 的产物落在 dist,因为 outputs 就是这么声明的;web:devapi:dev 分别监听 5173 与 8787;proto use 的作用对象是 .prototools 里列出的那三个工具。除此之外,退出码、缓存目录位置、moon 的输出格式这些,官方 README 没写,我们也没跑过,本文一律不编。

五、怎么验收

搬这套做法到自己项目里,人工要检查的是这么几处:

  1. 配置文件在不在仓库根,且已被提交。放在 .gitignore 里或者只存在于某个人机器上的版本约束,等于没有。
  2. 版本写法是定值还是范围。想要「大家完全一致」,就不能写范围。这两者混用是最容易被忽略的一步。
  3. CI 是否真的走同一条路径。仓库里钉住了版本,但 CI 脚本如果绕开 proto 自己 apt install 一个,钉死就失效了。验收时看 CI 配置里到底是谁在安装工具。
  4. inputs 有没有漏项。每新增一类参与构建的文件,都要回头看一眼是否已被 inputs 覆盖。漏了不会报错,只会静悄悄给你旧产物——这是最难查的一种。
  5. Windows 侧单独验一遍。类 Unix 与 PowerShell 的安装脚本是两条不同的命令,Set-ExecutionPolicy 这一步只有 Windows 才有。团队里只要有一台 Windows 机器,就别只在 macOS 上验收完事。

六、什么情况不适用

这套做法不是万能的,下面几种情况要么别照搬,要么必须人工兜底。

你的工具链不在 proto 的管辖范围内。 版本钉死的前提是有一个统一的安装器去落地它。系统级依赖、显卡驱动、平台 SDK 这类东西不归它管,仍然要靠文档和镜像约束。

团队需要同时维护多个互不兼容的版本。 钉死是「所有人一致」,如果你的现实需求是「这个分支用旧版、那个分支用新版」,那要解决的是分支与环境的映射问题,单靠一个仓库级定值不够。

引入成本要算清楚。 这套流程要求每个人先装 proto,再用 moon run 而不是习惯的直接命令。人少、项目短的场景,收益可能覆盖不了改造成本。

别把它当质量保证。 版本一致只消除了「环境不同」这一类差异,不代表构建结果正确,也不代表缓存命中的产物就是你要的那个。inputs 写错时它照样自信地给你旧东西。

最后再强调一次边界:本文讲的全部是重写主仓里的工程配置文件,与 OpenCut 作为一个视频编辑器的功能无关。README 列出的 Editor API、第三方插件、桌面/移动/浏览器共用一套代码库、MCP server、无头模式、编辑器内脚本标签页这六项,是官方声明的计划,不是现在能用的能力;README 同时说明「架构还在设计中,暂时还没准备好接受外部贡献」。想跟进的人可以看官方仓库的 issue 或 Discord,但别抱着交 PR 的预期过去。

延伸阅读


本文依据 OpenCut 官方仓库(github.com/OpenCut-app/OpenCut 与已归档的 github.com/OpenCut-app/opencut-classic)的 README、.prototoolsmoon.ymlapps/web/moon.ymlCargo.tomlchangelog/ 整理,核对日 2026-08-09。本文内容为仓库源码与文档口径,我们没有编译或运行过任何版本的 OpenCut,也没有安装 proto/moon 或执行过文中任何命令。

本文描述的是 OpenCut 重写版仓库当前的代码结构与官方 README 声明的路线图。该版本尚未发布,Editor API、插件体系、MCP server、无头模式等均为官方声明的计划。

许可条款请以官方 LICENSE 原文为准,本文不构成法律意见。Set-ExecutionPolicy 等涉及执行策略的做法请结合自身环境评估,本文不构成安全方案建议。

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。