Skip to content

fix: Close the five Codex findings on PR #437, one of which was mine … #1403

fix: Close the five Codex findings on PR #437, one of which was mine …

fix: Close the five Codex findings on PR #437, one of which was mine … #1403

Workflow file for this run

name: CI
on:
push:
branches: [ main, master, develop, 'claude/**' ]
pull_request:
branches: [ main, master, develop ]
# Least privilege (audit H47). Every job in this workflow reads the repository,
# runs `npm ci` and uploads artifacts; none of it writes anything back to
# GitHub. Declaring that here is what stops the workflow inheriting the
# repository's *default* `GITHUB_TOKEN` — a setting that lives in repository
# administration and is therefore invisible in the pull request that would
# widen it. It matters most on this workflow and on `e2e.yml` precisely because
# they are the two that run `npm ci`, i.e. the two that execute dependency
# lifecycle scripts, in a repository that deploys to production from its own
# workflows.
permissions:
contents: read
jobs:
lint-and-test:
name: Lint, Type-Check & Test
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [22.x, 26.x]
steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run ESLint
run: npm run lint
continue-on-error: false
- name: Run TypeScript type-check
run: npm run type-check
continue-on-error: false
- name: Run tests
run: npm test
continue-on-error: false
- name: Run tests with coverage
run: npm run test:coverage
continue-on-error: false
- name: Upload coverage to artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: matrix.node-version == '22.x'
with:
name: coverage-report
path: coverage/
retention-days: 30
build:
name: Build Production
runs-on: ubuntu-latest
needs: lint-and-test
steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: '22.x'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build application
run: npm run build
- name: Upload build artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: dist
path: dist/
retention-days: 7
coverage-scope:
name: Coverage (whole codebase)
runs-on: ubuntu-latest
# Runs in parallel with the matrix rather than as an extra step inside it,
# so the honest repo-wide figure costs no wall-clock time on the critical
# path and is measured once instead of once per Node version.
steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: '22.x'
cache: 'npm'
- name: Install dependencies
run: npm ci
# `npm run test:coverage` gates the layer unit tests own (~45% of
# production statements). This one measures everything and enforces a
# floor, so the percentage the project quotes cannot drift back into
# describing a subset while reading like a whole.
- name: Measure coverage across the whole codebase
run: npm run test:coverage:all
security-audit:
name: Security Audit
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: '22.x'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run npm audit (all dependencies)
run: npm audit --audit-level=moderate
continue-on-error: true
- name: Check for high/critical vulnerabilities (production only)
run: npm audit --omit=dev --audit-level=high
# Stable aggregate gate. Branch protection should require THIS single check
# ("CI Success") instead of the individual matrix legs. Because it depends on
# every other CI job and re-derives its result from theirs, the Node matrix
# (and the rest of this workflow) can change freely in this file without ever
# touching the required-status-checks list in repo settings. `if: always()`
# guarantees the job always reports a status, and it fails unless every
# dependency succeeded (a failed/cancelled/skipped leg is not a success).
ci-success:
name: CI Success
runs-on: ubuntu-latest
if: always()
needs: [lint-and-test, build, security-audit, coverage-scope]
steps:
- name: Verify all CI jobs succeeded
env:
LINT_AND_TEST_RESULT: ${{ needs.lint-and-test.result }}
BUILD_RESULT: ${{ needs.build.result }}
SECURITY_AUDIT_RESULT: ${{ needs.security-audit.result }}
COVERAGE_SCOPE_RESULT: ${{ needs.coverage-scope.result }}
run: |
echo "Lint, Type-Check & Test: $LINT_AND_TEST_RESULT"
echo "Build Production: $BUILD_RESULT"
echo "Security Audit: $SECURITY_AUDIT_RESULT"
echo "Coverage (whole codebase): $COVERAGE_SCOPE_RESULT"
if [ "$LINT_AND_TEST_RESULT" != "success" ] \
|| [ "$BUILD_RESULT" != "success" ] \
|| [ "$SECURITY_AUDIT_RESULT" != "success" ] \
|| [ "$COVERAGE_SCOPE_RESULT" != "success" ]; then
echo "::error::One or more CI jobs did not succeed — failing the CI Success gate."
exit 1
fi
echo "All CI jobs succeeded."