Files
siop2/.github/workflows/ci.yml
pr-daaif 2ffdd13cf4 feat(r3.2): bibliothèque de documents (FileStorage réel) + analytics
- FileStorage : putObject/getObjectStream/removeObject, bucket créé au
  démarrage — MinIO confiné à son implémentation (règle ESLint intacte)
- documents : upload multipart (PDF/JPG/PNG, 20 Mo max, rattachement
  appareil OU OT requis, permission d'édition sur la CIBLE), liste
  filtrable, téléchargement STREAMÉ par l'API (MinIO jamais exposé),
  suppression ; e2e : octets téléchargés identiques aux octets envoyés
- analytics : GET /analytics/summary dérivé du réel — coûts/mois
  (mouvements + main-d'œuvre figés), pannes par organe (bilans codés),
  taux de préventif, durée moyenne de résolution, top équipements
- générateur OpenAPI : query params, multipart, réponse binaire (71 ops)
- CI : service MinIO (bitnami) sur les jobs api et e2e
- test de régression du tri « Interventions récentes » rendu déterministe
  (positions absolues instables sous 12 suites parallèles) ; 58 tests,
  6 runs complets consécutifs verts

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 21:24:04 +01:00

199 lines
6.9 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# CI SIOP V2 (R0.12) — trois verrous :
# ci-contract : la règle d'or (Zod → OpenAPI → clients typés) — échoue au
# moindre diff entre le contrat committé et la génération ;
# api : migration + tests Jest (couverture ≥ 70 % bloquante, jest.config) ;
# web : typecheck + vitest + build de production.
# lint : ESLint monorepo (dont la règle ADR-001 « pas d'import MinIO
# hors FileStorage ») ;
# e2e : parcours démo Playwright (connexion 1 clic → coquille →
# bascule de rôle < 3 s → /design) contre l'API construite.
name: CI
on:
push:
branches: [main]
pull_request:
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs:
lint:
name: lint (ESLint monorepo)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v5
with:
node-version-file: .nvmrc
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm lint
contract:
name: ci-contract (règle d'or)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v5
with:
node-version-file: .nvmrc
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm --filter @siop/shared build
- name: Régénérer la spec et le client typé
run: |
pnpm --filter @siop/shared contract
pnpm --filter @siop/web generate:client
- name: Vérifier qu'aucun artefact ne dérive du contrat
run: |
if ! git diff --exit-code -- docs/openapi.json apps/web/src/api/schema.d.ts; then
echo "::error::Contrat désynchronisé — régénérez spec et clients dans le même commit (pnpm contract && pnpm --filter @siop/web generate:client)."
exit 1
fi
api:
name: api (tests + couverture ≥ 70 %)
runs-on: ubuntu-latest
services:
# R1+ : la migration r1_referentiel exige PostGIS (colonne générée geography).
# pgvector arrivera en R5 (bascule alors sur limage infra/postgres).
postgres:
image: postgis/postgis:18-3.6
env:
POSTGRES_USER: siop
POSTGRES_PASSWORD: siop
POSTGRES_DB: siop
ports: ['5432:5432']
options: >-
--health-cmd "pg_isready -U siop -d siop"
--health-interval 5s --health-timeout 3s --health-retries 10
redis:
image: redis:7.4-alpine
ports: ['6379:6379']
options: >-
--health-cmd "redis-cli ping"
--health-interval 5s --health-timeout 3s --health-retries 10
minio:
image: bitnami/minio:2025
env:
MINIO_ROOT_USER: siop
MINIO_ROOT_PASSWORD: siop-minio
ports: ['9000:9000']
env:
DATABASE_URL: postgresql://siop:siop@localhost:5432/siop
REDIS_URL: redis://localhost:6379
JWT_SECRET: ci-only-secret-0123456789abcdef
MINIO_ENDPOINT: localhost
MINIO_ACCESS_KEY: siop
MINIO_SECRET_KEY: siop-minio
steps:
- uses: actions/checkout@v5
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v5
with:
node-version-file: .nvmrc
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm --filter @siop/shared build
- run: pnpm --filter @siop/api prisma:generate
- run: pnpm --filter @siop/api exec prisma migrate deploy
- run: pnpm --filter @siop/api typecheck
- run: pnpm --filter @siop/api test:cov
web:
name: web (typecheck + tests + build)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v5
with:
node-version-file: .nvmrc
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm --filter @siop/shared build
- run: pnpm --filter @siop/web test
- run: pnpm --filter @siop/web build
e2e:
name: e2e (parcours démo Playwright)
runs-on: ubuntu-latest
services:
postgres:
image: postgis/postgis:18-3.6
env:
POSTGRES_USER: siop
POSTGRES_PASSWORD: siop
POSTGRES_DB: siop
ports: ['5432:5432']
options: >-
--health-cmd "pg_isready -U siop -d siop"
--health-interval 5s --health-timeout 3s --health-retries 10
redis:
image: redis:7.4-alpine
ports: ['6379:6379']
options: >-
--health-cmd "redis-cli ping"
--health-interval 5s --health-timeout 3s --health-retries 10
minio:
image: bitnami/minio:2025
env:
MINIO_ROOT_USER: siop
MINIO_ROOT_PASSWORD: siop-minio
ports: ['9000:9000']
env:
DATABASE_URL: postgresql://siop:siop@localhost:5432/siop
REDIS_URL: redis://localhost:6379
JWT_SECRET: ci-only-secret-0123456789abcdef
MINIO_ENDPOINT: localhost
MINIO_ACCESS_KEY: siop
MINIO_SECRET_KEY: siop-minio
steps:
- uses: actions/checkout@v5
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v5
with:
node-version-file: .nvmrc
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm --filter @siop/shared build
- run: pnpm --filter @siop/api prisma:generate
- run: pnpm --filter @siop/api exec prisma migrate deploy
- run: pnpm --filter @siop/api build
- run: pnpm --filter @siop/web exec playwright install --with-deps chromium
- run: pnpm --filter @siop/web e2e
- name: Traces Playwright en cas d'échec
if: failure()
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: apps/web/playwright-report/
retention-days: 7
# Déploiement continu (runbook §4) : Dokploy reconstruit et redéploie le
# compose UNIQUEMENT quand les 5 verrous sont verts sur main. Le webhook
# vit dans le secret DOKPLOY_WEBHOOK_URL (Dokploy → service compose →
# Deployments → Webhook URL) ; tant qu'il n'est pas configuré, le job
# se termine en « skip » sans faire échouer le pipeline.
deploy:
name: deploy (Dokploy — siop2.apps.enset.top)
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
needs: [lint, contract, api, web, e2e]
runs-on: ubuntu-latest
environment: production
steps:
- name: Déclencher le déploiement Dokploy
env:
WEBHOOK_URL: ${{ secrets.DOKPLOY_WEBHOOK_URL }}
run: |
if [ -z "$WEBHOOK_URL" ]; then
echo "::notice::DOKPLOY_WEBHOOK_URL absent — déploiement sauté (configurer le secret pour activer le CD)."
exit 0
fi
curl -fsS -X POST "$WEBHOOK_URL"
echo "Déploiement déclenché — suivi : Dokploy → Deployments."