build: Docker-Images, Compose-Setup und Gitea-Workflows

- Mehrstufige Dockerfiles je Dienst mit Stufen für Entwicklung und Betrieb,
  beide Images laufen als unprivilegierter Benutzer
- Backend-Entrypoint wartet auf die Datenbank, migriert und seedt nur bei
  leerem Kategoriebaum (neues Flag --if-empty)
- docker-compose.yml mit Netz-Trennung, Healthchecks und benannten Volumes;
  die Datenbank ist ausschließlich im internen Netz erreichbar
- nginx liefert das Bundle aus und reicht /api weiter; index.html ungecacht,
  gehashte Assets ein Jahr
- Gitea-Workflows: ci.yml für Lint, Tests und Bundle-Bau, build.yml für die
  Images nach linux/amd64 ohne Deploy-Schritt
- docs/runner.md und docs/deployment.md, .dockerignore je Dienst

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014e7t8UpmoVNMtWivY5LiSH
This commit is contained in:
moneyfy
2026-09-09 17:14:44 +02:00
co-authored by Claude Opus 5
parent 54c59c9f71
commit 87bee9b72a
17 changed files with 1184 additions and 10 deletions
+79
View File
@@ -0,0 +1,79 @@
# syntax=docker/dockerfile:1.7
#
# Backend-Image für moneyfy. Mehrstufig, damit im Endbild weder Build-Werkzeuge
# noch Entwicklungsabhängigkeiten landen.
# --- Abhängigkeiten -----------------------------------------------------------
FROM python:3.12-slim-bookworm AS deps
# uv installiert die Abhängigkeiten deutlich schneller als pip.
COPY --from=ghcr.io/astral-sh/uv:0.5.11 /uv /bin/uv
ENV PIP_DISABLE_PIP_VERSION_CHECK=1 \
PYTHONDONTWRITEBYTECODE=1 \
UV_LINK_MODE=copy
WORKDIR /app
# Nur die Abhängigkeitsdeklaration kopieren die Schicht bleibt gültig,
# solange sich pyproject.toml nicht ändert.
COPY pyproject.toml ./
RUN /bin/uv pip install --system --no-cache -r pyproject.toml
# --- Laufzeitbasis ------------------------------------------------------------
FROM python:3.12-slim-bookworm AS runtime
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PYTHONPATH=/app \
TZ=Europe/Berlin \
LOGO_STORAGE_DIR=/data/logos
# tzdata wird für Europe/Berlin gebraucht, curl für den Healthcheck.
RUN apt-get update \
&& apt-get install -y --no-install-recommends tzdata curl \
&& rm -rf /var/lib/apt/lists/* \
&& groupadd --system --gid 10001 moneyfy \
&& useradd --system --uid 10001 --gid moneyfy --create-home --home-dir /home/moneyfy moneyfy
COPY --from=deps /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages
COPY --from=deps /usr/local/bin /usr/local/bin
WORKDIR /app
# --- Entwicklung --------------------------------------------------------------
# Wird von docker-compose.override.yml genutzt; der Quellcode kommt per Volume.
FROM runtime AS development
COPY --from=ghcr.io/astral-sh/uv:0.5.11 /uv /usr/local/bin/uv
COPY pyproject.toml ./
RUN uv pip install --system --no-cache -r pyproject.toml --extra dev
COPY --chown=moneyfy:moneyfy . .
RUN mkdir -p /data/logos && chown -R moneyfy:moneyfy /data
USER moneyfy
EXPOSE 8000
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]
# --- Produktion ---------------------------------------------------------------
FROM runtime AS production
COPY --chown=moneyfy:moneyfy alembic.ini pyproject.toml ./
COPY --chown=moneyfy:moneyfy alembic ./alembic
COPY --chown=moneyfy:moneyfy app ./app
COPY --chown=moneyfy:moneyfy docker/entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh \
&& mkdir -p /data/logos \
&& chown -R moneyfy:moneyfy /data
USER moneyfy
EXPOSE 8000
VOLUME ["/data"]
HEALTHCHECK --interval=30s --timeout=5s --start-period=40s --retries=3 \
CMD curl -fsS http://127.0.0.1:8000/api/health || exit 1
ENTRYPOINT ["entrypoint.sh"]
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--proxy-headers", "--forwarded-allow-ips", "*"]