From 010cac8adca23e5cc4bf06a0d04d93280a1dee40 Mon Sep 17 00:00:00 2001 From: AdamThomas Date: Wed, 16 Sep 2026 21:09:28 +0200 Subject: [PATCH] gitea ci --- .gitea/workflows/ci-cd.yml | 168 +++++++++++++++++++++++++++++++++ .gitlab-ci.yml | 116 ----------------------- CLAUDE.md | 23 +++-- README.md | 2 +- backend/app/seed.py | 31 +++--- deploy/README.md | 59 +++++++----- deploy/docker-compose.prod.yml | 6 +- frontend/tailwind.config.js | 2 +- 8 files changed, 237 insertions(+), 170 deletions(-) create mode 100644 .gitea/workflows/ci-cd.yml delete mode 100644 .gitlab-ci.yml diff --git a/.gitea/workflows/ci-cd.yml b/.gitea/workflows/ci-cd.yml new file mode 100644 index 0000000..9352a7e --- /dev/null +++ b/.gitea/workflows/ci-cd.yml @@ -0,0 +1,168 @@ +# CI/CD Gitea Actions : tests à chaque push, puis sur `main` seulement, +# build des images (registre de conteneurs intégré à Gitea) et déploiement +# SSH sur le VPS. +# +# Nécessite un runner Gitea Actions (act_runner, exécuteur "docker") attaché +# au dépôt ou à l'organisation. +# +# Secrets à définir (Paramètres du dépôt → Actions → Secrets) : +# REGISTRY_TOKEN jeton d'accès (scope "write:package") pour pousser sur +# le registre de conteneurs de cette instance Gitea +# SSH_KEY clé privée SSH (format OpenSSH) pour se connecter au VPS +# +# Variables à définir (Paramètres du dépôt → Actions → Variables) : +# REGISTRY_HOST hôte du registre (ex. gitea.mon-domaine.fr) +# REGISTRY_NAMESPACE espace du registre (ex. /) +# REGISTRY_USER utilisateur associé à REGISTRY_TOKEN +# SSH_HOST hôte du VPS +# SSH_USER utilisateur SSH +# DEPLOY_PATH dossier du projet sur le VPS (ex. /opt/benevoles) +# VITE_EDITION_ID UUID de l'édition de production, à renseigner APRÈS +# la toute première mise en service (voir deploy/README.md) + +name: ci-cd + +on: + push: + branches: ['**'] + +jobs: + backend-test: + runs-on: ubuntu-latest + container: python:3.12-slim + services: + postgres: + image: postgres:16-alpine + env: + POSTGRES_USER: benevoles + POSTGRES_PASSWORD: benevoles + POSTGRES_DB: benevoles_test + ports: + - 5432:5432 + env: + TEST_DATABASE_URL: postgresql+psycopg://benevoles:benevoles@postgres:5432/benevoles_test + DATABASE_URL: postgresql+psycopg://benevoles:benevoles@postgres:5432/benevoles_test + steps: + - name: Installer git + run: apt-get update && apt-get install -y --no-install-recommends git + - uses: actions/checkout@v4 + - name: Installer uv + run: pip install --no-cache-dir uv + - name: Installer les dépendances + working-directory: backend + run: uv sync + - name: Lint + working-directory: backend + run: uv run ruff check . + - name: Migrer la base de test + working-directory: backend + run: uv run alembic upgrade head + - name: Vérifier migration ↔ modèle + working-directory: backend + run: uv run alembic check + - name: Tests + working-directory: backend + run: uv run pytest -q + + frontend-test: + runs-on: ubuntu-latest + container: node:20-alpine + steps: + - name: Installer git + run: apk add --no-cache git + - uses: actions/checkout@v4 + - name: Installer les dépendances + working-directory: frontend + run: npm ci + - name: Typecheck + working-directory: frontend + run: npm run typecheck + - name: Lint + working-directory: frontend + run: npm run lint + - name: Build + working-directory: frontend + run: npm run build + - name: Smoke test + working-directory: frontend + run: npm run smoke + + build-backend: + needs: backend-test + if: gitea.ref == 'refs/heads/main' + runs-on: ubuntu-latest + container: docker:27 + services: + docker: + image: docker:27-dind + env: + DOCKER_HOST: tcp://docker:2375 + DOCKER_TLS_CERTDIR: "" + steps: + - name: Installer git + run: apk add --no-cache git + - uses: actions/checkout@v4 + - name: Connexion au registre + run: echo "${{ secrets.REGISTRY_TOKEN }}" | docker login "${{ vars.REGISTRY_HOST }}" -u "${{ vars.REGISTRY_USER }}" --password-stdin + - name: Build & push + run: | + IMAGE="${{ vars.REGISTRY_HOST }}/${{ vars.REGISTRY_NAMESPACE }}/backend" + docker build -t "$IMAGE:${{ gitea.sha }}" -t "$IMAGE:latest" backend + docker push "$IMAGE:${{ gitea.sha }}" + docker push "$IMAGE:latest" + + build-frontend: + needs: frontend-test + if: gitea.ref == 'refs/heads/main' + runs-on: ubuntu-latest + container: docker:27 + services: + docker: + image: docker:27-dind + env: + DOCKER_HOST: tcp://docker:2375 + DOCKER_TLS_CERTDIR: "" + steps: + - name: Installer git + run: apk add --no-cache git + - uses: actions/checkout@v4 + - name: Connexion au registre + run: echo "${{ secrets.REGISTRY_TOKEN }}" | docker login "${{ vars.REGISTRY_HOST }}" -u "${{ vars.REGISTRY_USER }}" --password-stdin + - name: Build & push + run: | + IMAGE="${{ vars.REGISTRY_HOST }}/${{ vars.REGISTRY_NAMESPACE }}/frontend" + docker build \ + --build-arg VITE_API_MODE=http \ + --build-arg VITE_EDITION_ID="${{ vars.VITE_EDITION_ID }}" \ + -f frontend/Dockerfile.prod \ + -t "$IMAGE:${{ gitea.sha }}" -t "$IMAGE:latest" frontend + docker push "$IMAGE:${{ gitea.sha }}" + docker push "$IMAGE:latest" + + deploy: + needs: [build-backend, build-frontend] + if: gitea.ref == 'refs/heads/main' + runs-on: ubuntu-latest + container: alpine:3.20 + steps: + - name: Installer les outils + run: apk add --no-cache git openssh-client rsync + - uses: actions/checkout@v4 + - name: Déployer par SSH + run: | + mkdir -p ~/.ssh && chmod 700 ~/.ssh + echo "${{ secrets.SSH_KEY }}" | tr -d '\r' > ~/.ssh/id_ed25519 + chmod 600 ~/.ssh/id_ed25519 + ssh-keyscan -H "${{ vars.SSH_HOST }}" >> ~/.ssh/known_hosts + rsync -az -e "ssh -i ~/.ssh/id_ed25519" deploy/docker-compose.prod.yml "${{ vars.SSH_USER }}@${{ vars.SSH_HOST }}:${{ vars.DEPLOY_PATH }}/docker-compose.prod.yml" + ssh -i ~/.ssh/id_ed25519 "${{ vars.SSH_USER }}@${{ vars.SSH_HOST }}" sh -s < - docker build - --build-arg VITE_API_MODE=http - --build-arg VITE_EDITION_ID="$VITE_EDITION_ID" - -f frontend/Dockerfile.prod - -t "$FRONTEND_IMAGE:$CI_COMMIT_SHORT_SHA" -t "$FRONTEND_IMAGE:latest" - frontend - - docker push "$FRONTEND_IMAGE:$CI_COMMIT_SHORT_SHA" - - docker push "$FRONTEND_IMAGE:latest" - -deploy: - stage: deploy - image: alpine:3.20 - rules: - - if: '$CI_COMMIT_BRANCH == "main"' - before_script: - - apk add --no-cache openssh-client rsync - - eval "$(ssh-agent -s)" - - echo "$SSH_KEY" | tr -d '\r' | ssh-add - - - mkdir -p ~/.ssh && chmod 700 ~/.ssh - - ssh-keyscan -H "$SSH_HOST" >> ~/.ssh/known_hosts - script: - - rsync -az deploy/docker-compose.prod.yml "$SSH_USER@$SSH_HOST:$DEPLOY_PATH/docker-compose.prod.yml" - - | - ssh "$SSH_USER@$SSH_HOST" sh -s < 24 ⇒ le lendemain) diff --git a/deploy/README.md b/deploy/README.md index 40b234c..e5298cd 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -1,15 +1,18 @@ # Déploiement -Le pipeline [`.gitlab-ci.yml`](../.gitlab-ci.yml) se déclenche sur **chaque -push** : +Le pipeline [`.gitea/workflows/ci-cd.yml`](../.gitea/workflows/ci-cd.yml) (Gitea +Actions) se déclenche sur **chaque push** : 1. tests (backend : `ruff` + `pytest` + `alembic check` ; frontend : `typecheck` + `lint` + `build` + `smoke`) ; 2. sur `main` seulement : build des images `backend` et `frontend`, poussées - sur le registre de conteneurs du projet GitLab ; + sur le registre de conteneurs intégré à Gitea ; 3. copie de `deploy/docker-compose.prod.yml` sur le VPS par SSH ; 4. `docker compose pull`, migration Alembic, `up -d`. +Nécessite un runner Gitea Actions (`act_runner`, exécuteur Docker) activé sur +l'instance et attaché au dépôt. + Le VPS n'expose rien directement : un **Caddy déjà installé sur le serveur** (service système, hors de ce dépôt) reçoit le trafic public sur 443 et reverse-proxy vers les deux conteneurs, qui n'écoutent que sur @@ -39,27 +42,34 @@ serveur** (service système, hors de ce dépôt) reçoit le trafic public sur Caddy) : suffisant contre la force brute sur un compte connu, pas contre une attaque distribuée sur de nombreux emails différents. -## Étape 0 — pousser le dépôt sur GitLab +## Étape 0 — pousser le dépôt sur Gitea ```bash -# Créer d'abord un projet vide sur GitLab (sans README ni .gitignore), puis : -git remote add origin git@gitlab.com:/.git +# Créer d'abord un dépôt vide sur Gitea (sans README ni .gitignore), puis : +git remote add origin git@gitea.mon-domaine.fr:/.git git push -u origin main ``` -## Variables CI/CD à définir (Paramètres → CI/CD → Variables) +## Secrets et variables à définir (Paramètres du dépôt → Actions) -| Variable | Description | +Secrets (Paramètres → Actions → Secrets) : + +| Secret | Description | |-------------------|-------------------------------------------------------------------| -| `SSH_HOST` | Hôte du VPS | -| `SSH_USER` | Utilisateur SSH | -| `SSH_KEY` | Clé privée SSH (format OpenSSH) — **masquée**, sur `main` seulement| -| `DEPLOY_PATH` | Dossier du projet sur le VPS (ex. `/opt/benevoles`) | -| `VITE_EDITION_ID` | UUID de l'édition de production — **à ajouter après** l'étape 3 | +| `REGISTRY_TOKEN` | Jeton d'accès (scope `write:package`) pour pousser sur le registre de conteneurs de l'instance Gitea | +| `SSH_KEY` | Clé privée SSH (format OpenSSH) | -`CI_REGISTRY`, `CI_REGISTRY_USER`, `CI_REGISTRY_PASSWORD` et -`CI_REGISTRY_IMAGE` sont fournies automatiquement par GitLab, rien à -configurer. +Variables (Paramètres → Actions → Variables) : + +| Variable | Description | +|-----------------------|-------------------------------------------------------------------| +| `REGISTRY_HOST` | Hôte du registre (ex. `gitea.mon-domaine.fr`) | +| `REGISTRY_NAMESPACE` | Espace du registre (ex. `/`) | +| `REGISTRY_USER` | Utilisateur associé à `REGISTRY_TOKEN` | +| `SSH_HOST` | Hôte du VPS | +| `SSH_USER` | Utilisateur SSH | +| `DEPLOY_PATH` | Dossier du projet sur le VPS (ex. `/opt/benevoles`) | +| `VITE_EDITION_ID` | UUID de l'édition de production — **à ajouter après** l'étape 3 | La clé publique correspondant à `SSH_KEY` doit être dans le `~/.ssh/authorized_keys` de `SSH_USER` sur le VPS. @@ -101,10 +111,10 @@ quel — il n'y a rien d'autre à y copier. Le frontend a besoin de l'UUID d'une édition pour être construit (voir plus bas) : le tout premier déploiement se fait donc en deux temps. -1. Sur GitLab, lancer le pipeline sur `main` normalement (Push → CI/CD → - Pipelines, ou un simple commit). Le job `build-frontend` échouera ou - produira une image sans édition tant que `VITE_EDITION_ID` n'existe pas : - c'est attendu, on l'ignore pour l'instant. +1. Sur Gitea, lancer le pipeline sur `main` normalement (Push → Actions, ou + un simple commit). Le job `build-frontend` échouera ou produira une image + sans édition tant que `VITE_EDITION_ID` n'existe pas : c'est attendu, on + l'ignore pour l'instant. 2. Une fois `build-backend` et `deploy` passés, `db` et `backend` tournent sur le VPS. @@ -129,10 +139,11 @@ docker compose -f docker-compose.prod.yml exec -T db \ ## Étape 4 — brancher le frontend sur cette édition -Dans GitLab, ajouter la variable CI/CD `VITE_EDITION_ID` avec cet UUID, puis -relancer le pipeline sur `main` (bouton *Retry* sur le dernier pipeline, ou un -nouveau commit). Cette fois `build-frontend` construit une image qui pointe -sur la bonne édition, et `deploy` démarre le service `frontend`. +Dans Gitea, ajouter la variable `VITE_EDITION_ID` (Paramètres → Actions → +Variables) avec cet UUID, puis relancer le pipeline sur `main` (bouton +*Re-run* sur le dernier run, ou un nouveau commit). Cette fois +`build-frontend` construit une image qui pointe sur la bonne édition, et +`deploy` démarre le service `frontend`. `VITE_EDITION_ID` ne change plus ensuite : elle ne se touche à nouveau que pour une nouvelle édition (festival de l'année suivante) qu'on voudrait diff --git a/deploy/docker-compose.prod.yml b/deploy/docker-compose.prod.yml index 33441fc..adf6dff 100644 --- a/deploy/docker-compose.prod.yml +++ b/deploy/docker-compose.prod.yml @@ -4,7 +4,7 @@ name: benevoles # côté de ce fichier (non versionné) : POSTGRES_USER, POSTGRES_PASSWORD, # POSTGRES_DB, JWT_SECRET, PUBLIC_BASE_URL et les variables SMTP_* / EMAIL_ENABLED. # BACKEND_IMAGE / FRONTEND_IMAGE sont exportées par le pipeline de déploiement -# (.gitlab-ci.yml). +# (.gitea/workflows/ci-cd.yml). # # Les deux services ne publient que sur `127.0.0.1` : c'est le Caddy du VPS # (hors de ce compose, voir deploy/README.md) qui reçoit le trafic public et @@ -27,7 +27,7 @@ services: retries: 10 backend: - image: ${BACKEND_IMAGE:-registry.gitlab.com/OWNER/REPO/backend:latest} + image: ${BACKEND_IMAGE:-gitea.mon-domaine.fr/OWNER/REPO/backend:latest} environment: DATABASE_URL: postgresql+psycopg://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB} ENVIRONMENT: production @@ -50,7 +50,7 @@ services: restart: unless-stopped frontend: - image: ${FRONTEND_IMAGE:-registry.gitlab.com/OWNER/REPO/frontend:latest} + image: ${FRONTEND_IMAGE:-gitea.mon-domaine.fr/OWNER/REPO/frontend:latest} depends_on: - backend ports: diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js index 6e9eacc..e3ef1bc 100644 --- a/frontend/tailwind.config.js +++ b/frontend/tailwind.config.js @@ -38,7 +38,7 @@ export default { 'hatch-orange': 'repeating-linear-gradient(45deg,#FF5C39 0 6px,#E0421F 6px 12px)', 'hatch-mauve': - 'repeating-linear-gradient(45deg,#CBA6E3 0 6px,#BE93D9 6px 12px)', + 'repeating-linear-gradient(-45deg,#CBA6E3 0 6px,#BE93D9 6px 12px)', // Hachures « brouillon » 'hatch-ink': 'repeating-linear-gradient(45deg,#14110E 0 8px,#2A2520 8px 16px)',