name: CI # Tests, lint et builds des trois projets, plus une installation Docker complète testée comme un # auto-hébergeur. Le déploiement de production (deploy-hostinger.yml) attend la réussite de ce workflow. on: push: branches: - '**' pull_request: permissions: contents: read # Un nouveau push sur la même branche annule la vérification devenue obsolète. concurrency: group: ci-${{ github.ref }} cancel-in-progress: true jobs: frontend: name: Frontend runs-on: ubuntu-latest timeout-minutes: 15 defaults: run: working-directory: frontend steps: - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0 with: persist-credentials: false - uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 with: node-version: 24 cache: npm cache-dependency-path: frontend/package-lock.json - name: Dépendances run: npm ci --no-audit --no-fund # Bloque seulement sur une faille critique d'une dépendance de production - name: Audit des dépendances de production run: npm audit --omit=dev --audit-level=critical - name: Lint run: npm run lint - name: Tests run: npm test - name: Build run: npm run build landing: name: Landing runs-on: ubuntu-latest timeout-minutes: 15 defaults: run: working-directory: landing steps: - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0 with: persist-credentials: false - uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 with: node-version: 24 cache: npm cache-dependency-path: landing/package-lock.json - name: Dépendances run: npm ci --no-audit --no-fund # Bloque seulement sur une faille critique d'une dépendance de production - name: Audit des dépendances de production run: npm audit --omit=dev --audit-level=critical - name: Tests run: npm test # Le build ne lit que landing/ : aucune police n'est prise dans ../frontend. - name: Build run: npm run build backend: name: Backend runs-on: ubuntu-latest timeout-minutes: 15 defaults: run: working-directory: backend # Les tests vitest remplacent Prisma par un mock et fixent leurs propres variables d'environnement : # ils n'utilisent pas la base. Elle sert à vérifier que les migrations s'appliquent sur une base # vide (ce que fait le premier démarrage Docker) et qu'elles reproduisent exactement schema.prisma. services: postgres: image: postgres:16 env: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres POSTGRES_DB: genealogy ports: - 5432:5432 options: >- --health-cmd "pg_isready -U postgres -d genealogy" --health-interval 5s --health-timeout 5s --health-retries 12 env: DATABASE_URL: postgresql://postgres:postgres@localhost:5432/genealogy CHECKPOINT_DISABLE: '1' steps: - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0 with: persist-credentials: false - uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 with: node-version: 24 cache: npm cache-dependency-path: backend/package-lock.json - name: Dépendances run: npm ci --no-audit --no-fund # Bloque seulement sur une faille critique d'une dépendance de production - name: Audit des dépendances de production run: npm audit --omit=dev --audit-level=critical - name: Client Prisma run: npm run prisma:generate - name: Build TypeScript run: npm run build - name: Migrations sur une base vide run: npx prisma migrate deploy - name: Schéma Prisma aligné sur les migrations run: npx prisma migrate diff --from-config-datasource --to-schema prisma/schema.prisma --exit-code - name: Tests run: npm test docker: name: Docker (auto-hébergement) runs-on: ubuntu-latest timeout-minutes: 30 steps: - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0 with: persist-credentials: false - name: Fichier .env de test run: | set -euo pipefail cp .env.example .env set_env() { if grep -q "^$1=" .env; then sed -i "s|^$1=.*|$1=$2|" .env else printf '%s=%s\n' "$1" "$2" >> .env fi } # Valeurs hexadécimales : sans caractère spécial, sûres dans l'URL de connexion PostgreSQL. set_env POSTGRES_PASSWORD "$(openssl rand -hex 24)" set_env JWT_SECRET "$(openssl rand -hex 48)" set_env WEB_PORT 8080 set_env CORS_ORIGIN http://localhost:8080 grep -Ev '^(POSTGRES_PASSWORD|JWT_SECRET)=' .env | grep -Ev '^\s*(#|$)' - name: Configuration compose valide run: docker compose config --quiet - name: Construction et démarrage run: docker compose up -d --build --wait --wait-timeout 420 - name: Test de fumée par le port web run: bash tools/docker-smoke.sh http://localhost:8080 # Les commandes de sauvegarde et de restauration du README, jouées pour de vrai - name: Sauvegarde puis restauration run: | set -euo pipefail bash tools/docker-restore-check.sh http://localhost:8080 bash tools/docker-smoke.sh http://localhost:8080 # Un second front qui joint l'API par le nom de son conteneur (API_UPSTREAM), comme sur Umbrel. - name: Front avec une autre adresse d'API run: | WEB_IMAGE="$(docker compose config --images | grep parenthese-web)" API_CONTAINER="$(docker compose ps -q api | xargs docker inspect -f '{{.Name}}' | tr -d /)" docker run -d --name web-upstream --network parenthese_default -p 8081:80 \ -e API_UPSTREAM="$API_CONTAINER:4000" "$WEB_IMAGE" sleep 3 docker exec web-upstream grep -q "http://$API_CONTAINER:4000" /etc/nginx/conf.d/default.conf bash tools/docker-smoke.sh http://localhost:8081 - name: État et journaux en cas d'échec if: failure() run: | docker compose ps -a docker compose logs --no-color --timestamps docker logs web-upstream || true - name: Arrêt et suppression des volumes if: always() run: | docker rm -f web-upstream || true docker compose down -v --remove-orphans docker-aio: name: Docker tout-en-un (Unraid, NAS) runs-on: ubuntu-latest timeout-minutes: 30 env: SMOKE_WAIT_SECONDS: 180 steps: - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0 with: persist-credentials: false - name: Construction run: DOCKER_BUILDKIT=1 docker build -f Dockerfile.aio -t parenthese-aio:test . - name: Démarrage, sans aucune variable run: docker run -d --name aio -p 8080:80 -v aio-data:/data parenthese-aio:test - name: Test de fumée par le port web run: bash tools/docker-smoke.sh http://localhost:8080 # Les secrets et la base de /data doivent servir tels quels au démarrage suivant (mise à jour, reboot). - name: Redémarrage puis nouveau test run: | set -euo pipefail docker restart aio bash tools/docker-smoke.sh http://localhost:8080 docker logs aio 2>&1 | grep -c "première installation" | grep -qx 1 # Base à jour au redémarrage : pas de sauvegarde avant migration, mais pg_dump marche dans l'image [ -z "$(docker exec aio ls -A /data/backups 2>/dev/null)" ] [ "$(docker exec aio runuser -u postgres -- pg_dump -Fc parenthese | wc -c)" -gt 1000 ] - name: Journaux en cas d'échec if: failure() run: | docker ps -a docker logs --timestamps aio || true docker run --rm -v aio-data:/data busybox cat /data/postgres/postgres.log || true - name: Arrêt et suppression du volume if: always() run: | docker rm -f aio || true docker volume rm aio-data || true