From c0d136658fecc0d660e36b75e8a6230b102ea349 Mon Sep 17 00:00:00 2001 From: hugocasa Date: Tue, 24 Feb 2026 17:34:00 +0100 Subject: [PATCH] Improve workmux dev workflow scripts and docs (#8078) - Add CARGO_FEATURES passthrough: backend pane reads from .env.local, wm-cursor supports --features flag on add/open commands - Fix node_modules copy in worktrees: use cp -a to preserve .bin/ symlinks that cp -r would dereference (fixes openapi-ts errors) - Fix EE repo discovery from worktrees: resolve main repo root via git-common-dir, search multiple candidate paths - Add cursor session cleanup to worktree-cleanup (pre_remove hook) - Use workmux -b flag in wmc add, remove npm install from frontend pane - Change openBrowserOnce for Cursor port forwarding - Document cargo features usage and fix stale files.symlink reference in README Co-authored-by: Claude Opus 4.6 --- .workmux.yaml | 4 +-- README_WORKMUX_DEV.md | 31 +++++++++++++++++++- scripts/wm-cursor | 67 ++++++++++++++++++++++++++++++++++++------- scripts/worktree-env | 34 ++++++++++++++++------ 4 files changed, 114 insertions(+), 22 deletions(-) diff --git a/.workmux.yaml b/.workmux.yaml index 15ada48482..ec97aad3cf 100644 --- a/.workmux.yaml +++ b/.workmux.yaml @@ -57,9 +57,9 @@ panes: When restarting backend or frontend, make sure to use the ports listed in .env.local.\n Because we are running backend with cargo watch, to verify your changes, just check the logs in the backend pane. No need for cargo check." focus: true - - command: 'ROOT="$(git rev-parse --show-toplevel)"; [ -f "$ROOT/.env.local" ] && source "$ROOT/.env.local"; cd "$ROOT/backend" && PORT=${BACKEND_PORT:-8000} cargo watch -x run' + - command: 'ROOT="$(git rev-parse --show-toplevel)"; [ -f "$ROOT/.env.local" ] && source "$ROOT/.env.local"; cd "$ROOT/backend" && PORT=${BACKEND_PORT:-8000} cargo watch -x "run ${CARGO_FEATURES:+--features $CARGO_FEATURES}"' split: horizontal - - command: 'ROOT="$(git rev-parse --show-toplevel)"; [ -f "$ROOT/.env.local" ] && source "$ROOT/.env.local"; cd "$ROOT/frontend" && npm install && npm run generate-backend-client && REMOTE=${REMOTE:-http://localhost:${BACKEND_PORT:-8000}} npm run dev -- --port ${FRONTEND_PORT:-3000} --host 0.0.0.0' + - command: 'ROOT="$(git rev-parse --show-toplevel)"; [ -f "$ROOT/.env.local" ] && source "$ROOT/.env.local"; cd "$ROOT/frontend" && npm run generate-backend-client && REMOTE=${REMOTE:-http://localhost:${BACKEND_PORT:-8000}} npm run dev -- --port ${FRONTEND_PORT:-3000} --host 0.0.0.0' split: vertical files: diff --git a/README_WORKMUX_DEV.md b/README_WORKMUX_DEV.md index facaa4e863..bfb2d7e576 100644 --- a/README_WORKMUX_DEV.md +++ b/README_WORKMUX_DEV.md @@ -170,7 +170,8 @@ 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 + +The `post_create` hook also copies `frontend/node_modules` using `cp -a` (preserves `.bin/` symlinks that `cp -r` would dereference). ## Enterprise (EE) Code Access @@ -247,6 +248,34 @@ wmc close my-feature This kills the grouped tmux session and calls `workmux close` to close the tmux window. The worktree and branch are preserved. Grouped sessions are also automatically cleaned up when you `workmux rm` a worktree (via `scripts/worktree-cleanup`). +## Cargo Features + +To build the backend with specific Cargo features (e.g., `enterprise`, `parquet`), pass them via `CARGO_FEATURES`. The backend pane reads this from `.env.local` and appends `--features ` to the `cargo watch` command. + +**With `wm` (workmux):** + +Set `CARGO_FEATURES` as an environment variable before creating the worktree: + +```bash +CARGO_FEATURES="enterprise,parquet" wm add my-feature +``` + +This gets written to `.env.local` by the `post_create` hook (`scripts/worktree-env`), and the backend pane picks it up automatically. + +**With `wmc` (wm-cursor):** + +Use the `--features` flag: + +```bash +# Create a new worktree with features +wmc add --features "enterprise,parquet" -A -p "implement feature X" + +# Open an existing worktree with different features +wmc open my-feature --features "enterprise,parquet" +``` + +The `--features` flag exports `CARGO_FEATURES` so the `post_create` hook writes it to `.env.local`. When using `wmc open`, it updates the existing `.env.local` with the new features. + ## Login Default credentials: `admin@windmill.dev` / `changeme` diff --git a/scripts/wm-cursor b/scripts/wm-cursor index d45c1e7431..552bdcd985 100755 --- a/scripts/wm-cursor +++ b/scripts/wm-cursor @@ -81,7 +81,7 @@ setup_grouped_session() { }, "remote.portsAttributes": { "'$backend_port'": { "label": "Backend", "onAutoForward": "silent" }, - "'$frontend_port'": { "label": "Frontend", "onAutoForward": "openBrowser" } + "'$frontend_port'": { "label": "Frontend", "onAutoForward": "openBrowserOnce" } }' fi fi @@ -102,18 +102,47 @@ SETTINGS fi } +# --- Feature flag parsing --- +# Extracts --features from args, exports CARGO_FEATURES, returns remaining args. +# Usage: parse_features_flag "$@"; set -- "${remaining_args[@]}" + +parse_features_flag() { + remaining_args=() + while (( $# )); do + case $1 in + --features) + if (( $# < 2 )); then + print -u2 "Error: --features requires a value" + exit 1 + fi + export CARGO_FEATURES=$2 + shift 2 + ;; + --features=*) + export CARGO_FEATURES=${1#--features=} + shift + ;; + *) + remaining_args+=("$1") + shift + ;; + esac + done +} + # --- Subcommands --- cmd_add() { ensure_tmux check_dev_db + parse_features_flag "$@" + set -- "${remaining_args[@]}" + # Snapshot worktree list before local -a before=("${(@f)$(git worktree list --porcelain | grep '^worktree ')}") - local prev_window=$(tmux display-message -p '#I') - workmux add "$@" - tmux select-window -t $prev_window + workmux add -b "$@" # Diff to find the new entry local -a after=("${(@f)$(git worktree list --porcelain | grep '^worktree ')}") @@ -140,11 +169,24 @@ cmd_open() { ensure_tmux check_dev_db + parse_features_flag "$@" + set -- "${remaining_args[@]}" + + # Write CARGO_FEATURES to .env.local if specified + if [[ -n ${CARGO_FEATURES-} ]]; then + local wt_env=$(workmux path $name)/.env.local + if [[ -f $wt_env ]]; then + # Remove existing CARGO_FEATURES line and append new one + sed -i '/^CARGO_FEATURES=/d' $wt_env + echo "CARGO_FEATURES=$CARGO_FEATURES" >> $wt_env + fi + fi + local wt_path=$(workmux path $name) - local prev_window=$(tmux display-message -p '#I') + local prev_target=$(tmux display-message -p '#{session_name}:#{window_index}') workmux open $name "$@" - tmux select-window -t $prev_window + tmux select-window -t $prev_target setup_grouped_session $name $wt_path $cursor_bin -n $wt_path print "Opened Cursor for ${name}" @@ -210,7 +252,7 @@ TASKS }, "remote.portsAttributes": { "8000": { "label": "Backend", "onAutoForward": "silent" }, - "3000": { "label": "Frontend", "onAutoForward": "openBrowser" }, + "3000": { "label": "Frontend", "onAutoForward": "openBrowserOnce" }, "5432": { "label": "PostgreSQL", "onAutoForward": "silent" } } }' @@ -257,10 +299,13 @@ case ${1-} in print -u2 "Usage: wm-cursor [args...]" print -u2 "" print -u2 "Subcommands:" - print -u2 " add [workmux-add-args...] Create worktree + open Cursor" - print -u2 " open Open Cursor for existing worktree" - print -u2 " close Clean up grouped tmux session" - print -u2 " setup Set up .vscode settings, tasks + wmc alias" + print -u2 " add [--features ] [workmux-add-args...] Create worktree + open Cursor" + print -u2 " open [--features ] Open Cursor for existing worktree" + print -u2 " close Clean up grouped tmux session" + print -u2 " setup Set up .vscode settings, tasks + wmc alias" + print -u2 "" + print -u2 "Options:" + print -u2 " --features Cargo features for the backend (e.g. \"enterprise,parquet\")" exit 1 ;; esac diff --git a/scripts/worktree-env b/scripts/worktree-env index a90e8e4454..5edacccf20 100755 --- a/scripts/worktree-env +++ b/scripts/worktree-env @@ -46,8 +46,20 @@ FRONTEND_PORT=$frontend_port REMOTE=http://localhost:$backend_port EOF +if [[ -n "${CARGO_FEATURES:-}" ]]; then + echo "CARGO_FEATURES=$CARGO_FEATURES" >> .env.local +fi + echo "Created .env.local with ports: backend=$backend_port, frontend=$frontend_port" +# --- Copy frontend/node_modules preserving symlinks --- +# cp -a preserves .bin/ symlinks that cp -r would dereference, breaking require() paths +main_repo_root="$(cd "$(git rev-parse --git-common-dir 2>/dev/null)/.." && pwd)" +if [[ -n "$main_repo_root" && -d "$main_repo_root/frontend/node_modules" ]]; then + cp -a "$main_repo_root/frontend/node_modules" frontend/ + echo "Copied frontend/node_modules (with symlinks preserved)" +fi + # --- Allow direnv so the nix devshell activates in pane commands --- if command -v direnv &>/dev/null && [ -f .envrc ]; then direnv allow @@ -55,14 +67,20 @@ if command -v direnv &>/dev/null && [ -f .envrc ]; then fi # --- Create matching windmill-ee-private worktree --- -# Check parent directory first (sibling to worktree root), then fall back to home -parent_dir="$(cd "$(pwd)/.." && pwd)" -if [ -d "${parent_dir}/windmill-ee-private" ]; then - ee_repo="${parent_dir}/windmill-ee-private" -else - ee_repo="${HOME}/windmill-ee-private" -fi -if [ -d "$ee_repo" ]; then +# Find ee repo: sibling to the main worktree (git toplevel of the main checkout), +# then try parent of cwd, then fall back to home +ee_repo="" +for candidate in \ + "${main_repo_root:+${main_repo_root}/../windmill-ee-private}" \ + "$(pwd)/../windmill-ee-private" \ + "${HOME}/windmill-ee-private" \ + "${HOME}/projects/windmill-ee-private"; do + if [ -n "$candidate" ] && [ -d "$candidate" ]; then + ee_repo="$(cd "$candidate" && pwd)" + break + fi +done +if [ -n "$ee_repo" ]; then branch=$(git branch --show-current 2>/dev/null || true) wt_basename=$(basename "$(pwd)") ee_worktree_dir="${ee_repo}__worktrees/${wt_basename}"