name: CI on: push: branches: [main, master, develop, Dev] pull_request: permissions: contents: read concurrency: group: frontend-ci-${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: standards: name: Metadata and standards runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: actions/setup-node@v4 with: node-version: 24 - name: Verify required npm scripts run: | node <<'NODE' const { readFileSync } = require("node:fs"); const pkg = JSON.parse(readFileSync("package.json", "utf8")); const required = ["format:check", "lint", "build", "test", "test:e2e"]; const missing = required.filter((script) => !pkg.scripts?.[script]); if (missing.length > 0) { console.error(`Missing required scripts: ${missing.join(", ")}`); process.exit(1); } NODE - name: Guard changed lines run: | set -euo pipefail if [ "${{ github.event_name }}" = "pull_request" ]; then BASE_REF="${{ github.event.pull_request.base.sha }}" else BASE_REF="${{ github.event.before }}" fi if [ -z "${BASE_REF}" ] || [ "${BASE_REF}" = "0000000000000000000000000000000000000000" ]; then BASE_REF="$(git rev-parse HEAD~1 2>/dev/null || true)" fi if [ -z "${BASE_REF}" ]; then echo "No base ref available; skipping changed-line guard." exit 0 fi ADDED_LINES="$(git diff --unified=0 "${BASE_REF}" "${GITHUB_SHA}" | grep -E '^\+' | grep -vE '^\+\+\+' || true)" if printf '%s\n' "${ADDED_LINES}" | grep -E 'Generated with (Claude [C]ode|[C]odex|Chat[G]PT)|--no-[v]erify|HUSKY[=]0'; then echo "Found generated-tool footer or hook bypass wording in added lines." exit 1 fi if printf '%s\n' "${ADDED_LINES}" | grep -E 'A[K]IA[0-9A-Z]{16}|A[S]IA[0-9A-Z]{16}|AWS[_]SECRET[_]ACCESS[_]KEY|aws[_-]?secret[_-]?access[_-]?key|J[W]TAuthenticationHIGHsecuredPassword|P[a]ssword=|S[e]ndGrid.*A[p]iKey'; then echo "Found a likely secret in added lines. Move sensitive values to the environment or secret manager." exit 1 fi code-quality: name: Code quality runs-on: ubuntu-latest needs: standards steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 24 cache: npm - run: npm ci - run: npm run format:check - run: npm run lint build: name: Build runs-on: ubuntu-latest needs: standards steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 24 cache: npm - run: npm ci - run: npm run build unit-tests: name: Unit tests runs-on: ubuntu-latest needs: standards steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 24 cache: npm - run: npm ci - run: npm test e2e: name: Browser smoke runs-on: ubuntu-latest needs: standards steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 24 cache: npm - run: npm ci - run: npx playwright install --with-deps chromium - run: npm run test:e2e env: CI: true