Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions .config/wt.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Worktrunk project configuration (https://worktrunk.dev).
#
# Gives every worktree its own Docker Compose environment so several branches
# can run `task run` / `task tests` side by side. docker-compose.yml and the
# Taskfile read the `.env` written below and fall back to the historical
# defaults (port 8000, image python.ie/website-dev) when it is absent, so
# nothing changes for contributors who do not use worktrunk.
#
# The naming templates in pre-start and post-remove must stay in sync.

# Blocking, so the `.env` exists before anyone runs docker compose in the new
# worktree. It pins, per branch:
# - COMPOSE_PROJECT_NAME: namespaces containers, networks and volumes
# (including the Postgres data volume), so each worktree has its own database;
# - DOCKER_IMAGE: a per-branch tag, so rebuilding the image on a branch that
# changes dependencies does not break the other worktrees;
# - WEB_PORT / PGPORT: deterministic host ports (hash_port), so published ports
# never collide. PGPORT is also what psql / `task database:pull` use on the host.
[pre-start]
env = """
cat > .env <<EOF
# Generated by the worktrunk pre-start hook (.config/wt.toml) for {{ branch }}.
# Regenerate with: wt hook pre-start
COMPOSE_PROJECT_NAME=pythonie-{{ branch | sanitize_db }}
DOCKER_IMAGE=python.ie/website-dev:{{ branch | sanitize_db }}
WEB_PORT={{ branch | hash_port }}
PGPORT={{ ('pg-' ~ branch) | hash_port }}
EOF
"""

# Background: prepares the host virtualenv (.venv) of the new worktree so that
# `uv run ruff ...`, local tests and editors work right away.
[post-start]
deps = "uv sync --all-groups"

# Background, after the worktree is gone (runs from the primary worktree).
# DESTRUCTIVE: removes the containers, network and volumes (Postgres and Redis
# data) of this worktree's compose project, then its per-branch image. The
# label filter and `-p` restrict it to that single project; other worktrees
# and the primary checkout use different project names and are never touched.
# The first step is needed because `down` skips running one-off containers
# started by `docker compose run` (e.g. a `task run` left open), which would
# keep the network and the image in use.
[post-remove]
docker = """
ids=$(docker ps -aq --filter label=com.docker.compose.project=pythonie-{{ branch | sanitize_db }})
[ -z "$ids" ] || docker rm -f $ids
docker compose -p pythonie-{{ branch | sanitize_db }} down --volumes --remove-orphans
docker image rm python.ie/website-dev:{{ branch | sanitize_db }} 2>/dev/null || true
"""

# Shows each worktree's dev server URL in `wt list` (matches WEB_PORT above).
[list]
url = "http://localhost:{{ branch | hash_port }}"
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ project.db
.envrc
development.env
production.env
# Per-worktree Docker Compose overrides (generated, see .config/wt.toml)
.env

# Database dumps and local databases
*.dump
Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ uv run python pythonie/manage.py runserver --settings=pythonie.settings.dev

### Docker Development (preferred)

Uses Task for most operations. Requires docker-compose with services: web, postgres, redis, minio.
Uses Task for most operations. Requires docker-compose with services: web, postgres, redis. With worktrunk (`wt`), `.config/wt.toml` writes a per-worktree `.env` (compose project name, image tag, ports) and cleans up the worktree's containers and volumes on `wt remove`; see DEVELOPMENT.md.

```bash
# Build docker image
Expand Down
36 changes: 36 additions & 0 deletions DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,42 @@ uv run python pythonie/manage.py createsuperuser --settings=pythonie.settings.de
uv run python pythonie/manage.py runserver --settings=pythonie.settings.dev
```

### Parallel Worktrees with worktrunk (optional)

[worktrunk](https://worktrunk.dev) (`wt`) manages one git worktree per branch. The project hooks in `.config/wt.toml` give every worktree its own Docker Compose environment, so several branches can run `task run` or `task tests` at the same time without sharing a database, an image or a port.

```bash
# Create a worktree for a new branch (runs the hooks below)
wt switch --create my-feature

# Inside it, the usual commands work unchanged
task docker:build
task django:migrate
task run

# Show every worktree with its dev server URL
wt list

# Remove the worktree AND its Docker resources (see below)
wt remove my-feature
```

The first time, worktrunk asks you to approve the project hooks. Review them and approve with `wt config approvals add`.

What the hooks do:

| Hook | What it does |
|------|--------------|
| `pre-start` | Writes a `.env` in the new worktree with `COMPOSE_PROJECT_NAME`, `DOCKER_IMAGE`, `WEB_PORT` and `PGPORT`, all derived from the branch name |
| `post-start` | Runs `uv sync --all-groups` in the background to create the worktree's `.venv` |
| `post-remove` | Runs `docker compose -p <project> down --volumes --remove-orphans` and deletes the branch image |

Docker Compose reads `.env` automatically, so each worktree gets its own containers, network and volumes (including the PostgreSQL data), its own image tag (`python.ie/website-dev:<branch>`) and deterministic host ports. Check them with `cat .env`; the web server is at `http://localhost:$WEB_PORT/`. The Taskfile also loads `.env`, so host tools such as `psql` or `task database:pull` talk to the worktree's PostgreSQL on `PGPORT`.

**Removing a worktree deletes its database.** `wt remove` (and `wt merge`, which removes the worktree) destroys the Docker volumes of that worktree's compose project. Other worktrees and the main checkout use different project names and are not affected. Dump anything you want to keep first.

Without worktrunk, or in a worktree created before these hooks existed, there is no `.env` and the defaults apply: port 8000 for the web server, 5432 for PostgreSQL, image `python.ie/website-dev`, and a compose project named after the directory. To add the per-worktree `.env` to an existing worktree, run `wt hook pre-start` inside it.

### Environment Variables

Create `development.env` at the root (copy from `development.env.example` if it exists):
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Website for Python Ireland (python.ie / pycon.ie) community, built with Django 6

2. Start supporting services:
```bash
docker compose up -d postgres redis minio
docker compose up -d postgres redis
```

3. Run database migrations:
Expand Down Expand Up @@ -48,6 +48,8 @@ Website for Python Ireland (python.ie / pycon.ie) community, built with Django 6
7. Visit http://127.0.0.1:8000/ to see the site with sample content
8. Access Wagtail admin at http://127.0.0.1:8000/admin/

Working on several branches at once? See [Parallel Worktrees with worktrunk](DEVELOPMENT.md#parallel-worktrees-with-worktrunk-optional) to give each worktree its own database, image and ports.

## Local Setup (Without Docker)

If you prefer to develop without Docker:
Expand Down
12 changes: 7 additions & 5 deletions Taskfile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@
version: '3'

env:
DOCKER_IMAGE: python.ie/website-dev
PGDATABASE: pythonie
PGPASSWORD: pythonie
PGUSER: postgres
PGHOST: 127.0.0.1
HEROKU_APP: pythonie

dotenv: ['production.env']
# `.env` holds per-worktree overrides (COMPOSE_PROJECT_NAME, DOCKER_IMAGE,
# WEB_PORT, PGPORT) written by worktrunk, see .config/wt.toml. Missing files
# are ignored, so everything falls back to the defaults in docker-compose.yml.
dotenv: ['.env', 'production.env']

tasks:
database:drop:
Expand Down Expand Up @@ -193,12 +195,12 @@ tasks:
docker:build:
desc: Build the docker image
cmds:
- docker build --no-cache -t $DOCKER_IMAGE .
- docker compose build --no-cache web

docker:build:cached:
desc: Build the docker image reusing the build cache (faster rebuilds)
cmds:
- docker build -t $DOCKER_IMAGE .
- docker compose build web

docker:run:
desc: Run a shell into the docker container
Expand All @@ -208,7 +210,7 @@ tasks:
stack:pull:
desc: Pull the docker images for the stack
cmds:
- docker compose pull postgres minio mc redis
- docker compose pull postgres redis

stack:services:
desc: List the services from the docker compose file
Expand Down
41 changes: 8 additions & 33 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
services:
web:
image: python.ie/website-dev
build: .
# Image, host ports and the compose project name can be overridden from a
# `.env` file next to this one (worktrunk writes it for every worktree, see
# .config/wt.toml). The defaults below apply when there is no `.env`.
image: ${DOCKER_IMAGE:-python.ie/website-dev}
ports:
- "8000:8000"
- "${WEB_PORT:-8000}:8000"
command: /usr/bin/fish
depends_on:
- postgres
- minio
- redis
env_file:
- development.env
Expand All @@ -23,46 +26,18 @@ services:
postgres:
image: postgres:17
ports:
- "5432:5432"
- "${PGPORT:-5432}:5432"
environment:
POSTGRES_PASSWORD: pythonie

redis:
image: redis:6.2

minio:
image: quay.io/minio/minio
ports:
- "9000:9000"
- "9001:9001"
command: server --console-address ":9001" /data
environment:
MINIO_ROOT_USER: pythonie
MINIO_ROOT_PASSWORD: pythonie
volumes:
- minio-data:/data
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
interval: 30s
timeout: 20s
retries: 3

mc:
image: quay.io/minio/mc
links:
- minio
volumes:
- ./mc:/root/.mc
- ../s3:/s3

test:
image: python.ie/website-dev
image: ${DOCKER_IMAGE:-python.ie/website-dev}
environment:
DJANGO_SETTINGS_MODULE: pythonie.settings.tests
volumes:
- .:/app
working_dir: /app
command: python pythonie/manage.py test pythonie --verbosity=3

volumes:
minio-data:
Loading