Windmill 多 Worktree 开发环境实战:基于 workmux、tmux 与 Claude Code 的并行开发工作流
2026/9/14 3:27:18 网站建设 项目流程

Windmill 多 Worktree 开发环境实战:基于 workmux、tmux 与 Claude Code 的并行开发工作流

【免费下载链接】windmillOpen-source developer platform to power your entire infra and turn scripts into webhooks, workflows and UIs. Fastest workflow engine (13x vs Airflow). Open-source alternative to Retool and Temporal.项目地址: https://gitcode.com/GitHub_Trending/wi/windmill

本篇技术指南围绕 Windmill 仓库根目录的 README_WORKMUX_DEV.md 展开,系统讲解基于workmux的多 worktree 开发环境:每个 git worktree 拥有独立的 tmux 窗口,内含 Claude Code 智能体、自动热重载的后端服务和前端 dev server,并通过"端口槽(Slot)"机制在隔离端口上并行运行。读完本文,你将掌握从安装依赖、创建/打开/合并/清理 worktree,到 SSH 远程端口转发、企业版(EE)代码挂载、Cargo features 传递等完整实战能力,并能结合 scripts/worktree-env、scripts/worktree-common.sh 等仓库脚本理解其底层实现原理。

环境概览:一个 Worktree 就是一个独立开发沙箱

Windmill 的 workmux 方案将"并行开发多个功能"彻底解耦:每创建一个 feature worktree,workmux 就会为其分配一个 tmux 窗口,窗口内固定布局三个面板(pane):

  • Claude Code agent:负责接收你的自然语言任务(如"修复 auth.rs 的登录 bug"),直接在该 worktree 内读写代码、运行命令;
  • 后端服务:以cargo watch -x run方式运行,文件保存即自动重编译、重启,监听该 worktree 专属端口;
  • 前端服务:以npm run dev方式启动,并将/api等请求代理到同 worktree 的后端端口。

三者共享同一份 worktree 工作目录,天然保证"改代码 → 后端重载 → 前端联调 → agent 交付"在同一沙箱内闭环。仓库根目录的 README_WORKMUX_DEV.md 是这套方案的权威说明,下文所有命令与配置均以其为准,并辅以仓库脚本佐证。

前置要求

在开始之前,需要准备以下工具链:

依赖用途
tmux承载 worktree 的窗口/面板布局
Rust 工具链(rustup)编译并安装 workmux、cargo-watch,以及编译 Windmill 后端
Node.js + npm运行前端 dev server 与 CLI 工具
本地 PostgreSQL后端数据库,连接配置见 backend/.env

