name: CI on: push: branches: [master] pull_request: branches: [master] concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: # ── Detect which apps changed ────────────────────────────────────── changes: name: Detect changes runs-on: ubuntu-latest outputs: api: ${{ steps.filter.outputs.api }} scanner: ${{ steps.filter.outputs.scanner }} banner: ${{ steps.filter.outputs.banner }} admin-ui: ${{ steps.filter.outputs.admin-ui }} steps: - uses: actions/checkout@v4 - uses: dorny/paths-filter@v3 id: filter with: filters: | api: - 'apps/api/**' scanner: - 'apps/scanner/**' banner: - 'apps/banner/**' admin-ui: - 'apps/admin-ui/**' # ── API ──────────────────────────────────────────────────────────── api-lint: name: API Lint needs: changes if: needs.changes.outputs.api == 'true' runs-on: ubuntu-latest defaults: run: working-directory: apps/api steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: "3.12" cache: pip - run: pip install ruff - run: ruff check src/ tests/ - run: ruff format --check src/ tests/ api-test: name: API Tests needs: changes if: needs.changes.outputs.api == 'true' runs-on: ubuntu-latest defaults: run: working-directory: apps/api services: postgres: image: postgres:16-alpine env: POSTGRES_USER: consentos_test POSTGRES_PASSWORD: consentos_test POSTGRES_DB: consentos_test ports: - 5432:5432 options: >- --health-cmd pg_isready --health-interval 5s --health-timeout 5s --health-retries 5 redis: image: redis:7-alpine ports: - 6379:6379 options: >- --health-cmd "redis-cli ping" --health-interval 5s --health-timeout 5s --health-retries 5 env: DATABASE_URL: postgresql+asyncpg://consentos_test:consentos_test@localhost:5432/consentos_test REDIS_URL: redis://localhost:6379/0 JWT_SECRET_KEY: ci-test-secret-key-not-for-production ENVIRONMENT: test steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: "3.12" cache: pip - run: pip install -e ".[dev]" - name: Run migrations run: alembic upgrade head env: DATABASE_URL: postgresql://consentos_test:consentos_test@localhost:5432/consentos_test - run: pytest --cov=src --cov-report=term-missing -v # ── Scanner ──────────────────────────────────────────────────────── scanner-lint: name: Scanner Lint needs: changes if: needs.changes.outputs.scanner == 'true' runs-on: ubuntu-latest defaults: run: working-directory: apps/scanner steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: "3.12" cache: pip - run: pip install ruff - run: ruff check src/ tests/ - run: ruff format --check src/ tests/ scanner-test: name: Scanner Tests needs: changes if: needs.changes.outputs.scanner == 'true' runs-on: ubuntu-latest defaults: run: working-directory: apps/scanner steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: "3.12" cache: pip - run: pip install -e ".[dev]" - run: pytest --cov=src --cov-report=term-missing -v # ── Banner ───────────────────────────────────────────────────────── banner-lint: name: Banner Lint & Typecheck needs: changes if: needs.changes.outputs.banner == 'true' runs-on: ubuntu-latest defaults: run: working-directory: apps/banner steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: "20" cache: npm cache-dependency-path: apps/banner/package-lock.json - run: npm ci - run: npx tsc --noEmit banner-test: name: Banner Tests needs: changes if: needs.changes.outputs.banner == 'true' runs-on: ubuntu-latest defaults: run: working-directory: apps/banner steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: "20" cache: npm cache-dependency-path: apps/banner/package-lock.json - run: npm ci - run: npx vitest run --reporter=verbose banner-build: name: Banner Build needs: [banner-test, banner-lint] if: needs.changes.outputs.banner == 'true' runs-on: ubuntu-latest defaults: run: working-directory: apps/banner steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: "20" cache: npm cache-dependency-path: apps/banner/package-lock.json - run: npm ci - run: npm run build - name: Check bundle sizes run: | echo "=== Bundle sizes ===" ls -lh dist/consent-loader.js ls -lh dist/consent-bundle.js LOADER_SIZE=$(stat -c%s dist/consent-loader.js) if [ "$LOADER_SIZE" -gt 20480 ]; then echo "::warning::consent-loader.js is ${LOADER_SIZE} bytes (>20KB) — consider optimising" fi # ── Admin UI ─────────────────────────────────────────────────────── admin-ui-lint: name: Admin UI Typecheck needs: changes if: needs.changes.outputs.admin-ui == 'true' runs-on: ubuntu-latest defaults: run: working-directory: apps/admin-ui steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: "20" cache: npm cache-dependency-path: apps/admin-ui/package-lock.json - run: npm ci - run: npx tsc -b admin-ui-test: name: Admin UI Tests needs: changes if: needs.changes.outputs.admin-ui == 'true' runs-on: ubuntu-latest defaults: run: working-directory: apps/admin-ui steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: "20" cache: npm cache-dependency-path: apps/admin-ui/package-lock.json - run: npm ci - run: npx vitest run --reporter=verbose admin-ui-build: name: Admin UI Build needs: [admin-ui-test, admin-ui-lint] if: needs.changes.outputs.admin-ui == 'true' runs-on: ubuntu-latest defaults: run: working-directory: apps/admin-ui steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: "20" cache: npm cache-dependency-path: apps/admin-ui/package-lock.json - run: npm ci - run: npx vite build