feat(docker): image nginx + publication GHCR sur tag#44
Merged
Conversation
Adds a multi-stage Dockerfile (docker/Dockerfile): - Stage 1 (python:3.12-alpine) regenerates docs/data/*.json from the per-phase Markdown via the stdlib-only dima_convert.py. - Stage 2 (nginxinc/nginx-unprivileged:alpine) serves the result on port 8080 with a custom nginx.conf: gzip, no-cache for html/json, long cache for static assets, SPA fallback, autoindex off. - HEALTHCHECK pings /index.html every 30 s. Workflow .github/workflows/docker.yml builds and pushes a multi-arch image (linux/amd64 + linux/arm64) to GHCR: - on git tag push (v*): publishes <tag> AND latest; - on pull_request touching docker/**: build-only (no push) for safety; - on workflow_dispatch: manual run. Uses gha cache to keep rebuilds fast. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Splits the two image streams so they never overwrite each other: - push to main (default branch) -> ghcr.io/.../dima:latest - push of a git tag v* -> ghcr.io/.../dima:<tag> A tagged release no longer touches :latest, and a regular merge into main no longer waits for a tag to refresh :latest. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a tests stage to the Dockerfile alongside the existing nginx runtime. The stage installs pytest, copies the source and the four phase markdown files, then runs the suite as part of the build so a failing test breaks `docker build --target tests`. CMD points at pytest so the image is also usable as `docker run --rm <image>`. The Docker workflow gains a second job that builds and publishes this test image, tagged with the same scheme as the runtime image plus a -tests suffix: main push -> ghcr.io/.../dima:latest + :latest-tests v1.2.3 push -> ghcr.io/.../dima:v1.2.3 + :v1.2.3-tests The test image is amd64-only (no need to emulate arm64 just to run pytest) and uses its own GHA cache scope. Both jobs only push outside PRs; on PR they only build, which still triggers the in-build pytest run via the tests stage and therefore catches regressions. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Cette PR ajoute une image Docker “production” basée sur nginx pour servir la SPA contenue dans docs/, ainsi qu’un workflow GitHub Actions pour builder et publier l’image sur GHCR.
Changes:
- Ajout d’un
Dockerfilemulti-stage : régénération des JSON depuis les Markdown puis image runtime nginx unprivileged sur le port 8080. - Ajout d’une config nginx (gzip, politiques de cache, fallback SPA).
- Ajout d’un workflow CI pour build multi-arch et push sur GHCR (plus une image “tests” ciblant
pytest).
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
docker/nginx.conf |
Configuration nginx pour servir la SPA avec compression, cache headers et fallback index.html. |
docker/Dockerfile |
Build multi-stage : génération JSON (builder), exécution pytest (target tests), runtime nginx. |
.github/workflows/docker.yml |
CI Docker : build multi-arch runtime + build tests, push GHCR selon événements. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Five fixes raised by the Copilot review on this PR: - Switch both builder and tests stages to ghcr.io/astral-sh/uv:python3.12-alpine and drop `pip install pytest`. The tests stage now runs `uv sync --group dev` to pick up pytest with the version range pinned in pyproject.toml, then `uv run pytest` to execute it. Same toolchain (uv) everywhere. - nginx: re-apply Cache-Control: no-cache to the SPA fallback in `location /` so that /index.html served on unknown routes (e.g. /campaign/123) doesn't silently sit in a downstream cache after a redeploy. - workflow: add a `type=sha,prefix=sha-` fallback tag enabled on workflow_dispatch, and guard the build-push step with `steps.meta.outputs.tags != ''`, so a manual run from a non-default branch never tries to push with no tag. - New .dockerignore keeping .git/, the LaTeX article/, the published PDF, plugin/, misp/, .claude/ and various caches out of the build context. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two remaining Copilot remarks: - Commit uv.lock and drop the .gitignore entry for it. The tests stage now does `uv sync --group dev --frozen`, so rebuilding the test image from the same commit always resolves the exact pytest version that passed CI (pytest>=8 alone could otherwise drift). - Extend the docker.yml pull_request path filter to also watch docs/** and pyproject.toml (and uv.lock now that it's tracked). Both feed into the Docker build (COPY docs/ in runtime, uv sync in tests), so a PR touching only the SPA assets or dependency config should still validate the Docker build. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
BartM82
approved these changes
May 16, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Ajoute une image Docker prete pour la prod qui sert la SPA via nginx, plus la CI qui la publie sur GHCR.
docker/Dockerfile (multi-stage)
python:3.12-alpine) : regeneredocs/data/*.jsondepuis les.mdsources viadima_convert.py(stdlib only, aucune dependance). L'image est ainsi autocoherente : pas besoin que les JSON soient a jour dans le commit.nginxinc/nginx-unprivileged:alpine) : sertdocs/sur le port 8080 (non-root).HEALTHCHECK: ping de/toutes les 30 s.docker/nginx.conf
Cache-Control: no-cache, must-revalidatepour.htmlet.json(un redeploiement se voit tout de suite).Cache-Control: max-age=2592000, immutablepour les assets type fonts/images.try_files $uri $uri/ /index.html..github/workflows/docker.yml
v*: build multi-arch (linux/amd64+linux/arm64) puis push GHCR avec deux tags : la version du git tag ETlatest.docker/**: build-only (no push), pour valider que le Dockerfile compile encore.workflow_dispatch: run manuel.type=ghaactif pour des rebuilds rapides.Premier usage
docker run --rm -p 8080:8080 ghcr.io/m82-project/dima:latest # puis ouvrir http://localhost:8080A faire apres le premier push d'image
public(sinon GHCR demande une authentification au pull).Test plan
buildpasse sur cette PR (build-only).v1.0.0, verifier queghcr.io/m82-project/dima:v1.0.0et:latestexistent.docker run --rm -p 8080:8080 ghcr.io/m82-project/dima:latestet tester la SPA dans le navigateur (recherche, panneau detail, bouton PDF, rapport de campagne).Generated with Claude Code