mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-25 08:00:59 +00:00
fix(dev): correct the worktree dev-environment guidance
Several things agents were told to do did not match what the machine does. - Env discovery pointed at .env / .env.local / backend/.env. In a webmux worktree the real values are in $(git rev-parse --git-dir)/webmux/runtime.env (BACKEND_PORT, FRONTEND_PORT, DATABASE_URL, CARGO_FEATURES, WM_DB_NAME), sourced by every pane and undocumented. Reading it is also not blocked by the Read(**/.env) deny rules, which the old instruction walked straight into. - The database name rule said branch-with-underscores. worktree-common.sh uses the worktree directory basename, and Postgres truncates at 63 characters, so branch hugo/win-2340-… resolves to windmill_win_2340_…_and_eval with no hugo_ prefix and the tail chopped. A wrong DATABASE_URL guts the sqlx cache. - The restart procedure said "tmux pane 1" and sent keys to an undefined <pane1>. Pane 1 is the backend under the full profile and the frontend under frontendOnly. Replaced with finding the pane by pane_current_command, recovering the live feature set from the running process (CARGO_FEATURES in runtime.env only records what the pane started with), and restarting in place. - Added recovery for an orphaned backend holding the port: it reparents to systemd when its shell dies, so it survives anything that looks like cleanup. Three checks before killing a single pid, because pkill -f windmill takes out every sibling worktree. - Agents spawned their own servers because AGENTS.md opened by telling them to. Now it checks for the existing panes first; the spawn commands are scoped to a plain checkout. - New EE worktrees branched from the EE repo's local main, which nothing fast-forwards, so they started behind the commit pinned in backend/ee-repo-ref.txt — the one CI builds against. They now base on the pin, falling back to main only when it is unreadable. - Enabled webmux autoPull so local main stays current; new worktrees are branched from it. Documented what WM_CLONE_DB does, including that it terminates every connection to the base windmill database. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -5,9 +5,19 @@ workspace:
|
||||
mainBranch: main
|
||||
worktreeRoot: ../windmill__worktrees
|
||||
defaultAgent: claude
|
||||
# A new worktree is branched from the *local* `main` ref, so a stale local main means every
|
||||
# new worktree starts behind. This keeps it current: fetch origin/main + fast-forward merge.
|
||||
# Fast-forward only — it no-ops rather than forcing if local main has diverged.
|
||||
autoPull:
|
||||
enabled: true
|
||||
intervalSeconds: 300
|
||||
|
||||
startupEnvs:
|
||||
CARGO_FEATURES: "quickjs"
|
||||
# true clones the base `windmill` DB via CREATE DATABASE ... TEMPLATE, which first
|
||||
# terminates every open connection to `windmill` — expect the main dev instance to drop.
|
||||
# false creates an empty DB and runs migrations. Either way the license key is copied over
|
||||
# and pre-remove drops the DB. See scripts/worktree-common.sh.
|
||||
WM_CLONE_DB: false
|
||||
USE_RUST_PLUGIN: false
|
||||
|
||||
|
||||
@@ -50,9 +50,15 @@ Open-source platform for internal tools, workflows, API integrations, background
|
||||
> defaults in this section apply only to a plain single checkout. **Discover the real
|
||||
> values before running anything** — see "Per-worktree ports and database" below.
|
||||
|
||||
**Check whether they are already running before starting anything.** In a webmux worktree
|
||||
(`$WEBMUX_WORKTREE_PATH` is set) the backend and frontend are already up in sibling tmux panes —
|
||||
use those, don't spawn your own. `tmux list-panes -t "$(tmux display-message -p -t "$TMUX_PANE"
|
||||
'#{window_id}')" -F '#{pane_index} #{pane_current_command}'` shows what is running; read its log
|
||||
with `tmux capture-pane`, and see `backend/CLAUDE.md` to restart it with different cargo features.
|
||||
A second server started in your own shell fights the first one for the port. The commands below
|
||||
are for a plain checkout with nothing running.
|
||||
|
||||
- **Backend**: `cargo run` from `backend/` (API at http://localhost:8000)
|
||||
- **DuckDB local jobs**: before running DuckDB scripts locally, build the FFI shared library with `cd backend/windmill-duckdb-ffi-internal && ./build_dev.sh`. Re-run it after clean builds or when `backend/target/debug/libwindmill_duckdb_ffi_internal.*` is missing. The bundled DuckDB compile (~2min) is cached in a per-user dir shared across worktrees, so a fresh worktree reuses it and the build is near-instant.
|
||||
- **Data pipelines (DuckLake) from source**: a plain `cargo run` (even `--features quickjs`) advertises a `duckdb` worker tag but **cannot** execute DuckDB scripts and has **no** working S3 proxy (DuckLake writes 404). Build CE DuckLake with `cargo run --features quickjs,duckdb,parquet,private` (add `,python` for Python scripts, `,enterprise,license` for EE) **and** build the FFI (bullet above). See `backend/CLAUDE.md` → "Running data pipelines (DuckLake) from source" for the exact feature sets and the two feature-gate gotchas.
|
||||
- **Frontend**: `REMOTE=http://localhost:8000 npm run dev` from `frontend/` (port 3000+)
|
||||
- **DB**: `psql postgres://postgres:changeme@localhost:5432/windmill`
|
||||
- **Login**: `admin@windmill.dev` / `changeme`
|
||||
@@ -61,10 +67,20 @@ Open-source platform for internal tools, workflows, API integrations, background
|
||||
|
||||
### Per-worktree ports and database
|
||||
|
||||
A worktree's `.env` / `.env.local` (repo root) and `backend/.env` hold its own
|
||||
`DATABASE_URL` and `PORT`; the database is typically `windmill_<branch_with_underscores>`
|
||||
(branch `dbt-runtime` → `windmill_dbt_runtime`). Read them, or discover from what is
|
||||
already running:
|
||||
In a webmux worktree the authoritative values live in
|
||||
`$(git rev-parse --git-dir)/webmux/runtime.env` — `BACKEND_PORT`, `FRONTEND_PORT`,
|
||||
`DATABASE_URL`, `CARGO_FEATURES`, `WM_DB_NAME`. Every pane sources it at startup. Read that
|
||||
first: it is not a `.env*` file, so the repo's secret-file read rules don't stand in the way.
|
||||
|
||||
In a plain checkout, fall back to `.env` / `.env.local` (repo root) and `backend/.env`.
|
||||
|
||||
The database is named after the **worktree directory, not the branch** (`scripts/worktree-common.sh`):
|
||||
`windmill_` + the directory basename with `-` → `_`, which Postgres then truncates at 63
|
||||
characters. Branch `hugo/win-2340-ai-agent-evals-standalone-agent-runs-and-eval-datasets` sits in
|
||||
a worktree directory named `win-2340-…`, so its database is
|
||||
`windmill_win_2340_ai_agent_evals_standalone_agent_runs_and_eval` — no `hugo_`, and the tail
|
||||
chopped. Take `WM_DB_NAME` from `runtime.env` instead of reconstructing the name. Read those, or
|
||||
discover from what is already running:
|
||||
|
||||
```bash
|
||||
psql postgres://postgres:changeme@localhost:5432/postgres -tAc \
|
||||
|
||||
+56
-10
@@ -32,23 +32,69 @@ MUST **restart the backend with the appropriate features** for what you're worki
|
||||
|
||||
### Restarting the dev backend with the right features
|
||||
|
||||
The backend runs in tmux pane 1 as `cargo watch -x "run --features <…>"`. To restart it with a
|
||||
different feature set — scope kills by pid/cwd, **never** `pkill -f target/debug/windmill` (it
|
||||
kills every sibling worktree's backend):
|
||||
Restart in the **same pane**, so the relaunch inherits that pane's `DATABASE_URL`, `BACKEND_PORT`
|
||||
and the rest of `runtime.env`. Scope every kill to this worktree — **never**
|
||||
`pkill -f target/debug/windmill`, which kills every sibling worktree's backend.
|
||||
|
||||
1. Find the backend pane by what it is running, not by index. The index depends on the webmux
|
||||
profile: pane 1 is the backend under `full`, but the *frontend* under `frontendOnly`.
|
||||
|
||||
1. Stop the current run: `tmux send-keys -t <pane1> C-c`, then kill *this worktree's*
|
||||
`cargo-watch` pid (find it via `/proc/<pid>/cwd`).
|
||||
2. Relaunch in the same pane so it inherits the shell's `DATABASE_URL` etc.; the pane env's
|
||||
`PORT` may be stale, so set it explicitly:
|
||||
```bash
|
||||
export PORT=$BACKEND_PORT
|
||||
cargo watch -x "run --features enterprise,private,parquet,quickjs"
|
||||
WIN=$(tmux display-message -p -t "$TMUX_PANE" '#{window_id}')
|
||||
tmux list-panes -t "$WIN" -F '#{pane_index} #{pane_current_command} #{pane_pid}'
|
||||
```
|
||||
3. Wait for `health check completed` in the pane before hitting the API.
|
||||
|
||||
2. Read the feature set it is **actually** running. `CARGO_FEATURES` in `runtime.env` is only what
|
||||
the pane started with, and goes stale the first time anyone restarts by hand:
|
||||
|
||||
```bash
|
||||
ps --ppid <pane_pid> -o args=
|
||||
# /home/hugo/.cargo/bin/cargo-watch watch -x run --features quickjs
|
||||
```
|
||||
|
||||
3. Stop it and relaunch with the extended set. `PORT` in the pane shell can be stale, so pass it
|
||||
explicitly:
|
||||
|
||||
```bash
|
||||
tmux send-keys -t "$WIN.<idx>" C-c
|
||||
tmux send-keys -t "$WIN.<idx>" 'PORT=$BACKEND_PORT cargo watch -x "run --features quickjs,private,parquet"' Enter
|
||||
```
|
||||
|
||||
Carry over every feature the old command had unless you mean to drop one — rebuilding the list
|
||||
from memory is how a backend silently loses `quickjs`.
|
||||
|
||||
4. Persist the new set so a recreated pane starts with it: set `CARGO_FEATURES` in
|
||||
`$(git rev-parse --git-dir)/webmux/runtime.env`. That file is read at pane startup only, so it
|
||||
changes nothing about the process you just relaunched — step 3 is what takes effect now.
|
||||
|
||||
5. Re-capture the pane until `health check completed` appears before hitting the API. A cold
|
||||
rebuild takes ~60s, and the previous run's success line is still in the scrollback, so a
|
||||
capture taken too early reads as ready when it isn't.
|
||||
|
||||
cargo-watch only re-runs on a file change, so after an idle/failed run `touch README.md` (from
|
||||
`backend/`, where the watch runs) is a cheap retrigger (touching a `.rs` forces a full rebuild).
|
||||
|
||||
### An orphaned backend is holding the port
|
||||
|
||||
If the pane's `cargo watch` looks alive but the API never answers, or the build ends in an
|
||||
address-already-in-use error, a backend from an earlier run is probably still bound to the port.
|
||||
It gets reparented to `systemd --user` when its shell dies, so it survives everything that looks
|
||||
like a cleanup.
|
||||
|
||||
Confirm all three before killing anything — a dozen sibling worktrees run their own backend, and
|
||||
`pkill -f windmill` (or `-f target/debug/windmill`) kills every one of them:
|
||||
|
||||
```bash
|
||||
ss -ltnp | grep ":$BACKEND_PORT" # 1. which pid holds the port
|
||||
readlink /proc/<pid>/cwd # 2. must be THIS worktree's backend/
|
||||
ps -o ppid= -p <pid> # 3. parent is systemd/pid 1, not your pane's cargo-watch
|
||||
```
|
||||
|
||||
Only when the port owner is this worktree's backend **and** it is orphaned, kill that single pid
|
||||
(`kill <pid>`, then `kill -9` if it does not exit). Ask first when there is a human in the loop;
|
||||
unattended, the three checks are what make it safe. Then `touch README.md` to retrigger the
|
||||
watch.
|
||||
|
||||
### What each feature gate does (the ones you'll actually toggle)
|
||||
|
||||
`backend/Cargo.toml` `[features]` is the source of truth; this is the practical dev map. Combine
|
||||
|
||||
@@ -164,10 +164,20 @@ wm_find_ee_repo() {
|
||||
return 1
|
||||
}
|
||||
|
||||
# The EE commit this CE checkout builds against. CI reads the same file, so basing a new EE
|
||||
# worktree on it keeps a local `cargo check --features private` on the tree CI compiles. Local
|
||||
# `main` in the EE repo is not a substitute: nothing fast-forwards it, so it drifts behind the pin.
|
||||
wm_ee_pinned_ref() {
|
||||
local repo_root=$1 ref
|
||||
ref="$(tr -d '[:space:]' < "${repo_root}/backend/ee-repo-ref.txt" 2>/dev/null)" || return 1
|
||||
[[ -n "$ref" ]] || return 1
|
||||
printf '%s' "$ref"
|
||||
}
|
||||
|
||||
wm_setup_ee_worktree() {
|
||||
local repo_root=$1
|
||||
local main_repo_root=$2
|
||||
local ee_repo branch wt_basename ee_worktree_dir ee_rel rust_plugin
|
||||
local ee_repo branch wt_basename ee_worktree_dir ee_rel rust_plugin ee_ref
|
||||
|
||||
if ! ee_repo="$(wm_find_ee_repo "$repo_root" "$main_repo_root")"; then
|
||||
return
|
||||
@@ -189,8 +199,11 @@ wm_setup_ee_worktree() {
|
||||
elif git -C "$ee_repo" show-ref --verify --quiet "refs/remotes/origin/$branch" \
|
||||
&& git -C "$ee_repo" worktree add --track -b "$branch" "$ee_worktree_dir" "origin/$branch" 2>/dev/null; then
|
||||
echo "Created EE worktree at $ee_worktree_dir (tracking origin/$branch)"
|
||||
elif ee_ref="$(wm_ee_pinned_ref "$repo_root")" \
|
||||
&& git -C "$ee_repo" worktree add -b "$branch" "$ee_worktree_dir" "$ee_ref" 2>/dev/null; then
|
||||
echo "Created EE worktree at $ee_worktree_dir (new branch: $branch from pinned ${ee_ref:0:12})"
|
||||
elif git -C "$ee_repo" worktree add -b "$branch" "$ee_worktree_dir" main 2>/dev/null; then
|
||||
echo "Created EE worktree at $ee_worktree_dir (new branch: $branch from main)"
|
||||
echo "Created EE worktree at $ee_worktree_dir (new branch: $branch from main — pin unavailable)"
|
||||
else
|
||||
echo "Warning: Could not create EE worktree for branch $branch"
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user