name: CI on: push: branches: [main, master, "codex/**"] tags: ["v*"] pull_request: branches: [main, master] workflow_dispatch: # Reuse the Playwright browsers that ship with the host runner (snapped to # the root HOME cache instead of a fresh per-job HOME) so `playwright install` # is a near-instant no-op instead of a ~100s CDN download on every run. env: PLAYWRIGHT_BROWSERS_PATH: /opt/ms-playwright jobs: # ───────────────────────────────────────────── # Fast quality gate: toolchain, install, dependabot audit, # lint, i18n contracts & typecheck. No heavy test suites here. # Draait op de host (self-hosted) waar Node 26 + pnpm 11 # geïnstalleerd zijn en internet beschikbaar is. # ───────────────────────────────────────────── check: runs-on: self-hosted steps: - name: Checkout uses: actions/checkout@v4 with: repository: ${{ gitea.repository }} token: ${{ gitea.token }} - name: Toolchain check run: node scripts/check-node-toolchain.mjs - name: Install dependencies run: pnpm install --frozen-lockfile - name: Dependency security audit run: pnpm deps:audit - name: Lint run: pnpm biome:lint - name: CMS translation contracts run: pnpm i18n:check - name: Typecheck run: pnpm typecheck # ───────────────────────────────────────────── # Test suites. Parallel jobs (host runner capacity >= 3) so unit, # integration and UI tests each get a worker instead of running # back-to-back inside the check job (~2min wall-time saving). # ───────────────────────────────────────────── tests-unit: needs: check runs-on: self-hosted steps: - name: Checkout uses: actions/checkout@v4 with: repository: ${{ gitea.repository }} token: ${{ gitea.token }} - name: Install dependencies run: pnpm install --frozen-lockfile - name: Unit & coverage tests env: SKIP_ENV_VALIDATION: 1 NODE_ENV: test DATABASE_URL: "mysql://test:test@localhost:3306/test?charset=utf8mb4" REDIS_URL: "redis://127.0.0.1:6379?connect_timeout=2" AUTH_SECRET: "ci-test-secret-key-that-is-long-enough" BCRYPT_ROUNDS: 4 run: | if [ -x /usr/bin/time ]; then /usr/bin/time -f 'Tests: %e seconds; peak process RSS: %M KiB' pnpm test:coverage --maxWorkers=4 else time pnpm test:coverage --maxWorkers=4 fi tests-integration: needs: check runs-on: self-hosted steps: - name: Checkout uses: actions/checkout@v4 with: repository: ${{ gitea.repository }} token: ${{ gitea.token }} - name: Install dependencies run: pnpm install --frozen-lockfile - name: MariaDB and Redis integration tests run: pnpm test:integration tests-ui: needs: check runs-on: self-hosted steps: - name: Checkout uses: actions/checkout@v4 with: repository: ${{ gitea.repository }} token: ${{ gitea.token }} - name: Install dependencies run: pnpm install --frozen-lockfile # Compare against reviewed Linux references; updates are explicit. - name: Install UI test browser run: pnpm exec playwright install chromium - name: Accessibility and UI regression checks run: pnpm test:ui - name: Upload UI results if: always() uses: https://gitea.com/actions/gitea-upload-artifact@62ac910c5d3dfa85c7cb2df15afe2e342b2407c2 with: name: ui-results path: | e2e/ui/__screenshots__/linux/ playwright-report/ui/ test-results/ui/ retention-days: 14 # Validate branch/PR Docker images before integration into a deployment branch. preflight: needs: [tests-unit, tests-integration, tests-ui] if: gitea.event_name == 'pull_request' || (gitea.event_name == 'push' && startsWith(gitea.ref, 'refs/heads/codex/')) runs-on: self-hosted steps: - name: Checkout uses: actions/checkout@v4 with: repository: ${{ gitea.repository }} token: ${{ gitea.token }} - name: Toolchain check run: node scripts/check-node-toolchain.mjs - name: Build and verify isolated candidate shell: bash run: bash scripts/ci-preflight.sh - name: Upload preflight news browser results if: always() uses: https://gitea.com/actions/gitea-upload-artifact@62ac910c5d3dfa85c7cb2df15afe2e342b2407c2 with: name: preflight-news-browser-results path: | test-results/news-real/ playwright-report/news-real/ if-no-files-found: warn retention-days: 14 # ───────────────────────────────────────────── # Docker build & deploy # Draait op de host (self-hosted) zodat Docker # toegang heeft tot de daemon en volumes. # ───────────────────────────────────────────── deploy: needs: [tests-unit, tests-integration, tests-ui] if: gitea.event_name == 'push' && (gitea.ref_name == 'main' || gitea.ref_name == 'master') runs-on: self-hosted steps: - name: Checkout uses: actions/checkout@v4 with: repository: ${{ gitea.repository }} token: ${{ gitea.token }} - name: Build, deploy and smoke test shell: bash env: DEPLOY_BRANCH: ${{ gitea.ref_name }} run: bash scripts/ci-deploy.sh - name: Upload isolated news browser results if: always() uses: https://gitea.com/actions/gitea-upload-artifact@62ac910c5d3dfa85c7cb2df15afe2e342b2407c2 with: name: news-browser-results path: | test-results/news-real/ playwright-report/news-real/ if-no-files-found: warn retention-days: 14 - name: Upload JavaScript size report if: always() uses: https://gitea.com/actions/gitea-upload-artifact@62ac910c5d3dfa85c7cb2df15afe2e342b2407c2 with: name: javascript-size-report path: build-reports/ if-no-files-found: warn retention-days: 14