Le job "test" échouait ("Could not open requirements file:
requirements-dev.txt") : docker run -v "$PWD":/app lancé DEPUIS un
runner qui exécute déjà le job dans son propre conteneur (Docker-
outside-of-Docker) ne peut pas monter "$PWD" — ce chemin vit dans le
conteneur du job, pas sur l'hôte où tourne le vrai démon Docker sollicité
par ce docker run imbriqué ; /app se retrouvait donc vide dans le
conteneur imbriqué.
Corrigé en utilisant la clé "container" (standard Gitea/GitHub Actions) :
le job tourne DIRECTEMENT dans l'image voulue, le checkout dépose les
fichiers dans son propre système de fichiers, aucun montage de volume à
faire. Un seul job "test" ne peut avoir qu'UNE image : scindé en
test-python (python:3.13-slim) et test-js (node:20-slim, pour les tests
node:test de static/js/play/__tests__/), build-and-push dépend des deux.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
111 lines
4.6 KiB
YAML
111 lines
4.6 KiB
YAML
name: Build and deploy
|
|
|
|
on:
|
|
push:
|
|
branches: [main, dev]
|
|
|
|
# Le job "test" tourne sur CHAQUE push (main et dev) : jusqu'ici aucune
|
|
# étape de CI n'exécutait la suite de tests, rien n'empêchait un commit
|
|
# cassé d'atteindre la production (voir l'audit qualité de la Phase 0 du
|
|
# plan). "build-and-push"/"deploy", eux, restent réservés à main (via le
|
|
# filtre "if" sur gitea.ref) — un push sur dev ne doit jamais redéployer
|
|
# la prod, seulement faire tourner les tests.
|
|
|
|
# Secrets à configurer dans Gitea (Paramètres du dépôt > Actions > Secrets) :
|
|
# REGISTRY_HOST adresse du registre d'images (ex: gitea.exemple.com)
|
|
# REGISTRY_IMAGE chemin complet de l'image (ex: gitea.exemple.com/mon-compte/forge-engine)
|
|
# REGISTRY_USER utilisateur pour "docker login" sur ce registre
|
|
# REGISTRY_TOKEN jeton d'accès (scope package:write) pour ce registre
|
|
# DEPLOY_HOST adresse du serveur de production
|
|
# DEPLOY_USER utilisateur SSH sur ce serveur
|
|
# DEPLOY_SSH_KEY clé privée SSH (au format PEM) autorisée sur ce serveur
|
|
# DEPLOY_PATH dossier sur le serveur où vit docker-compose.prod.yml (ex: /home/deploy/forge-engine)
|
|
#
|
|
# Utilise directement docker/ssh/scp en ligne de commande plutôt que des
|
|
# actions du marketplace, pour ne pas dépendre de la disponibilité de
|
|
# github.com / du miroir gitea.com depuis le runner self-hosted.
|
|
|
|
jobs:
|
|
# Le job tourne DIRECTEMENT dans le conteneur (clé "container", standard
|
|
# Gitea/GitHub Actions) plutôt que de lancer un `docker run` imbriqué
|
|
# depuis le runner — un essai précédent (docker run -v "$PWD":/app ...)
|
|
# échouait avec "Could not open requirements file" : sur un runner qui
|
|
# exécute déjà le job dans un conteneur (Docker-outside-of-Docker), "$PWD"
|
|
# ne correspond à aucun chemin que le démon Docker de l'HÔTE peut monter,
|
|
# /app se retrouvait donc vide dans le conteneur imbriqué. Avec "container",
|
|
# le checkout dépose directement les fichiers dans le système de fichiers
|
|
# du conteneur du job, aucun montage de volume à faire soi-même.
|
|
test-python:
|
|
runs-on: ubuntu-latest
|
|
container:
|
|
image: python:3.13-slim
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- name: Installer les dépendances Python (dev)
|
|
run: pip install --quiet -r requirements-dev.txt
|
|
- name: Tests Python (pytest)
|
|
run: python -m pytest tests/ -q
|
|
|
|
test-js:
|
|
runs-on: ubuntu-latest
|
|
container:
|
|
image: node:20-slim
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- name: Tests JS (node:test — logique pure de static/js/play/, voir le plan de modularisation)
|
|
run: node --test static/js/play/__tests__/*.test.js
|
|
|
|
build-and-push:
|
|
needs: [test-python, test-js]
|
|
if: gitea.ref == 'refs/heads/main'
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Connexion au registre d'images
|
|
run: echo "${{ secrets.REGISTRY_TOKEN }}" | docker login "${{ secrets.REGISTRY_HOST }}" -u "${{ secrets.REGISTRY_USER }}" --password-stdin
|
|
|
|
- name: Build de l'image
|
|
run: |
|
|
docker build \
|
|
-t "${{ secrets.REGISTRY_IMAGE }}:${{ gitea.sha }}" \
|
|
-t "${{ secrets.REGISTRY_IMAGE }}:latest" \
|
|
.
|
|
|
|
- name: Publication de l'image
|
|
run: |
|
|
docker push "${{ secrets.REGISTRY_IMAGE }}:${{ gitea.sha }}"
|
|
docker push "${{ secrets.REGISTRY_IMAGE }}:latest"
|
|
|
|
deploy:
|
|
needs: build-and-push
|
|
if: gitea.ref == 'refs/heads/main'
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Préparation de la clé SSH
|
|
run: |
|
|
mkdir -p ~/.ssh
|
|
echo "${{ secrets.DEPLOY_SSH_KEY }}" > ~/.ssh/deploy_key
|
|
chmod 600 ~/.ssh/deploy_key
|
|
ssh-keyscan -H "${{ secrets.DEPLOY_HOST }}" >> ~/.ssh/known_hosts
|
|
|
|
- name: Envoi du docker-compose.prod.yml sur le serveur
|
|
run: |
|
|
scp -i ~/.ssh/deploy_key docker-compose.prod.yml \
|
|
"${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }}:${{ secrets.DEPLOY_PATH }}/docker-compose.prod.yml"
|
|
|
|
- name: Pull et redémarrage sur le serveur
|
|
run: |
|
|
ssh -i ~/.ssh/deploy_key "${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }}" '
|
|
set -e
|
|
cd "${{ secrets.DEPLOY_PATH }}"
|
|
echo "REGISTRY_IMAGE=${{ secrets.REGISTRY_IMAGE }}" > .env
|
|
echo "IMAGE_TAG=${{ gitea.sha }}" >> .env
|
|
docker login "${{ secrets.REGISTRY_HOST }}" -u "${{ secrets.REGISTRY_USER }}" -p "${{ secrets.REGISTRY_TOKEN }}"
|
|
docker compose -f docker-compose.prod.yml pull
|
|
docker compose -f docker-compose.prod.yml up -d
|
|
docker image prune -f
|
|
'
|