Git worktrees
Worktrees live in .worktrees/ and are provisioned by name from the main
checkout:
just worktree-up NAME # URL, migrated + seeded database, test database, CSSjust worktree-up NAME card:<cardId> # the same, and link widget comments to that cardjust worktrees # list them with URL, database and sidecar statusjust worktree-down NAME # remove it along with its sidecar, route and databasesjust worktree-prune # drop sidecars and databases whose worktree is goneEach gets https://<slug>.<project>.dev.localhost and its own dev database, so
bin/console in a worktree never touches the main development database. Log in
with the seeded dev@loupe.test / password, or admin@loupe.test for the
admin area.
Rules that prevent real damage
Never remove one with a bare git worktree remove. That leaves the sidecar
container, the Traefik route and both databases behind, and the route then
serves 502s. Use just worktree-down.
Never match worktrees by directory name. A worktree called Feature_X owns
the slug feature-x; looking for a directory of that name would declare a live
worktree orphaned and drop its databases. The tooling matches on the derived
slug for exactly this reason.
Run just recipes from the worktree in a subshell, so the working directory
dies with the command:
( cd .worktrees/<name> && just worktree-up )The preview token of a worktree
just worktree-up mints a preview token
for the worktree. It writes the token to SITE_REVIEW_WIDGET_PREVIEW_TOKEN in
the .env.local of the worktree. The widget then signs in with no popup.
just worktree-down revokes the token.
The scripts pass the widget backend as --instance. A CLI that is signed in to
another instance mints nothing.
A failed mint never stops just worktree-up. The script prints one warning line
and writes an empty token, and the widget shows its Sign in button. The warning
appears when the CLI is missing, signed out, or signed in to another instance
than SITE_REVIEW_WIDGET_BACKEND.
Running tests in parallel
PHPUnit’s database is app_test<TEST_TOKEN>. just worktree-up writes a unique
TEST_TOKEN into each worktree’s .env.test.local, so each worktree gets its own
schema. ParaTest replaces an exported TEST_TOKEN with the worker number, so set
it in that file, not in the shell.
ParaTest adds the worker number, so worker 3 of card-116 uses
app_test_card_116__p3. just worktree-down drops the worker databases too.
The e2e suite is the exception and cannot be parallelised — Mailpit is shared.
Warm the cache before an e2e run
( cd .worktrees/<name> && bin/worktrees/compose-exec.sh bin/console cache:warmup )A run started against a cold var/cache/dev, or one whose cache is rebuilt
mid-flight by a concurrent just cs, produces false failures that look like
unrelated flaky specs: a request that never completes, a submit button left
disabled, and no validation errors — because the container-load fatal happens
before the logger exists, so nothing is logged at all.
If a spec fails and then passes in isolation, count the container hashes in
var/cache/dev/. More than one means a rebuild happened during the run. That is
a diagnosis, not a licence to re-run until green.