# 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 l’image 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 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 - name: Démarrer MinIO (l'image officielle exige une commande — pas un service) run: | docker run -d --name minio -p 9000:9000 \ -e MINIO_ROOT_USER=siop -e MINIO_ROOT_PASSWORD=siop-minio \ minio/minio:RELEASE.2025-09-07T16-13-09Z server /data - 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 mobile: name: mobile (typecheck + tests jest-expo) 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/mobile typecheck - run: pnpm --filter @siop/mobile test -- --ci 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 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 - name: Démarrer MinIO (l'image officielle exige une commande — pas un service) run: | docker run -d --name minio -p 9000:9000 \ -e MINIO_ROOT_USER=siop -e MINIO_ROOT_PASSWORD=siop-minio \ minio/minio:RELEASE.2025-09-07T16-13-09Z server /data - 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, mobile, 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."