name: CI # Runs on pull requests and on the branches they merge into, so a green tick means the code was # checked in the state it will actually land in. `develop` is where work integrates and `main` is # what it is promoted to; a merge into either is a state nothing else would otherwise check, # since a pull request is checked before the merge rather than after it. on: push: branches: [main, develop] pull_request: # A second push to the same branch makes the first run's answer irrelevant. concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true permissions: contents: read jobs: checks: name: Typecheck, unit tests, build runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - uses: actions/setup-node@v5 with: node-version: 22 cache: npm # `npm ci` rather than `npm install`: it installs exactly what package-lock.json pins and # fails if the lockfile has drifted from package.json, so CI cannot silently test a # different dependency tree than the one committed. - run: npm ci # Four TypeScript projects, checked by four different things. These two have no build of # their own, so nothing else would ever compile them. - name: Typecheck the ETL run: npm run etl:typecheck - name: Typecheck the end-to-end tests run: npm run e2e:typecheck # `tsconfig.spec.json` is compiled here, `tsconfig.app.json` by the build below. - name: Unit tests run: npm test -- --no-watch - name: Production build run: npm run build e2e: name: End-to-end runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - uses: actions/setup-node@v5 with: node-version: 22 cache: npm - run: npm ci # `--with-deps` installs the system libraries headless Chromium needs, which a bare runner # does not have. Only chromium: playwright.config.ts defines no other project. - name: Install Playwright Chromium run: npx playwright install --with-deps chromium # Playwright starts the dev server itself (see `webServer` in playwright.config.ts). # GitHub sets CI=true, which turns on `forbidOnly` and the two retries. - name: End-to-end tests run: npm run e2e # The HTML reporter's output is the only way to see why a headless browser failed. Only # kept when something did fail — on a green run it is several megabytes saying so. - name: Upload Playwright report if: failure() uses: actions/upload-artifact@v4 with: name: playwright-report path: playwright-report/ retention-days: 7