From 7052a36026e3f55bd7cd94c0bb35aac9e2b48599 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Thu, 19 Feb 2026 12:00:51 +0000 Subject: [PATCH] workmux setup --- .workmux.yaml | 2 +- README_WORKMUX_DEV.md | 141 ++++++++++++++++++++++++++++++++++++++++++ scripts/worktree-env | 32 ++++++++-- 3 files changed, 168 insertions(+), 7 deletions(-) create mode 100644 README_WORKMUX_DEV.md diff --git a/.workmux.yaml b/.workmux.yaml index 36a57ab13c..ea62ec687d 100644 --- a/.workmux.yaml +++ b/.workmux.yaml @@ -29,7 +29,7 @@ post_create: panes: - command: focus: true - - command: "[ -f .env.local ] && source .env.local; cd backend && PORT=${BACKEND_PORT:-8000} cargo run" + - command: "[ -f .env.local ] && source .env.local; cd backend && PORT=${BACKEND_PORT:-8000} cargo watch -x run" split: horizontal - command: "[ -f .env.local ] && source .env.local; cd frontend && REMOTE=${REMOTE:-http://localhost:${BACKEND_PORT:-8000}} npm run dev -- --port ${FRONTEND_PORT:-3000}" split: vertical diff --git a/README_WORKMUX_DEV.md b/README_WORKMUX_DEV.md new file mode 100644 index 0000000000..c35c2e9038 --- /dev/null +++ b/README_WORKMUX_DEV.md @@ -0,0 +1,141 @@ +# Windmill Development with workmux + +This guide covers the workmux-based development setup for Windmill. Each worktree gets its own tmux window with a Claude Code agent, a backend server (with auto-reload), and a frontend dev server — all on isolated ports. + +## Prerequisites + +- tmux +- Rust toolchain (rustup) +- Node.js + npm +- PostgreSQL running locally (see `backend/.env`) + +## Installation + +### 1. Install workmux + +```bash +cargo install workmux +``` + +### 2. Install the Claude Code plugin + +```bash +workmux claude install +``` + +This lets workmux manage Claude Code agents in worktree panes. + +### 3. Install cargo-watch + +Used for auto-recompiling the backend on file changes: + +```bash +cargo install cargo-watch +``` + +## Port Slot System + +Each worktree is assigned a **slot** that determines its ports: + +| Slot | Backend | Frontend | +|------|---------|----------| +| 0 | 8000 | 3000 | +| 1 | 8010 | 3010 | +| 2 | 8020 | 3020 | +| 3 | 8030 | 3030 | +| ... | ... | ... | + +- **Slot 0** is reserved for the main worktree (default `cargo run` / `npm run dev`). +- Without `WM_SLOT`, the script auto-assigns the first available slot (starting from 1) and prints it. +- With `WM_SLOT=N`, it uses that slot and errors if the ports are taken. + +## SSH Port Forwarding + +If you develop over SSH, add this to `~/.ssh/config` on your **local machine** to pre-configure tunnels for each slot: + +``` +Host windmill-dev + HostName + User + # 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 +``` + +Then connect once and all tunnels are active: + +```bash +ssh windmill-dev +``` + +Access the frontend at `http://localhost:` in your local browser. + +## Quickstart + +```bash +# Create a new worktree (auto-assigns slot, prints ports) +workmux add my-feature + +# Or with an explicit slot +WM_SLOT=2 workmux add my-feature +``` + +This will: + +1. Create a git worktree + branch `my-feature` +2. Run `scripts/worktree-env` to assign ports and write `.env.local` +3. Open a tmux window with three panes: + - **Claude Code agent** (focused) + - **Backend**: `cargo watch -x run` on the assigned port (auto-reloads on save) + - **Frontend**: `npm run dev` proxying to the backend + +Check which ports were assigned: + +```bash +cat /.env.local +``` + +### Sending work to the agent + +```bash +# Send a prompt to the agent in a worktree +workmux send my-feature "fix the login bug in auth.rs" + +# Check agent status +workmux status +``` + +### Cleaning up + +```bash +# Close the tmux window but keep the worktree +workmux close my-feature + +# Remove everything (worktree, branch, tmux window) +workmux rm my-feature + +# Merge into main, then clean up +workmux merge my-feature +``` + +## Configuration + +The setup is defined in `.workmux.yaml` at the repo root. Key sections: + +- **`post_create`**: Runs `scripts/worktree-env` to generate `.env.local` with port assignments +- **`panes`**: Defines the tmux layout (agent, backend, frontend) +- **`files.copy`**: Copies `backend/.env` and `scripts/` into each worktree +- **`files.symlink`**: Symlinks `node_modules` and `.svelte-kit` to avoid reinstalling per worktree + +## Login + +Default credentials: `admin@windmill.dev` / `changeme` diff --git a/scripts/worktree-env b/scripts/worktree-env index 944a5cc8b3..765d7331ba 100755 --- a/scripts/worktree-env +++ b/scripts/worktree-env @@ -13,13 +13,33 @@ find_port() { echo "$port" } -# Hash the handle to get a deterministic port offset (0-99) -hash=$(echo -n "$WM_HANDLE" | md5sum | cut -c1-4) -offset=$((16#$hash % 100)) +if [[ -z "${WM_SLOT:-}" ]]; then + # Auto-assign: find the first slot (1-99) where both ports are free + # Slot 0 (8000/3000) is reserved for the main worktree + for slot in $(seq 1 99); do + bp=$((8000 + slot * 10)) + fp=$((3000 + slot * 10)) + if ! port_in_use "$bp" && ! port_in_use "$fp"; then + WM_SLOT=$slot + break + fi + done + if [[ -z "${WM_SLOT:-}" ]]; then + echo "ERROR: No available slot found (tried 1-99)" >&2 + exit 1 + fi + echo "Auto-assigned slot $WM_SLOT" +fi -# Find available ports starting from the hash-based offset -backend_port=$(find_port $((8000 + offset * 10))) -frontend_port=$(find_port $((3000 + offset * 10))) +# Slot-based: predictable ports for SSH forwarding +# Slot 0 = 8000/3000, slot 1 = 8010/3010, slot 2 = 8020/3020, etc. +backend_port=$((8000 + WM_SLOT * 10)) +frontend_port=$((3000 + WM_SLOT * 10)) + +if port_in_use "$backend_port" || port_in_use "$frontend_port"; then + echo "ERROR: Slot $WM_SLOT ports ($backend_port/$frontend_port) already in use" >&2 + exit 1 +fi # Generate .env.local with port overrides cat > .env.local <