"no tests ran ... file or directory not found: tests/" : .dockerignore (à la racine, pensé pour l'image de PROD buildée par build-and-push) exclut tests/ du contexte de build — COPY . . dans le Dockerfile jetable de test-python ne l'incluait donc jamais, quel que soit le Dockerfile utilisé (.dockerignore s'applique au contexte entier envoyé au démon, pas à un -f en particulier). Renomme .dockerignore avant ce build précis (le checkout de ce job est jetable, propre à lui, jamais repoussé vers le dépôt réel) — test-js n'a pas besoin du même correctif, il ne copie que static/js/play/, jamais exclu. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
133 lines
5.9 KiB
YAML
133 lines
5.9 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:
|
|
# Deux essais précédents ont échoué :
|
|
# 1. docker run -v "$PWD":/app ... imbriqué depuis le runner — sur un
|
|
# runner qui exécute déjà le job dans son propre conteneur (Docker-
|
|
# outside-of-Docker), "$PWD" ne correspond à aucun chemin que le démon
|
|
# Docker de l'HÔTE peut monter : /app se retrouvait vide.
|
|
# 2. jobs.test.container: image: python:3.13-slim — actions/checkout@v4
|
|
# (une action Node.js) a alors besoin de Node dans CE conteneur pour
|
|
# s'exécuter, qui ne l'a pas ("command not found", voir
|
|
# nektos/act#107) : `container:` remplace tout l'environnement des
|
|
# steps, pas seulement celui des commandes qu'on y lance soi-même.
|
|
# Solution : le job tourne sur le runner PAR DÉFAUT (checkout fonctionne,
|
|
# Node y est déjà disponible), et les tests s'exécutent PENDANT un
|
|
# `docker build` (Dockerfile passé par stdin, jamais commité) — le
|
|
# transfert du contexte de build au démon Docker ne dépend JAMAIS d'un
|
|
# chemin hôte/montage de volume (protocole API, un tar envoyé tel quel),
|
|
# donc insensible au problème Docker-outside-of-Docker du point 1.
|
|
test-python:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- name: Tests Python (pytest, exécutés PENDANT le build d'une image jetable)
|
|
run: |
|
|
# .dockerignore (à la racine, pensé pour l'image de PROD) exclut
|
|
# tests/ du contexte de build — COPY . . ne l'aurait donc jamais
|
|
# inclus, quel que soit le Dockerfile utilisé (.dockerignore
|
|
# s'applique au contexte entier, pas à un Dockerfile en
|
|
# particulier). Neutralisé ici SANS RISQUE : ce checkout est
|
|
# propre à ce job, jetable, jamais repoussé vers le dépôt.
|
|
mv .dockerignore .dockerignore.disabled-for-ci
|
|
docker build -f - -t forge-test-python:${{ gitea.sha }} . <<'DOCKERFILE'
|
|
FROM python:3.13-slim
|
|
WORKDIR /app
|
|
COPY requirements.txt requirements-dev.txt ./
|
|
RUN pip install --no-cache-dir -r requirements-dev.txt
|
|
COPY . .
|
|
RUN python -m pytest tests/ -q
|
|
DOCKERFILE
|
|
|
|
test-js:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- name: Tests JS (node:test — logique pure de static/js/play/, voir le plan de modularisation)
|
|
run: |
|
|
docker build -f - -t forge-test-js:${{ gitea.sha }} . <<'DOCKERFILE'
|
|
FROM node:20-slim
|
|
WORKDIR /app
|
|
COPY static/js/play/ static/js/play/
|
|
RUN node --test static/js/play/__tests__/*.test.js
|
|
DOCKERFILE
|
|
|
|
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
|
|
'
|