ci(release): publie les images Docker sur le registre Forgejo #145
4 changed files with 96 additions and 25 deletions
|
|
@ -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
|
||||
|
|
|
|||
66
.github/workflows/release.yml
vendored
66
.github/workflows/release.yml
vendored
|
|
@ -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
|
||||
|
||||
|
|
|
|||
34
README.md
34
README.md
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue