Skip to content

Fix: docker override URL, worktree .env isolation, ruff version, dependency upgrades#501

Open
paribaker wants to merge 7 commits into
mainfrom
fix/issues-495-496-497-499
Open

Fix: docker override URL, worktree .env isolation, ruff version, dependency upgrades#501
paribaker wants to merge 7 commits into
mainfrom
fix/issues-495-496-497-499

Conversation

@paribaker
Copy link
Copy Markdown
Contributor

Summary

Closes #495, closes #496, closes #497, closes #499

Test plan

  • Verify just up works in non-Traefik mode (client can reach server via http://server:8000)
  • Verify just worktree add <branch> copies .env and patches PROJECT/DB_NAME/PLAYWRIGHT_TEST_BASE_URL
  • Verify two worktrees can run simultaneously without container-name or DB collisions
  • Verify uv sync resolves cleanly with updated ruff and dependency versions
  • Run linting CI to confirm ruff >=0.9.8 passes

🤖 Generated with Claude Code

…on, and dependency upgrades

- Fix VITE_DEV_BACKEND_URL in docker-compose.override.yml to use container
  hostname (server:8000) instead of localhost:8000 (#495)
- Copy and patch .env when creating worktrees to isolate PROJECT, DB_NAME,
  and PLAYWRIGHT_TEST_BASE_URL per worktree (#496)
- Upgrade ruff minimum from >=0.1.9 to >=0.9.8 in bootstrapper root
  pyproject.toml to match the template (#497)
- Upgrade boto3 to 1.42.63 and add pydantic-ai>=1.84.0 (#499)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@whusterj whusterj temporarily deployed to tn-spa-bootstrapper-pr-501 May 10, 2026 17:51 Inactive
@whusterj whusterj temporarily deployed to tn-spa-bootstrapper-pr-501 May 10, 2026 21:30 Inactive
Pari Work Temp and others added 2 commits May 18, 2026 11:27
…e generated my_project

- Ruff: pre-commit rev v0.8.6 → v0.9.8, pin ruff==0.9.8 in linting.yml
- Pydantic-AI: minimum already at >=1.84.0, boto3 pinned to 1.42.63
- Vite 8 migration: vite ^8.0.16, vitest ^4.1.8, plugin-react-swc ^4.3.1
- Add @vitest/coverage-v8, bump qs to ^6.15.2
- Update vitest reference type in vite.config.ts for Vite 8
- Templatize uv.lock project name with cookiecutter syntax
- Remove generated my_project/ test artifact

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@paribaker paribaker temporarily deployed to tn-spa-bootstrapper-pr-501 June 2, 2026 12:09 Inactive
…vars

just's dotenv-load does not override env vars already set in the shell.
When switching between worktrees, stale values cause docker compose to
interpolate wrong DB_NAME/DB_USER/DB_PASS in the YAML.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@whusterj whusterj temporarily deployed to tn-spa-bootstrapper-pr-501 June 2, 2026 14:26 Inactive
…t env

The worktree recipe runs in the main project's context, so just's
dotenv-load has already loaded the main project's .env vars. When it
calls just setup-dev in the worktree, the child just process inherits
those vars and dotenv-load won't override them. This causes django-migrate
and create-test-data to use wrong DB credentials (from the parent project).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@whusterj whusterj temporarily deployed to tn-spa-bootstrapper-pr-501 June 2, 2026 15:28 Inactive
@paribaker
Copy link
Copy Markdown
Contributor Author

paribaker commented Jun 3, 2026

Worktree setup issues and fixes

1. django-migrate fails with connection refused

When running just worktree add <branch>, the setup-dev recipe calls django-migrate which runs on the host (uv run python manage.py migrate connecting to 127.0.0.1:5432). In Traefik mode, postgres port 5432 is never exposed to the host — it's only accessible inside the Docker network.

Root cause

docker-postgres-redis in Traefik mode uses:

docker compose -f docker-compose.yaml -f compose/docker-compose.traefik.yml up -d postgres redis

This skips docker-compose.override.yml, so no ports: "5432:5432" mapping. The host-side django-migrate can't reach postgres.

Fix

Don't run migrate in the worktree recipe at all — the server container's /start entrypoint already runs it. Instead, wait for the server to finish starting (i.e. runserver is listening), then only run create_test_data.

Additionally, setup-dev should not be delegated to the worktree's justfile since it may be an older version without fixes. The worktree recipe should inline the setup steps.


2. Concurrent migrate causes DuplicateTable / DuplicateColumn errors

Root cause

The server container's /start script runs python server/manage.py migrate on startup. The worktree recipe was also running docker compose exec -T server python server/manage.py migrate after the container started. The wait loop only checked if the container was running (exec true), not if the first migrate had finished — so both ran concurrently, causing errors like:

psycopg2.errors.DuplicateTable: relation "tn_surveys_core_user" already exists
psycopg2.errors.DuplicateColumn: column "job_status" of relation "import_export_celery_exportjob" already exists

Fix

Remove the redundant migrate call. Wait for the runserver to actually be listening on port 8000 (which means /start finished migrating), then only run create_test_data.


3. docker exec fails on worktree containers: "mount namespace breakout detected"

Root cause

Docker Desktop 29.x + git worktrees trigger a false-positive security check. The server container bind-mounts the worktree directory (.:/app), but a git worktree's .git is a file pointing outside the directory:

gitdir: /path/to/main-repo/.git/worktrees/<name>

Docker detects this as a potential container breakout and blocks all docker exec calls that touch the /app bind mount — even with -w /app or -w /tmp, any command that accesses /app fails:

OCI runtime exec failed: exec failed: unable to start container process:
current working directory is outside of container mount namespace root
-- possible container breakout detected

The running container itself can access /app fine (that's how /start runs migrate), but docker exec processes cannot.

Fix

  • Readiness check: Use docker exec -w /tmp with a socket test (no /app access needed):
    docker exec -w /tmp "$container" python -c \
      "import socket; s=socket.create_connection(('localhost',8000),1); s.close()"
  • Management commands: Use docker compose run --rm -T which starts a fresh container that CAN access the bind mount:
    docker compose run --rm -T server python server/manage.py create_test_data

4. sed delimiter conflict in branch name stripping

Root cause

The sed command used | as the delimiter, but the regex alternation inside the group also uses |:

# BROKEN — sed sees (feature| as the full pattern
sed -E 's|^(feature|bugfix|hotfix|fix|chore|release)/||'

Fix

Use , as the delimiter:

sed -E 's,^(feature|bugfix|hotfix|fix|chore|release)/,,'

5. Second worktree fails: port is already allocated

Root cause

When Traefik is not already set up, just up falls into the non-Traefik path:

# just up — else branch (no proxy network)
PROJECT=$project docker compose up -d

Plain docker compose up -d auto-applies docker-compose.override.yml, which has hardcoded host port bindings:

# docker-compose.override.yml
postgres:
  ports:
    - "5432:5432"    # hardcoded — same for every worktree
redis:
  ports:
    - "6379:6379"
server:
  ports:
    - "8000:8000"
client:
  ports:
    - "8080:8080"

The first worktree binds these ports successfully. The second worktree tries to bind the same ports and fails:

Error response from daemon: Bind for 0.0.0.0:5432 failed: port is already allocated

Container names and database names are correctly uniquified per worktree (${PROJECT}-postgres, ${DB_NAME}), but the host port bindings are not.

Fix

The worktree recipe now ensures Traefik is set up before calling just up. This guarantees just up takes the Traefik path (-f docker-compose.yaml -f compose/docker-compose.traefik.yml), which skips the override file entirely. Postgres and redis don't need host port bindings — containers communicate over the internal Docker network. Server and client traffic routes through Traefik using hostname-based routing (${PROJECT}.localhost).

# Added to `just worktree add` before calling `just up`:
if ! docker network inspect proxy >/dev/null 2>&1; then
  echo "Setting up Traefik (required for concurrent worktree stacks)..."
  just setup-traefik
fi

Each worktree is then accessible at its own hostname:

  • http://tn-surveys-my-feature.localhost (client)
  • http://api.tn-surveys-my-feature.localhost (API)

Summary: complete worktree add flow

# 1. Strip branch prefix for directory/project naming
dir_name=$(echo "$BRANCH" | sed -E 's,^(feature|bugfix|hotfix|fix|chore|release)/,,' | ...)

# 2. Ensure Traefik is running (prevents port conflicts between worktrees)
if ! docker network inspect proxy >/dev/null 2>&1; then
  just setup-traefik
fi

# 3. Create worktree, copy/patch .env, bring up stack
just up

# 4. Wait for server to be FULLY ready (migrate done + runserver listening)
#    Uses docker exec -w /tmp to avoid the bind mount namespace issue
for i in $(seq 1 120); do
  if docker exec -w /tmp "${PROJECT}-server" python -c \
    "import socket; s=socket.create_connection(('localhost',8000),1); s.close()" 2>/dev/null; then
    break
  fi
  sleep 1
done

# 5. Seed data via docker compose run (not exec) to bypass the mount issue
docker compose run --rm -T server python server/manage.py create_test_data

@whusterj whusterj had a problem deploying to tn-spa-bootstrapper-pr-501 June 4, 2026 12:33 Failure
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants