From 63546c1fe32e9ce24c2db05f11d25faf0ef4012d Mon Sep 17 00:00:00 2001 From: ollyhearn Date: Sat, 6 Jun 2026 12:30:49 +0300 Subject: [PATCH] feat: docker & startup --- app/workers/arq_worker.py | 6 +++- dockerfiles/Dockerfile.dev | 34 +++++++++++++++++++++++ Dockerfile => dockerfiles/Dockerfile.prod | 8 ++++-- pyproject.toml | 1 - 4 files changed, 44 insertions(+), 5 deletions(-) create mode 100644 dockerfiles/Dockerfile.dev rename Dockerfile => dockerfiles/Dockerfile.prod (78%) diff --git a/app/workers/arq_worker.py b/app/workers/arq_worker.py index db728f6..ae9937d 100644 --- a/app/workers/arq_worker.py +++ b/app/workers/arq_worker.py @@ -24,8 +24,12 @@ async def shutdown(_ctx: dict[str, Any]) -> None: log.info("worker_shutdown") +async def _noop(_ctx: dict[str, Any]) -> None: + pass + + class WorkerSettings: - functions: ClassVar[list[Any]] = [] # populated as tasks are implemented + functions: ClassVar[list[Any]] = [_noop] # populated as tasks are implemented on_startup = startup on_shutdown = shutdown max_jobs = get_settings().max_parallel_downloads diff --git a/dockerfiles/Dockerfile.dev b/dockerfiles/Dockerfile.dev new file mode 100644 index 0000000..31bc70c --- /dev/null +++ b/dockerfiles/Dockerfile.dev @@ -0,0 +1,34 @@ +# syntax=docker/dockerfile:1 +# DEV image: source is bind-mounted by compose, uvicorn runs with --reload. +# Build context = mcma-backend/ (see docker-compose.yml). +FROM python:3.14-slim + +ENV PYTHONUNBUFFERED=1 \ + PYTHONDONTWRITEBYTECODE=1 \ + UV_LINK_MODE=copy \ + VIRTUAL_ENV=/app/.venv \ + PATH="/app/.venv/bin:$PATH" + +# Runtime tools: ffmpeg (transcode/HLS), fpcalc (Chromaprint fingerprinting). +RUN apt-get update \ + && apt-get install -y --no-install-recommends ffmpeg libchromaprint-tools \ + && rm -rf /var/lib/apt/lists/* + +COPY --from=ghcr.io/astral-sh/uv:0.4 /uv /uvx /bin/ + +WORKDIR /app + +# Dependency layer (incl. dev group for tooling) — cached unless lockfile changes. +COPY pyproject.toml uv.lock* ./ +RUN --mount=type=cache,target=/root/.cache/uv \ + uv sync --frozen --no-install-project + +# Project layer. At runtime compose bind-mounts the live source over this. +COPY . . +RUN --mount=type=cache,target=/root/.cache/uv \ + uv sync --frozen + +EXPOSE 8000 + +# --reload watches /app (the bind mount) and restarts on edit. +CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"] diff --git a/Dockerfile b/dockerfiles/Dockerfile.prod similarity index 78% rename from Dockerfile rename to dockerfiles/Dockerfile.prod index 0bfb16c..c8f6ac1 100644 --- a/Dockerfile +++ b/dockerfiles/Dockerfile.prod @@ -1,5 +1,7 @@ # syntax=docker/dockerfile:1 -# Single image serves both `api` and `worker` (different commands in compose). +# PROD image: self-contained, no dev deps, no source mount. +# Single image serves both `api` and `worker` (different commands at runtime). +# Build context = mcma-backend/. FROM python:3.14-slim AS base ENV PYTHONUNBUFFERED=1 \ @@ -18,7 +20,7 @@ COPY --from=ghcr.io/astral-sh/uv:0.4 /uv /uvx /bin/ WORKDIR /app -# Dependency layer — cached unless lockfile changes. +# Dependency layer (prod only) — cached unless lockfile changes. COPY pyproject.toml uv.lock* ./ RUN --mount=type=cache,target=/root/.cache/uv \ uv sync --frozen --no-install-project --no-dev @@ -30,5 +32,5 @@ RUN --mount=type=cache,target=/root/.cache/uv \ EXPOSE 8000 -# Default: API server. Worker overrides command in docker-compose. +# Default: API server. Worker overrides command (arq ...) at runtime. CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"] diff --git a/pyproject.toml b/pyproject.toml index 13b48c2..ba358d5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,7 +2,6 @@ name = "mcma-backend" version = "0.1.0" description = "Self-hosted, offline-first music service — backend (hexagonal architecture)" -readme = "README.md" requires-python = ">=3.14" dependencies = [ # web