diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..dea93c0 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,12 @@ +.git +.gitignore +.venv +__pycache__ +*.pyc +.pytest_cache +.ruff_cache +.claude +data/*.db +.env +tests +*.md diff --git a/CLAUDE.md b/CLAUDE.md index d842ddf..e087efa 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -51,6 +51,12 @@ ruff check . # linter Les tests utilisent une base SQLite **en mémoire** (voir `tests/conftest.py`), jamais le fichier `data/stock.db` du développement. +`docker compose up --build` construit et lance l'app (Dockerfile, Python +3.14-slim, `data/` monté en volume depuis l'hôte, 1 seul worker Uvicorn +volontairement — SQLite gère mal les écritures concurrentes multi-process). +Pas testé avec un vrai build dans cette session (Docker indisponible dans +l'environnement de dev) — à vérifier avant un premier déploiement. + ## Convention : commentaires détaillés Contrairement à la préférence par défaut (commentaires minimaux), ce projet diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..676dbd3 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,45 @@ +# Image officielle Python, même version majeure qu'en dev local (3.14). +# "slim" = Debian minimal : largement suffisant ici, aucune dépendance du +# projet ne nécessite de compilation (pas besoin d'une image complète). +FROM python:3.14-slim + +# Pas de bytecode .pyc écrit sur disque (inutile dans un conteneur +# éphémère) et sortie non bufferisée (les logs uvicorn apparaissent tout +# de suite dans `docker logs`, pas seulement à la fermeture du process). +ENV PYTHONDONTWRITEBYTECODE=1 \ + PYTHONUNBUFFERED=1 + +WORKDIR /app + +# Copié avant le reste du code : cette couche ne se reconstruit que si +# requirements.txt change, ce qui accélère largement les rebuilds pendant +# le développement (le code applicatif change bien plus souvent). +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +COPY app/ app/ +COPY scripts/ scripts/ + +# data/ (base SQLite) est destiné à être monté en volume (voir +# docker-compose.yml) pour que les données survivent aux +# rebuilds/redémarrages du conteneur. +RUN mkdir -p data + +# Utilisateur non-root par sécurité. Si le volume data/ monté depuis +# l'hôte pose un problème de permissions (l'utilisateur de l'hôte n'a pas +# forcément l'UID 1000), ajustez les droits du dossier hôte avec +# `chown -R 1000:1000 data/`, ou retirez ces deux lignes pour tourner en root. +RUN useradd --create-home --uid 1000 appuser && chown -R appuser:appuser /app +USER appuser + +EXPOSE 8000 + +# Vérifie que le serveur répond, sans dépendre d'un outil externe +# (curl/wget, absents de l'image "slim"). +HEALTHCHECK --interval=30s --timeout=5s --start-period=10s \ + CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/materiels')" || exit 1 + +# Un seul worker Uvicorn : SQLite gère mal les écritures concurrentes +# depuis plusieurs process. Largement suffisant pour l'usage interne visé. +# Pas de --reload : réservé au dev local hors conteneur (voir README). +CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"] diff --git a/README.md b/README.md index 92954cf..748b23d 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,26 @@ uvicorn app.main:app --reload L'app est accessible sur http://127.0.0.1:8000. La base SQLite est créée automatiquement au premier démarrage dans `data/stock.db`. +## Lancer avec Docker + +```bash +cp .env.example .env # requis, voir Configuration des emails ci-dessous +docker compose up --build +``` + +L'app est accessible sur http://localhost:8000. `data/` est monté depuis +l'hôte : la base SQLite (`data/stock.db`) reste au même endroit qu'en dev +local sans Docker, visible et sauvegardable directement dans ce dossier. + +Un seul worker Uvicorn dans le conteneur (voir commentaire dans le +`Dockerfile`) : SQLite gère mal les écritures concurrentes depuis +plusieurs process, et un seul worker est largement suffisant pour l'usage +interne visé. + +Pour du développement actif, la boucle venv + `--reload` ci-dessus reste +plus rapide (pas de rebuild d'image à chaque changement) ; Docker sert +surtout à tester ou reproduire l'environnement de déploiement. + ## Lancer les tests ```bash @@ -107,4 +127,6 @@ scripts/ envoyer_resume_stock.py déclenche manuellement l'email récapitulatif tests/ tests pytest (client de test FastAPI + base SQLite en mémoire) data/ base SQLite locale (ignorée par git) +Dockerfile image de l'app (Python 3.14-slim, utilisateur non-root, 1 worker Uvicorn) +docker-compose.yml lancement local avec data/ monté en volume ``` diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..04d16e1 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,20 @@ +# Lancement : docker compose up --build +# L'app est accessible sur http://localhost:8000 +# +# Prérequis : cp .env.example .env (et le remplir si un vrai relais SMTP +# est disponible ; sinon les emails restent simulés dans les logs, comme +# en dev local — voir `docker compose logs -f`). +# +# data/ est monté depuis l'hôte : la base SQLite (data/stock.db) reste +# visible et sauvegardable directement dans ce dossier, exactement comme +# en dev local sans Docker. +services: + stock: + build: . + ports: + - "8000:8000" + volumes: + - ./data:/app/data + env_file: + - .env + restart: unless-stopped