PostgreSQL 的配置细节可在 backend/.env 中查看(默认连接串形如postgres://postgres:changeme@127.0.0.1:5432/windmill,与 scripts/worktree-common.sh 中的约定一致)。

安装步骤

1. 安装 workmux

workmux 是这套工作流的编排核心,通过 Cargo 安装:

cargo install workmux

2. 安装 Claude Code 插件

workmux claude install

该命令让 workmux 具备在 worktree 面板中管理 Claude Code agent 的能力(创建、发送提示词、查看状态等)。

3. 安装 cargo-watch

用于在后端源码变更时自动重编译并重启服务:

cargo install cargo-watch

4. 安装 llm CLI(自动分支命名必需)

workmux 使用llmCLI 根据任务提示词自动生成分支名。通过 uv 安装:

uv tool install llm llm install llm-anthropic

随后配置 Anthropic API Key:

llm keys set anthropic # 按提示粘贴你的 API Key

5. 推荐:Shell 别名与自动补全

为日常操作设置便捷别名,在~/.zshrc中添加:

alias wm="workmux"

同时建议为 workmux 配置 zsh 自动补全,具体步骤参见 workmux 官方文档(workmux安装后可在其帮助信息中找到 autocomplete 子命令)。

端口槽(Slot)系统:隔离与可预测的端口分配

这是整套方案的基石。每个 worktree 会被分配一个slot,由 slot 唯一确定前后端端口:

Slot后端端口前端端口
080003000
180103010
280203020
380303030
.........

端口计算公式即8000 + slot*103000 + slot*10,这一点可直接在仓库脚本 scripts/worktree-env 中验证:

backend_port=$((8000 + WM_SLOT * 10)) frontend_port=$((3000 + WM_SLOT * 10))

三条关键规则:

  • Slot 0 保留给主 worktree(默认的cargo run/npm run dev),端口为 8000/3000;
  • 不设置WM_SLOT,scripts/worktree-env 会自动扫描所有现存 worktree 的.env.local,找出被占用的 slot,再从 1 开始分配最小的空闲 slot,并在终端打印结果。脚本注释说明了为何采用"扫描已占用"而非"位置索引":worktree 被删除后位置索引会复用仍存活的 slot,导致端口冲突;
  • 设置WM_SLOT=N,强制使用该 slot;若端口已被占用,脚本会输出WARNING: Slot N ports (...) already in use警告(见 scripts/worktree-env 的port_in_use检查逻辑)。

分配结果会写入该 worktree 根目录的.env.local

BACKEND_PORT=$backend_port FRONTEND_PORT=$frontend_port REMOTE=http://localhost:$backend_port

其中REMOTE被前端 vite 配置读取,作为/api请求的代理目标——这与 frontend/vite.config.js 中process.env.REMOTE ?? (process.env.BACKEND_PORT ? \http://localhost:${process.env.BACKEND_PORT}` : 'https://app.windmill.dev/')` 的取址逻辑一一对应。

SSH 端口转发:远程开发本地访问

如果你通过 SSH 远程开发,可在本地机器~/.ssh/config中为每个 slot 预配置隧道:

Host windmill-dev HostName <remote-ip> User <username> # Slot 0 (main worktree) LocalForward 8000 localhost:8000 LocalForward 3000 localhost:3000 # Slot 1 LocalForward 8010 localhost:8010 LocalForward 3010 localhost:3010 # Slot 2 LocalForward 8020 localhost:8020 LocalForward 3020 localhost:3020 # Slot 3 LocalForward 8030 localhost:8030 LocalForward 3030 localhost:3030

之后只需连接一次,所有隧道即全部生效:

ssh windmill-dev

随后在本地浏览器访问http://localhost:<frontend-port>即可看到对应 worktree 的前端界面。端口槽的"可预测性"正是为了配合这种静态隧道配置:slot 一旦确定,端口就永远可预期,无需每次手动改配置。

快速上手:创建、打开、协作与清理

创建 worktree

# 创建新 worktree(自动分配 slot,打印端口) workmux add my-feature # 或显式指定 slot WM_SLOT=2 workmux add my-feature # 创建 worktree 并立即向 agent 发送提示词 workmux add -A -p "fix the login bug in auth.rs"

add命令只创建 worktree 而不会自动打开它。创建后查看分配的端口:

cat <worktree-path>/.env.local

-A参数则会在创建后立即打开 worktree,并把-p指定的提示词直接交给 Claude Code agent 开始工作。

打开 worktree

workmux open my-feature

该命令打开一个 tmux 窗口,包含三个面板(agent 面板获得焦点):

  • Claude Code agent(聚焦面板)
  • 后端cargo watch -x run,监听该 worktree 分配的端口,保存即自动重载
  • 前端npm run dev,将请求代理到同一 worktree 的后端

向 agent 派发任务

# 向 worktree 中的 agent 发送提示词 workmux send my-feature "fix the login bug in auth.rs" # 查看所有 agent 状态 workmux status

合并与清理

Windmill 团队的协作纪律是:绝不直接 merge worktree,一律通过 GitHub PR 合入 main(可以请 worktree 内的 Claude Code agent 代为创建 PR)。PR 合并后:

# 关闭 tmux 窗口但保留 worktree workmux close my-feature # PR 合并后,删除 worktree、分支与 tmux 窗口 workmux rm my-feature

注意:不要使用workmux merge。所有变更都必须经过 PR 流程进入 main。

rm的清理动作由 scripts/pre-remove.sh 与 scripts/worktree-cleanup 承载:二者都会先调用wm_kill_processes_from_env_file依据.env.local中的端口杀死对应进程,再执行wm_shared_pre_remove删除该 worktree 专属数据库并移除对应的 EE worktree(见 scripts/worktree-common.sh)。

配置解析:post_create 钩子与依赖预置

文档描述的.workmux.yaml位于仓库根目录(由 workmux 管理,每个开发者本地生成),关键配置段如下:

  • post_create:worktree 创建后执行 scripts/worktree-env,生成带端口分配的.env.local
  • panes:定义 tmux 布局(agent / 后端 / 前端三面板);
  • files.copy:将 backend/.env 和scripts/目录复制进每个 worktree。

post_create钩子还会用cp -a复制frontend/node_modules——这是刻意为之:cp -a保留符号链接,而cp -r会解引用.bin/下的符号链接,破坏可执行入口。

这套"复制依赖"的完整逻辑在 scripts/worktree-common.sh 的wm_copy_dependencies中实现,除frontend/node_modules外还包括:

  • 复制主仓库的backend/.env
  • 复制cli/node_modules,并在 worktree 内执行npm install && npm run gen-client重新生成 CLI 客户端;
  • 复制编译好的wm-ts-nav二进制(wm-ts-nav/target/release/wm-ts-nav)。

每个 Worktree 独立数据库

wm_shared_post_create中的wm_setup_database会为每个 worktree 创建独立数据库,命名规则为windmill_${worktree名中的-替换为_},并自动执行迁移:

  • 默认CREATE DATABASE空库后,用sqlx migrate run --source backend/migrations应用全部迁移(迁移文件位于 backend/migrations);
  • 若设置WM_CLONE_DB=1,则以windmill库为模板克隆(CREATE DATABASE ... TEMPLATE windmill),适合需要完整演示数据的场景;
  • 还会从主库的global_settings中复制license_key,保证 EE 功能可用;
  • 生成的DATABASE_URL会被追加写入.env.local

对应地,删除 worktree 时数据库会被DROP DATABASE ... WITH (FORCE)清理,避免垃圾库堆积。

按需启动的前端 Dev Server(资源优化)

并行打开多个 worktree 时,前端 dev server 的内存开销不可忽视:一旦页面被访问,单个 vite dev server 常驻内存达1.1–1.7 GB。为此仓库提供了 frontend/scripts/dev-supervisor.mjs,由"监督进程"独占公开端口,仅在收到首个连接时才拉起真实 dev server(冷启动到可服务约 1 秒),流量停止后按空闲超时回收:

node scripts/dev-supervisor.mjs # 本 worktree,使用 $FRONTEND_PORT node scripts/dev-supervisor.mjs -t 3340:/path/wt-a -t 3350:/path/wt-b --idle 15m node scripts/dev-supervisor.mjs -t 3340:/path --idle 10m --stats rss.jsonl node scripts/dev-supervisor.mjs -t 3340:/path --bind 0.0.0.0 # 允许跨主机访问

关键参数说明:

  • -t/--target <port>[:<cwd>]:指定监督端口与对应 worktree 目录,可重复传入管理多个 worktree;省略时默认$FRONTEND_PORT与当前目录;
  • --idle <时长>:空闲回收阈值,支持ms/s/m/h后缀,默认15m;代码注释特别提醒该值必须大于应用自身的 5 分钟后台轮询休眠期,否则"从未休眠的标签页"可能被误回收且无法通过 websocket 重新唤醒;
  • --stats <文件>:周期性记录各目标 RSS 样本(JSONL),便于观测内存占用;
  • --bind:默认仅绑定127.0.0.1,如需跨主机访问才显式放宽。

其代理位于 TCP 层,HTTP、HMR websocket 与/api代理均原样穿透;WebSocket 连接不会唤醒已回收的服务(前端 y-websocket 会无条件重连,若允许唤醒会导致常驻),休眠标签页的唤醒依赖应用侧的 dormancy 预热机制。此外,前端应用在标签页非活跃 5 分钟后会挂起后台轮询(VITE_DEV_DORMANT_MS),进一步降低常驻内存;但该机制仅对明文 HTTP 生效——HTTPS=true时 HMR 连接与真实流量无法区分,打开的标签页会保持服务存活。这一优化策略的完整说明见 frontend/README_DEV.md。

企业版(EE)代码访问

Windmill 的企业版源码位于独立私有仓库windmill-ee-private(本仓库的兄弟目录)。创建 worktree 时,scripts/worktree-common.sh 的wm_setup_ee_worktree会自动在 EE 仓库中创建同名分支的配套 worktree,并写入.claude/settings.local.json,通过additionalDirectories授权 Claude Code 访问 EE 代码。

EE worktree 分支的确定顺序(源码可见):

  1. EE 仓库存在同名本地分支 → 直接worktree add
  2. 否则存在origin/<branch>--track创建并跟踪;
  3. 否则以 backend/ee-repo-ref.txt 中固定的 commit 引用(CI 读取同一文件)创建新分支——这是刻意设计:EE 仓库本地的main不会快进更新、会偏离 pin,而基于 pin 创建的 worktree 与 CI 编译的代码一致,保证本地cargo check --features private结果可信;
  4. 兜底:从 EE 仓库main创建新分支。

此外还会执行 backend/substitute_ee_code.sh(-d指定 EE worktree 目录)完成本地代码替换,使 CE 工作区与 EE 源码正确衔接。

Sandbox 模式的挂载配置

如果 workmux 以 sandbox 模式运行,容器需要显式挂载才能访问 EE 仓库。在全局配置~/.config/workmux/config.yaml中添加:

sandbox: extra_mounts: - host_path: ~/windmill-ee-private writable: true - host_path: ~/windmill-ee-private__worktrees writable: true

第一个路径挂载 EE 主仓库(主 worktree 使用),第二个挂载 EE worktrees 目录(feature worktree 使用),确保每个 sandbox 容器都能读到对应分支的 EE 源码。

Cargo Features 传递

构建后端时如需启用特定 Cargo features(如enterpriseparquet),通过环境变量CARGO_FEATURES传递。后端面板会从.env.local读取该值,并追加--features <value>cargo watch命令。

配合wm别名使用:

CARGO_FEATURES="enterprise,parquet" wm add my-feature

创建 worktree 时,post_create钩子(scripts/worktree-env)会把CARGO_FEATURES写入.env.local

if [[ -n "${CARGO_FEATURES:-}" ]]; then echo "CARGO_FEATURES=$CARGO_FEATURES" >> .env.local fi

后端面板启动时自动读取并生效,无需手工干预。

登录与本地验证

worktree 环境启动后,访问对应前端端口即可登录:

  • 默认账号:admin@windmill.dev
  • 默认密码:changeme

配合"每 worktree 独立数据库 + 自动迁移"的机制,登录后即可在完全隔离的环境中验证当前分支的前后端行为。

小结

Windmill 的 workmux 开发流程本质上是把"并行开发"工程化:git worktree 隔离代码,slot 端口系统隔离运行环境,独立数据库隔离数据,Claude Code 面板承接编码任务。其配套脚本(scripts/worktree-env、scripts/worktree-common.sh、frontend/scripts/dev-supervisor.mjs)沉淀了大量工程细节——符号链接保留、slot 冲突规避、EE 分支与 pin 对齐、dev server 按需启停——为大规模多分支并行开发提供了可直接复用的参考实现。新手可按"安装 →wm addwm openwm send"四步上手,进阶团队则可进一步定制post_create钩子与 sandbox 挂载,把该模式推广到任意复杂度的仓库。

【免费下载链接】windmillOpen-source developer platform to power your entire infra and turn scripts into webhooks, workflows and UIs. Fastest workflow engine (13x vs Airflow). Open-source alternative to Retool and Temporal.项目地址: https://gitcode.com/GitHub_Trending/wi/windmill

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询