Skip to content

Development

Whatever you touch in the Windmill UI has to come back to git.

Terminal window
pnpm run windmill:diff # what differs between the UI and git (does not push)
pnpm run windmill:pull # take the UI's changes into git
pnpm run windmill:push # make git authoritative and apply it

Both directions delete. push removes remote items that are absent locally; pull removes local files that are absent remotely. Commit before syncing and push before pulling — see Windmill CE.

windmill/wmill.yaml sets includeSchedules: true. Secrets are not synced (skipSecrets); u/admin/waggle_token is injected through the API by scripts/capture-ledger-token.ts.

The PNGs on The UI are generated by scripts/docs-shots.ts — none are taken by hand. Re-take them when the UI changes (i.e. when you bump the Windmill pin in docker-compose.yml). check-doc-refs fails when the compose pin and the version in shots-manifest.json disagree, so CI stops you if you forget.

Terminal window
# 1. Bring the stack up and create the run history the script needs (two of them):
./setup.sh && container-compose up -d -b # + the capture-ledger API / issuer
# - one successful crawl_level
# - one failed report_level (any failure)
# 2. Capture (Chromium is fetched by hand the first time; puppeteer is docs-only)
./node_modules/.bin/puppeteer browsers install chrome
pnpm run docs:shots

When you drop puppeteer, drop its //devDependencies note in package.json too.

path what
windmill/f/waggle/*.ts the scripts Windmill runs, under Bun
windmill/f/waggle/crawl_level.flow/flow.yaml the flow that runs one crawl level
windmill/f/waggle/daily.schedule.yaml when the nightly crawl fires
test/ unit tests; never put these under windmill/f/ — sync push would deploy them
scripts/*.ts host-side helpers (bootstrap, tokens, checks)
.env.local written by the tools (WINDMILL_TOKEN); git ignores it
.dev/capture-ledger.env the four lines for capture-ledger. Its pnpm run connect reads them

Adding an environment variable is a three-part contract, checked in both directions by scripts/check-env.ts: .env.example, the name lists in scripts/env.ts, and a literal-string read.

Every runnable script hands node .env and then .env.local (two --env-file-if-exists flags). The later one wins, so a name present in both takes its value from .env.local.

They are split because they have different owners. .env holds what a person decided; .env.local holds what only running things can decide (the Windmill token). When a tool reorders or drops a line a person wrote, nobody can tell afterwards what happened.

Write only inside your own repo; to cross, read. windmill:bootstrap is a command you run in capture-scheduler, so it never writes into capture-ledger — the four lines go into .dev/capture-ledger.env, and fetching them is the other side’s pnpm run connect. A repo changing because of a command typed somewhere else is not what the person typing it expects.

When you suspect a stale .env.local, the fastest fix is to regenerate both: run pnpm run windmill:bootstrap again, then pnpm run connect in capture-ledger. Both are safe to repeat (tokens accumulate, and the old ones stay valid).

  1. Nobody is told when it fails. Schedule error handlers are a Windmill Enterprise feature. A failure leaves a red entry in the run history and waits to be noticed.
  2. No production topology is decided. Apple Container is for development.

Windmill itself is AGPLv3. Running it internally for your own purposes is unconstrained; redistributing it as part of a product means complying with AGPLv3 or buying a commercial licence.