diff --git a/.gitignore b/.gitignore index 474bdc25..965fadad 100644 --- a/.gitignore +++ b/.gitignore @@ -12,5 +12,6 @@ backend/.env # JavaScript stuff node_modules +frontend/dist .editorconfig diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d9311fd8..3b19d612 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -184,7 +184,41 @@ And the frontend should be available at [localhost:3000](localhost:3000). ### Docker Instructions -Edit the `docker-compose.yml` file and replace the [`image: bbilly1/tubearchivist` line](https://github.com/tubearchivist/tubearchivist/blob/4af12aee15620e330adf3624c984c3acf6d0ac8b/docker-compose.yml#L7) with `build: .`. Also make any other changes to the environment variables and so on necessary to run the application, just like you're launching the application as normal. +There are two docker-based development workflows: + +#### Dev stack (hot reload, recommended) + +Use the local dev stack in `dev/docker/docker-compose.dev.yml`. This runs: +- Elasticsearch + Redis +- Django dev server (`runserver`) with source mounted from your host +- Celery worker +- Frontend dev server (Vite) + +From the repo root: + +```bash +docker compose -f dev/docker/docker-compose.dev.yml up -d --build +``` + +Then: +- Frontend: [localhost:3000](localhost:3000) +- Backend health: [localhost:8000/api/health/](localhost:8000/api/health/) + +Stop everything with: + +```bash +docker compose -f dev/docker/docker-compose.dev.yml down +``` + +This compose file uses persistent docker volumes for Elasticsearch, Redis and frontend `node_modules`. If you want a clean slate, remove the volumes: + +```bash +docker compose -f dev/docker/docker-compose.dev.yml down -v +``` + +#### Production-like rebuild (full image) + +If you want to test closer to the production container, edit the `docker-compose.yml` file and replace the `image: bbilly1/tubearchivist` line with `build: .`. Also make any other changes to the environment variables and so on necessary to run the application, just like you're launching the application as normal. Run `docker compose up --build`. This will bring up the application. Kill it with `ctrl-c` or by running `docker compose down` from a new terminal window in the same directory. diff --git a/backend/.env.example b/backend/.env.example new file mode 100644 index 00000000..6271d4dc --- /dev/null +++ b/backend/.env.example @@ -0,0 +1,21 @@ +# Local development example (place a copy as backend/.env) +# +# Note: +# - This repo's .gitignore ignores backend/.env, backend/static, backend/staticfiles. +# - Frontend dev server (Vite) expects backend on http://localhost:8000 in DEV mode. + +TA_HOST="http://localhost:8000" +TA_USERNAME=tubearchivist +TA_PASSWORD=verysecret + +TA_MEDIA_DIR="static/volume/media" +TA_CACHE_DIR="static" +TA_APP_DIR="." + +REDIS_CON=redis://localhost:6379 +ES_URL="http://localhost:9200" +ELASTIC_PASSWORD=verysecret + +TZ=America/New_York +DJANGO_DEBUG=True + diff --git a/dev/docker/Dockerfile.dev b/dev/docker/Dockerfile.dev new file mode 100644 index 00000000..17bbbe97 --- /dev/null +++ b/dev/docker/Dockerfile.dev @@ -0,0 +1,21 @@ +FROM denoland/deno:bin AS deno-bin + +FROM python:3.11.13-slim-bookworm + +ENV PYTHONUNBUFFERED=1 \ + PYTHONDONTWRITEBYTECODE=1 + +COPY --from=deno-bin /deno /usr/local/bin/deno + +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential gcc libldap2-dev libsasl2-dev libssl-dev \ + ffmpeg atomicparsley curl \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /workspace + +COPY backend/requirements.txt backend/requirements.txt +COPY requirements-dev.txt requirements-dev.txt + +RUN pip install --no-cache-dir -r requirements-dev.txt + diff --git a/dev/docker/docker-compose.dev.yml b/dev/docker/docker-compose.dev.yml new file mode 100644 index 00000000..969d99c8 --- /dev/null +++ b/dev/docker/docker-compose.dev.yml @@ -0,0 +1,121 @@ +name: tubearchivist-dev + +services: + frontend-dev: + image: node:lts-alpine + container_name: tubearchivist-frontend-dev + restart: unless-stopped + working_dir: /workspace/frontend + volumes: + - ../../:/workspace + - frontend-node-modules:/workspace/frontend/node_modules + environment: + - CHOKIDAR_USEPOLLING=true + ports: + - "3000:3000" + depends_on: + - backend-dev + command: > + sh -lc + "if [ ! -x node_modules/.bin/vite ]; then npm ci; fi; npm run dev -- --host 0.0.0.0 --port 3000" + + backend-dev: + build: + context: ../../ + dockerfile: dev/docker/Dockerfile.dev + container_name: tubearchivist-backend-dev + restart: unless-stopped + working_dir: /workspace/backend + volumes: + - ../../:/workspace + environment: + - TA_HOST=http://localhost:8000 http://localhost:3000 + - TA_USERNAME=tubearchivist + - TA_PASSWORD=verysecret + - TA_MEDIA_DIR=static/volume/media + - TA_CACHE_DIR=static + - TA_APP_DIR=. + - REDIS_CON=redis://archivist-redis:6379 + - ES_URL=http://archivist-es:9200 + - ELASTIC_PASSWORD=verysecret + - TZ=America/New_York + - DJANGO_DEBUG=True + ports: + - "8000:8000" + depends_on: + - archivist-es + - archivist-redis + command: > + bash -lc + "mkdir -p static static/img static/volume/media && + cp -an /workspace/frontend/public/img/. static/img/ || true && + python manage.py ta_stop_on_error && + python manage.py migrate && + python manage.py ta_envcheck && + python manage.py ta_connection && + python manage.py ta_startup && + python manage.py runserver 0.0.0.0:8000" + + celery-dev: + build: + context: ../../ + dockerfile: dev/docker/Dockerfile.dev + container_name: tubearchivist-celery-dev + restart: unless-stopped + working_dir: /workspace/backend + volumes: + - ../../:/workspace + environment: + - TA_HOST=http://localhost:8000 http://localhost:3000 + - TA_USERNAME=tubearchivist + - TA_PASSWORD=verysecret + - TA_MEDIA_DIR=static/volume/media + - TA_CACHE_DIR=static + - TA_APP_DIR=. + - REDIS_CON=redis://archivist-redis:6379 + - ES_URL=http://archivist-es:9200 + - ELASTIC_PASSWORD=verysecret + - TZ=America/New_York + - DJANGO_DEBUG=True + depends_on: + - backend-dev + command: > + bash -lc + "mkdir -p static static/img static/volume/media && + cp -an /workspace/frontend/public/img/. static/img/ || true && + celery -A task.celery worker --loglevel=DEBUG --concurrency 2 --max-tasks-per-child 5 --max-memory-per-child 150000" + + archivist-redis: + image: redis + container_name: archivist-redis-dev + restart: unless-stopped + ports: + - "6379:6379" + volumes: + - redis-dev:/data + + archivist-es: + # This is the same ES image as the default docker-compose.yml uses. + # If your Docker host errors on ulimits/rlimits, omit ulimits (see README.md). + image: bbilly1/tubearchivist-es + container_name: archivist-es-dev + restart: unless-stopped + environment: + - "ELASTIC_PASSWORD=verysecret" + - "ES_JAVA_OPTS=-Xms1g -Xmx1g" + - "xpack.security.enabled=true" + - "discovery.type=single-node" + - "path.repo=/usr/share/elasticsearch/data/snapshot" + ulimits: + memlock: + soft: -1 + hard: -1 + ports: + - "9200:9200" + volumes: + - es-dev:/usr/share/elasticsearch/data + +volumes: + frontend-node-modules: {} + redis-dev: {} + es-dev: {}