Spaces:
Sleeping
Sleeping
| # Image **moteur** Cinoc pour un Hugging Face Space (SDK: docker). | |
| # | |
| # ≠ la vitrine lecture seule (deploy/Dockerfile{,.space}) : celle-ci **bake un vrai | |
| # moteur OCR gratuit** (Tesseract 5 + langues fra/lat/eng) pour qu'un visiteur du | |
| # Space public lance un run Tesseract **sans clé ni installation**. Les moteurs | |
| # cloud et les plugins tiers restent *gated* (mode public, fail-closed côté appli). | |
| # | |
| # Contexte de build « racine plate » (Dockerfile / requirements.txt / pyproject.toml | |
| # / cinoc / reports TOUS à la racine) — assemblé par .github/workflows/deploy-space.yml, | |
| # comme Dockerfile.space. | |
| FROM python:3.11-slim | |
| # ── Dépendances système : Tesseract 5 + langues + libs image (Pillow) ────────── | |
| # Les langues sont baquées (pas téléchargées au runtime) : un Space free-tier n'a | |
| # pas d'accès réseau garanti au démarrage, et le déterminisme exige une version de | |
| # binaire figée. ``fra`` est le besoin patrimonial central ; ``lat`` (latin) et | |
| # ``eng`` complètent. Pas de pin de version exacte (Debian point-release rebump | |
| # fréquemment ``5.3.0-2`` → ``+deb12u1`` → casse le build) : la repro repose sur la | |
| # capture de ``tesseract --version`` dans ``RunManifest.system_binaries_lock``. | |
| RUN apt-get update -o Acquire::Retries=3 \ | |
| && apt-get install -y --no-install-recommends -o Acquire::Retries=3 \ | |
| tesseract-ocr \ | |
| tesseract-ocr-fra \ | |
| tesseract-ocr-lat \ | |
| tesseract-ocr-eng \ | |
| libtiff6 \ | |
| libjpeg62-turbo \ | |
| libpng16-16 \ | |
| libwebp7 \ | |
| && apt-get clean \ | |
| && rm -rf /var/lib/apt/lists/* | |
| # ── Variables d'exécution ────────────────────────────────────────────────────── | |
| # TESSDATA_PREFIX : emplacement Debian bookworm des modèles Tesseract 5. | |
| ENV TESSDATA_PREFIX=/usr/share/tesseract-ocr/5/tessdata | |
| # OMP_THREAD_LIMIT=1 : OBLIGATOIRE sur free-tier 2 vCPU. Tesseract LSTM (oem 3) | |
| # parallélise via OpenMP ; non borné sur un CPU partagé il **deadlock** (incident | |
| # documenté côté Picarones, 2026-05-16) — l'OCR gèle et chaque document timeoute. | |
| # Mono-thread par appel = plus rapide ET déterministe ici. | |
| ENV OMP_THREAD_LIMIT=1 | |
| # Mode public (verrou *fail-closed*) **non forcé** : par défaut l'instance exécute | |
| # ses moteurs (cloud compris) avec la clé de l'opérateur — pas de 403. Qui expose | |
| # un Space **public** et veut protéger sa clé d'un visiteur pose lui-même | |
| # CINOC_PUBLIC_MODE=true (alors seul le socle gratuit tesseract s'exécute). | |
| ENV PYTHONUNBUFFERED=1 | |
| # ── Smoke-test du binaire au BUILD (fail-fast) ───────────────────────────────── | |
| # Un Tesseract cassé OU figé doit casser le BUILD (le Space garde l'image | |
| # précédente fonctionnelle), pas déployer une image qui gèle en silence : | |
| # (a) le binaire charge ses libs et expose la langue ``fra`` (sinon build refusé) ; | |
| # (b) une reconnaissance réelle sur une image générée se TERMINE sous 30 s — un | |
| # timeout (rc 124) = deadlock OpenMP / libs incohérentes → build refusé. | |
| RUN set -eu; \ | |
| timeout 30 tesseract --version; \ | |
| timeout 30 tesseract --list-langs 2>&1 | grep -qx fra; \ | |
| printf 'P1\n16 16\n' > /tmp/smoke.pbm; \ | |
| i=0; while [ "$i" -lt 16 ]; do \ | |
| printf '1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0\n' >> /tmp/smoke.pbm; \ | |
| i=$((i + 1)); \ | |
| done; \ | |
| rc=0; \ | |
| timeout 30 tesseract /tmp/smoke.pbm - -l fra --psm 6 >/dev/null 2>&1 || rc=$?; \ | |
| rm -f /tmp/smoke.pbm; \ | |
| if [ "$rc" = 124 ]; then \ | |
| echo "FATAL: tesseract a gelé (timeout) — build refusé, libs incohérentes." >&2; \ | |
| exit 1; \ | |
| fi | |
| # ── Utilisateur non-root (sécurité d'un service exposé) ──────────────────────── | |
| RUN useradd --create-home --uid 1000 cinoc | |
| WORKDIR /home/cinoc/app | |
| # ── Dépendances Python d'abord (cache de couche) ─────────────────────────────── | |
| # requirements.txt fige le cœur + l'extra [serve] + pytesseract (wrapper du binaire). | |
| COPY requirements.txt ./requirements.txt | |
| RUN pip install --no-cache-dir -r requirements.txt | |
| # ── Code + paquet (sans deps : tout est déjà figé ci-dessus) ─────────────────── | |
| COPY pyproject.toml README.md ./ | |
| COPY cinoc ./cinoc | |
| RUN pip install --no-cache-dir --no-deps . | |
| # ── Rapports bakés (lecture seule) — dossier toujours présent ────────────────── | |
| COPY reports ./reports | |
| ENV CINOC_REPORTS_DIR=/home/cinoc/app/reports | |
| USER cinoc | |
| EXPOSE 7860 | |
| # 0.0.0.0 attendu en conteneur (le réseau est géré par la plateforme). | |
| CMD ["cinoc", "serve", "--host", "0.0.0.0", "--port", "7860"] | |