Compare commits

...

3 commits

Author SHA1 Message Date
1eb7308527 Merge pull request 'ci(release): publie les images Docker sur le registre Forgejo' (#145) from feat/release-push-docker-images into main
All checks were successful
CI / lint (push) Successful in 3m29s
CI / build (push) Successful in 3m34s
CI / intent-service-test (push) Successful in 5m14s
CI / e2e (push) Successful in 11m15s
CI / test (push) Successful in 19m53s
CI / build-android (push) Successful in 5m17s
Reviewed-on: #145
2026-09-10 22:23:41 +02:00
7d1ee934ca ci(release): build et push des images Docker sur le registre Forgejo
All checks were successful
CI / lint (push) Successful in 2m48s
CI / build (push) Successful in 2m55s
CI / intent-service-test (push) Successful in 4m57s
CI / e2e (push) Successful in 9m28s
CI / test (push) Successful in 19m10s
CI / build-android (push) Has been skipped
Le job `sanity-build` (build de `apps/api/Dockerfile` sans push) devient
`build-and-push-images` : `docker login` sur le registre Forgejo intégré
via le token Actions du run (`permissions: packages: write`), puis build +
push des 3 images `batchcooking-{app,intent-service,llm-worker}` taguées
`vX.Y.Z` et `latest`. Un job séquentiel plutôt qu'une matrix — 3 builds
concurrents saturent le daemon Docker du runner unique (cf. mémoire
`ci-forgejo-runner-constraints`) sans gain réel pour une release.

`docker-compose.yml` référence maintenant ces images
(`image: git.dev.kyuno.fr/kyuno/batchcooking-*:${IMAGE_TAG:-latest}`) avec
le bloc `build:` gardé en repli local — Portainer peut tirer les images
pré-construites ou continuer à builder depuis Git. `.env.example` documente
`IMAGE_TAG` ; le README (section registre + Release) est réécrit.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-10 22:03:26 +02:00
5a915a6cee Merge pull request 'feat(admin): espace « Utilisateurs » pour créer des comptes' (#144) from feat/admin-user-accounts into main
Some checks failed
CI / lint (push) Successful in 3m13s
CI / build (push) Successful in 3m15s
CI / intent-service-test (push) Successful in 5m18s
CI / e2e (push) Successful in 11m42s
CI / test (push) Failing after 17m38s
CI / build-android (push) Has been skipped
Reviewed-on: #144
2026-09-10 21:48:29 +02:00
4 changed files with 96 additions and 25 deletions

View file

@ -15,6 +15,14 @@ JWT_SECRET=changeme-generate-a-real-random-secret-at-least-32-chars
# (docker-compose.yml), serving both the API and the built frontend.
# APP_PORT=3000
# Optional — tag of the prebuilt images the release CI pushes to the Forgejo
# container registry (git.dev.kyuno.fr/kyuno/batchcooking-{app,intent-service,
# llm-worker}). Used by `docker compose pull` and a pull-based Portainer
# stack; leave it `latest` or pin a release tag (e.g. v0.2.4). Ignored when
# building locally instead (`docker compose build`, or `up` with no image
# present) — see docker-compose.yml's `image:`/`build:` comment.
# IMAGE_TAG=latest
# --- Admin surface (apps/web's /admin/* routes + the /admin/* API) --------
# All optional: an instance that doesn't run the admin app needs none of
# these. `requireAdmin` fails closed when ADMIN_JWT_SECRET is unset, so

View file

@ -1,20 +1,34 @@
name: Release
# No image registry involved: Portainer is wired to this repo's Git remote
# and builds/redeploys apps/api/Dockerfile itself. This workflow sanity-checks
# that the image actually builds at the tagged commit, builds+attaches a
# signed Android APK, publishes a Forgejo Release, then (best-effort) pings
# Portainer's deploy webhook so it doesn't have to wait for its own polling
# interval.
# On a `vX.Y.Z` tag: build the three service images and push them to this
# Forgejo instance's built-in container registry (git.dev.kyuno.fr/<owner>/
# batchcooking-{app,intent-service,llm-worker}, tagged with the release tag
# and `latest`), build+attach a signed Android APK, publish a Forgejo
# Release, then (best-effort) ping Portainer's deploy webhook. Portainer can
# pull those prebuilt images (docker-compose.yml references them via
# ${IMAGE_TAG}) or keep building from the Git remote — `docker-compose.yml`
# keeps a `build:` fallback next to every `image:`.
on:
push:
tags: ["v*.*.*"]
permissions:
contents: write
# Lets this workflow's GITHUB_TOKEN push to the Forgejo container registry
# (the `build-and-push-images` job below `docker login`s with it). If a
# future instance policy scopes the run token tighter, swap that login's
# password for a `write:package` PAT added as a repo secret.
packages: write
jobs:
sanity-build:
# Build the three service images at the tagged commit and push them to the
# Forgejo container registry. A successful push also *is* the "does it
# still build" check the old `sanity-build` job did (which only covered
# apps/api). One sequential job, not a per-image matrix: three concurrent
# `docker build`s hammer this single persistent runner's Docker daemon
# (the contended resource per project memory `ci-forgejo-runner-constraints`)
# for no real gain on a release, which is rare and usually runs alone.
build-and-push-images:
runs-on: ubuntu-latest
steps:
# This runner's job containers don't come with docker preinstalled —
@ -25,10 +39,36 @@ jobs:
- uses: https://github.com/actions/checkout@v4
# Build only, no push, no registry — just confirms Portainer will be
# able to build this same Dockerfile successfully at this tag before
# anyone points it at this ref.
- run: docker build -f apps/api/Dockerfile .
- name: Build et push des 3 images vers le registre Forgejo
env:
# The per-run token Forgejo Actions mints — has `packages: write`
# via this workflow's `permissions:` block. `docker login` accepts
# it as the password for the actor who pushed the tag.
REGISTRY_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -eu
# $GITHUB_SERVER_URL is "https://git.dev.kyuno.fr" — the registry
# is that same host. $GITHUB_REPOSITORY is "<owner>/<repo>"; the
# registry namespace is the owner, lower-cased (Forgejo package
# names are case-insensitive but the CLI wants a stable form).
registry="${GITHUB_SERVER_URL#http*://}"
owner="$(printf '%s' "${GITHUB_REPOSITORY%%/*}" | tr '[:upper:]' '[:lower:]')"
tag="$GITHUB_REF_NAME"
echo "$REGISTRY_TOKEN" | docker login "$registry" -u "$GITHUB_ACTOR" --password-stdin
# Every Dockerfile's build context is the repo root (see
# docker-compose.yml's `build.context: .`), hence the trailing `.`.
build_push() {
image="$registry/$owner/$1"
echo "::group::$1"
docker build -f "$2" -t "$image:$tag" -t "$image:latest" .
docker push "$image:$tag"
docker push "$image:latest"
echo "::endgroup::"
}
build_push batchcooking-app apps/api/Dockerfile
build_push batchcooking-intent-service services/tech-step-intent-service/Dockerfile
build_push batchcooking-llm-worker services/tech-step-llm-worker/Dockerfile
# Builds the same signed APK as ci.yml's build-android job (issue #64),
# with one difference: the version baked into the APK is derived from the
@ -124,11 +164,11 @@ jobs:
forgejo-release:
runs-on: ubuntu-latest
needs: [sanity-build, build-android-apk]
needs: [build-and-push-images, build-android-apk]
steps:
# Ce runner ne fournit ni docker ni curl par défaut dans ses
# conteneurs de job — même constat déjà fait pour docker (voir
# sanity-build) et pour curl dans ci.yml's test job.
# build-and-push-images) et pour curl dans ci.yml's test job.
- name: Install curl
run: apt-get update && apt-get install -y curl

View file

@ -207,10 +207,18 @@ depuis la même origine. Les deux autres services du fichier
(`tech-step-intent-service`, `tech-step-llm-worker`) n'exposent eux aucun port
au host — voir leurs propres README pour leur rôle.
**Pas de registre d'image** dans cette configuration : l'instance **Portainer** de
production est reliée directement au dépôt Git et reconstruit elle-même
`docker-compose.yml`/`apps/api/Dockerfile` à chaque déploiement — la CI ne pousse
donc aucune image nulle part.
**Registre d'image** : la CI de release (voir plus bas) construit et pousse les
trois images de service — `batchcooking-app`, `batchcooking-intent-service`,
`batchcooking-llm-worker` — sur le **registre de conteneurs intégré à Forgejo**
(`git.dev.kyuno.fr/kyuno/…`), taguées à la fois avec le tag de release (`vX.Y.Z`)
et `latest`. `docker-compose.yml` référence ces images via `IMAGE_TAG` (variable
d'environnement, défaut `latest` — voir `.env.example`), tout en gardant un bloc
`build:` local à côté de chaque `image:` : `docker compose pull` récupère les
images pré-construites, `docker compose up`/`build` retombe sur un build local si
elles sont absentes. L'instance **Portainer** de production peut donc désormais
tirer les images pré-construites (stack en mode *pull*, `IMAGE_TAG` pinné sur la
release voulue) **ou** continuer à builder elle-même depuis le dépôt Git — les
deux modes fonctionnent avec le même `docker-compose.yml`.
### Release (`.github/workflows/release.yml`)
@ -221,14 +229,16 @@ git tag vX.Y.Z
git push --tags
```
Le pipeline enchaîne trois jobs : `sanity-build` (build de l'image Docker sans
push, juste pour vérifier qu'elle build encore à ce tag avant de laisser Portainer
redéployer dessus), `github-release` (crée une Release GitHub avec changelog
auto-généré à partir des PRs mergées), puis `notify-portainer` — envoie une requête
au webhook de redeploy de Portainer si le secret de dépôt `PORTAINER_WEBHOOK_URL`
est configuré (sinon Portainer se resynchronise simplement à son prochain
polling Git). Pour l'activer : récupérer l'URL du webhook depuis les réglages du
stack Portainer, puis l'ajouter comme secret GitHub `PORTAINER_WEBHOOK_URL`.
Le pipeline enchaîne quatre jobs : `build-and-push-images` (build des 3 images à
ce tag + push sur le registre Forgejo sous `vX.Y.Z` et `latest` — un push
réussi vaut aussi vérification que tout build encore à ce commit),
`build-android-apk` (APK signé, `versionName` dérivé du tag), `forgejo-release`
(crée la Release Forgejo `vX.Y.Z` et y attache `batchcooking.apk`), puis
`notify-portainer` — envoie une requête au webhook de redeploy de Portainer si
le secret de dépôt `PORTAINER_WEBHOOK_URL` est configuré (sinon Portainer se
resynchronise à son prochain polling). Pour l'activer : récupérer l'URL du
webhook depuis les réglages du stack Portainer, puis l'ajouter comme secret de
dépôt `PORTAINER_WEBHOOK_URL`.
## Application mobile (Android)

View file

@ -23,6 +23,13 @@ services:
# apps/api/Dockerfile) — no separate nginx/web container, no cross-origin
# CORS_ORIGIN to keep in sync between two ports.
app:
# Prebuilt image pushed by .github/workflows/release.yml to the Forgejo
# container registry on every `vX.Y.Z` tag. `IMAGE_TAG` (root .env,
# default `latest`) pins which release to run — a pull-based Portainer
# stack / `docker compose pull` uses this. The `build:` block below stays
# as a local fallback: `docker compose up` builds it if the image isn't
# present, `docker compose build` (re)creates it under this same name.
image: git.dev.kyuno.fr/kyuno/batchcooking-app:${IMAGE_TAG:-latest}
build:
context: .
dockerfile: apps/api/Dockerfile
@ -87,6 +94,9 @@ services:
# port — reachable only from `app` on the compose network, nothing ever
# calls into it from outside.
tech-step-intent-service:
# See the `app` service's `image:` comment — same release-pushed / local
# fallback split, shared `IMAGE_TAG`.
image: git.dev.kyuno.fr/kyuno/batchcooking-intent-service:${IMAGE_TAG:-latest}
build:
context: .
dockerfile: services/tech-step-intent-service/Dockerfile
@ -131,6 +141,9 @@ services:
# less deployment can omit this service entirely and `app` still runs
# fine, just without the offline audit/feedback-loop jobs.
tech-step-llm-worker:
# See the `app` service's `image:` comment — same release-pushed / local
# fallback split, shared `IMAGE_TAG`.
image: git.dev.kyuno.fr/kyuno/batchcooking-llm-worker:${IMAGE_TAG:-latest}
build:
context: .
dockerfile: services/tech-step-llm-worker/Dockerfile