diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..bd327742 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Shell scripts run inside Linux containers and Git Bash, which fail on CRLF +*.sh text eol=lf diff --git a/README.md b/README.md index a11cb405..9154a734 100644 --- a/README.md +++ b/README.md @@ -117,6 +117,48 @@ You can now access localhost:OUTBOUND_PORT > - If you're planning on running this behind Tailscale or other similar service also add your machine given IP to `DJANGO_ALLOWED_HOSTS` > - If you're going to use another IP that isn't localhost, add it to `DJANGO_ALLOWED_HOSTS`, without `http://` +## Development + +The development stack runs from `docker-compose.dev.yml`: + +```bash +$ docker compose -f docker-compose.dev.yml up -d +``` + +Set `COMPOSE_FILE=docker-compose.dev.yml` in your `.env` to drop the `-f` flag. + +### Working in multiple git worktrees + +You can run one dev stack per [git worktree](https://git-scm.com/docs/git-worktree), side by side. Each worktree gets its own containers, database and volumes, because Compose names them after the worktree's folder. + +A new worktree has no `.env` (it's gitignored). After creating one, run this from the worktree's root: + +```bash +$ sh docker/dev/worktree.sh +``` + +It copies `.env` from your main checkout, gives the worktree its own host ports (printed when it finishes) and installs `frontend/node_modules` for tools you run outside Docker. The database starts empty. + +To stop the worktree's containers and delete its volumes before removing it: + +```bash +$ sh docker/dev/worktree.sh teardown +``` + +The commands work on Linux, macOS and Windows (Git Bash or WSL). PowerShell and cmd have no `sh`, so on Windows set up git aliases once in your clone; they run through Git's bundled shell from any terminal, and every worktree of the clone shares them: + +```bash +git config alias.wt-setup '!sh docker/dev/worktree.sh' +git config alias.wt-teardown '!sh docker/dev/worktree.sh teardown' +``` + +Run those two lines in Git Bash or PowerShell; cmd doesn't understand the single quotes. + +Then use `git wt-setup` and `git wt-teardown`. Tools that create worktrees for you, such as Orca, Conductor or Superset, usually have setup and archive hooks: put these commands there. + +> [!NOTE] +> Ports come from the worktree's folder name, so two worktrees can rarely land on the same ones. If `docker compose up` reports a port in use, change `OUTBOUND_PORT`, `WEBPACK_OUTBOUND_PORT` or `SQL_OUTBOUND_PORT` in that worktree's `.env`. + ## Latest changes Features are only added to `main` when ready, if you want to run the latest version, you must build from source or use the `:nightly` tag on docker. Keep in mind that there can be undocumented breaking changes. diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 53aace60..43396dce 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -11,7 +11,6 @@ services: context: . dockerfile: ./docker/dev/django/Dockerfile image: wygiwyh_dev_server - container_name: wygiwyh_dev_server volumes: - ./app/:/usr/src/app/:z - ./frontend/:/usr/src/frontend:z @@ -21,6 +20,9 @@ services: - "${OUTBOUND_PORT:-8000}:${INTERNAL_PORT:-8000}" env_file: - .env + environment: + # The browser loads assets straight from vite, so Django must point at the published port + - DJANGO_VITE_DEV_SERVER_PORT=${WEBPACK_OUTBOUND_PORT:-5173} depends_on: - db - vite @@ -31,21 +33,20 @@ services: context: . dockerfile: ./docker/dev/vite/Dockerfile image: wygiwyh_dev_node - container_name: wygiwyh_dev_node volumes: - ./frontend/:/usr/src/frontend - ./app/:/usr/src/app/ # http://jdlm.info/articles/2016/03/06/lessons-building-node-app-docker.html - wygiwyh_node_modules:/usr/src/frontend/node_modules ports: - - '${WEBPACK_OUTBOUND_PORT}:5173' + - '${WEBPACK_OUTBOUND_PORT:-5173}:${WEBPACK_OUTBOUND_PORT:-5173}' environment: - WATCHPACK_POLLING=true + - VITE_DEV_SERVER_PORT=${WEBPACK_OUTBOUND_PORT:-5173} restart: unless-stopped db: image: postgres:15 - container_name: ${SQL_HOST} volumes: - wygiwyh_dev_postgres_data:/var/lib/postgresql/data/ environment: @@ -53,5 +54,10 @@ services: - POSTGRES_PASSWORD=${SQL_PASSWORD} - POSTGRES_DB=${SQL_DATABASE} ports: - - '${SQL_PORT}:5432' + - '${SQL_OUTBOUND_PORT:-${SQL_PORT:-5432}}:5432' + networks: + default: + # Keeps .env files that point SQL_HOST at the old fixed container name working + aliases: + - ${SQL_HOST:-db} restart: unless-stopped diff --git a/docker/dev/worktree.sh b/docker/dev/worktree.sh new file mode 100755 index 00000000..e87d8645 --- /dev/null +++ b/docker/dev/worktree.sh @@ -0,0 +1,43 @@ +#!/bin/sh +# Prepares or tears down the dev environment of an extra git worktree. +# sh docker/dev/worktree.sh copy .env from the main checkout with free host ports, install host node_modules +# sh docker/dev/worktree.sh teardown stop this worktree's containers and delete its volumes +# Plain POSIX sh: no GNU-only flags, so it runs on Linux, macOS and Git for Windows. +set -eu + +if [ "${1:-setup}" = "teardown" ]; then + # Without .env compose can't find its config, and nothing was ever started here + [ -f .env ] || exit 0 + exec docker compose down -v --remove-orphans +fi + +main=$(git worktree list --porcelain | head -n 1 | cut -d ' ' -f 2-) +if [ "$main" = "$(git rev-parse --show-toplevel)" ]; then + echo "This is the main checkout, nothing to do." >&2 + exit 0 +fi + +if [ ! -f .env ]; then + if [ ! -f "$main/.env" ]; then + echo "No .env in the main checkout ($main). Create it from .env.example first." >&2 + exit 1 + fi + # Same folder name -> same offset, so re-creating a worktree keeps its ports + off=$(( $(basename "$PWD" | cksum | cut -d ' ' -f 1) % 100 + 1 )) + grep -v -E '^(OUTBOUND_PORT|WEBPACK_OUTBOUND_PORT|SQL_OUTBOUND_PORT)=' "$main/.env" > .env || true + { + echo "" + echo "# Host ports for this worktree, set by docker/dev/worktree.sh" + echo "OUTBOUND_PORT=$((18000 + off))" + echo "WEBPACK_OUTBOUND_PORT=$((15100 + off))" + echo "SQL_OUTBOUND_PORT=$((15400 + off))" + } >> .env + echo "Created .env: app on http://localhost:$((18000 + off))" +fi + +# For tools run on the host (the vite container keeps its own copy in a volume) +if command -v npm > /dev/null 2>&1; then + npm ci --prefix frontend --no-audit --no-fund +else + echo "npm not found, skipping frontend/node_modules." >&2 +fi