diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a0a7fd8..5b3d937 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,6 +39,10 @@ jobs: --health-retries 5 steps: - uses: actions/checkout@v4 + with: + # Full history so coverage:diff can resolve `origin/...HEAD` + # against the PR's base branch (ADR-020 L1). + fetch-depth: 0 - uses: pnpm/action-setup@v4 with: version: 9 @@ -58,12 +62,26 @@ jobs: DATABASE_URL: postgres://postgres:postgres@localhost:5432/cms_test PAYLOAD_SECRET: test-secret-do-not-use-in-prod run: pnpm test -- --coverage + # L2 — merge per-package lcovs to coverage/lcov.info + emit + # coverage/summary.json (ADR-020). Runs even on test failure so the + # artifact still captures what was produced. + - name: Coverage — aggregate (L2) + if: always() + run: pnpm coverage:aggregate + # L1 — cover-the-diff gate. Only meaningful on PRs (push-to-main has + # no base ref to diff against). Compares against the PR's base branch. + - name: Coverage — diff (L1) + if: github.event_name == 'pull_request' + run: pnpm coverage:diff -- --base origin/${{ github.base_ref }} - run: pnpm build - uses: actions/upload-artifact@v4 if: always() with: name: coverage - path: "**/coverage/lcov.info" + path: | + coverage/lcov.info + coverage/summary.json + **/coverage/lcov.info retention-days: 7 e2e: diff --git a/.github/workflows/coverage-snapshot.yml b/.github/workflows/coverage-snapshot.yml new file mode 100644 index 0000000..51924fc --- /dev/null +++ b/.github/workflows/coverage-snapshot.yml @@ -0,0 +1,72 @@ +# Coverage snapshot — commits coverage/summary.json back to main after each +# merge so the trend history accumulates in `git log -- coverage/summary.json` +# (ADR-020 L2). This is the only workflow that needs `contents: write`. +# +# Skipped if summary.json hasn't changed since the previous commit. + +name: Coverage snapshot + +on: + push: + branches: [main] + +# Avoid two snapshot runs racing each other on rapid-fire merges. +concurrency: + group: coverage-snapshot + cancel-in-progress: false + +permissions: + contents: write + +jobs: + snapshot: + runs-on: ubuntu-latest + services: + postgres: + image: postgres:16-alpine + env: + POSTGRES_PASSWORD: postgres + POSTGRES_USER: postgres + POSTGRES_DB: cms_test + ports: + - 5432:5432 + options: >- + --health-cmd "pg_isready -U postgres" + --health-interval 10s + --health-timeout 5s + --health-retries 5 + steps: + - uses: actions/checkout@v4 + with: + # Token with write scope so we can push the snapshot back. + token: ${{ secrets.GITHUB_TOKEN }} + - uses: pnpm/action-setup@v4 + with: + version: 9 + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: pnpm + - run: pnpm install --frozen-lockfile + - name: Test with coverage + env: + DATABASE_URL: postgres://postgres:postgres@localhost:5432/cms_test + PAYLOAD_SECRET: test-secret-do-not-use-in-prod + run: pnpm test -- --coverage + - name: Aggregate + run: pnpm coverage:aggregate + - name: Commit summary.json if changed + run: | + if git diff --quiet --exit-code coverage/summary.json; then + echo "No summary.json change; skipping commit." + exit 0 + fi + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add coverage/summary.json + git commit -m "chore(coverage): snapshot ${GITHUB_SHA::7} + + Auto-generated by .github/workflows/coverage-snapshot.yml. + + [skip ci]" + git push origin HEAD:main