name: CI on: push: branches: [main] tags: ['v*'] pull_request: # Least privilege by default. permissions: contents: read # A newer push to the same ref cancels the in-flight run. concurrency: group: ci-${{ github.ref }} cancel-in-progress: true jobs: frontend: runs-on: ubuntu-latest timeout-minutes: 15 steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 24 cache: npm - run: npm ci --prefer-offline --no-audit --no-fund - run: npm run lint - run: npm run format:check - run: npm run check:tokens - run: npm test # --localize builds every configured locale (nl + en, angular.json's i18n # block) in one pass; i18nMissingTranslation:"error" (angular.json) fails # this step if messages.en.xlf is missing a unit the source (WP-20) gains. - run: npx ng build --localize # The shipped bundle must stay clean; dev-only advisories are excluded. - run: npm audit --omit=dev storybook-a11y: # Axe runs against every story in the static build; a violation fails the build. runs-on: ubuntu-latest timeout-minutes: 15 steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 24 cache: npm - run: npm ci --prefer-offline --no-audit --no-fund # Cache the chromium download across runs; `install --with-deps` then only # runs the (fast, idempotent) apt deps check on a hit. - uses: actions/cache@v4 with: path: ~/.cache/ms-playwright key: playwright-${{ runner.os }}-${{ hashFiles('package-lock.json') }} - run: npx playwright install --with-deps chromium - run: npm run build-storybook - run: npm run test-storybook:ci backend: runs-on: ubuntu-latest timeout-minutes: 15 steps: - uses: actions/checkout@v4 - uses: actions/setup-dotnet@v4 with: dotnet-version: 10.0.x - uses: actions/cache@v4 with: path: ~/.nuget/packages key: nuget-${{ runner.os }}-${{ hashFiles('**/*.csproj') }} restore-keys: nuget-${{ runner.os }}- - run: dotnet format backend/BigRegister.slnx --verify-no-changes - run: dotnet test backend/BigRegister.slnx e2e: # Smoke-level Playwright run against the REAL FE+backend (WP-19) — a fresh # runner checkout per run, so there's no bigregister.db (WP-22, gitignored) # left over from a prior run to leak state in; the backend creates + migrates # an empty one on this boot, same as a fresh clone always has. # Playwright's `webServer` (playwright.config.ts) starts BOTH the backend and # `ng serve`, waits for them, runs the suite, and tears them down — all in the # one `npm run e2e` process. Do NOT background them as separate steps: a `&` # process from one Actions step is dead by the next step, so `wait-on` hung # forever (the 2-hour e2e hang). runs-on: ubuntu-latest timeout-minutes: 15 steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 24 cache: npm - uses: actions/setup-dotnet@v4 with: dotnet-version: 10.0.x - uses: actions/cache@v4 with: path: ~/.nuget/packages key: nuget-${{ runner.os }}-${{ hashFiles('**/*.csproj') }} restore-keys: nuget-${{ runner.os }}- - run: npm ci --prefer-offline --no-audit --no-fund - uses: actions/cache@v4 with: path: ~/.cache/ms-playwright key: playwright-${{ runner.os }}-${{ hashFiles('package-lock.json') }} - run: npx playwright install --with-deps chromium - run: npm run e2e semgrep: # SAST for both sides — replaces CodeQL, which is GitHub-only (its analyze step uploads # SARIF to GitHub's code-scanning API) and can't run on this Gitea instance. Semgrep OSS # is a plain CLI: no account, no external platform API. Findings print in the job log. # Installed via the runner's preinstalled python3/pip — NOT `setup-python` (its Python # download failed on this runner) and NOT a job `container:` (this act_runner times out # pulling the base runner image for container jobs). `--break-system-packages` survives # PEP-668; pip drops `semgrep` on PATH. `--ignore-installed` is required because some of # semgrep's deps (e.g. PyJWT) are already present as apt-managed packages, which pip cannot # uninstall ("RECORD file not found") — this flag installs fresh without uninstalling, so it # never touches the Debian copies. Don't drop it. # ponytail: report-only for now (no `--error`, so the job stays green while the initial # findings are triaged); flip to `--error` to make it a blocking gate. See WP-30. runs-on: ubuntu-latest timeout-minutes: 15 steps: - uses: actions/checkout@v4 - run: python3 -m pip install --break-system-packages --ignore-installed semgrep # p/default = curated cross-language security (covers JS/TS); p/csharp = the backend. # Anonymous registry fetch; --metrics=off disables telemetry (not `auto`, which uploads # project metadata). - run: semgrep scan --config p/default --config p/csharp --metrics=off api-client-drift: # The committed typed client must match the backend OpenAPI doc. runs-on: ubuntu-latest timeout-minutes: 15 steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 24 cache: npm - uses: actions/setup-dotnet@v4 with: # 8.0 for the bundled NSwag runtime, 10.0 to build/emit the spec. dotnet-version: | 8.0.x 10.0.x - uses: actions/cache@v4 with: path: ~/.nuget/packages key: nuget-${{ runner.os }}-${{ hashFiles('**/*.csproj') }} restore-keys: nuget-${{ runner.os }}- - run: npm ci --prefer-offline --no-audit --no-fund - run: npm run gen:api - run: git diff --exit-code src/app/shared/infrastructure/api-client.ts backend/swagger.json