Challenge 2: Build a CI Pipeline — Possible Solution ==================================================================== # .github/workflows/ci.yml name: CI on: pull_request: branches: [main] jobs: verify: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Node.js uses: actions/setup-node@v4 with: node-version: 20 cache: npm - name: Install dependencies run: npm ci - name: Type check run: npm run typecheck # package.json: "typecheck": "tsc --noEmit" - name: Run tests run: npm test -- --ci --coverage # package.json: "test": "jest" - name: Build run: npm run build # package.json: "build": "tsc" # Only runs if typecheck AND test both succeeded — each step in a # GitHub Actions job runs sequentially and the job stops at the # first failing step by default. WHY THIS WORKS -------------- - Steps run in order and the job stops at the first failure — a type error in "Type check" means "Run tests" and "Build" never even execute, giving the fastest possible failure signal (type errors are much cheaper to detect than running the full test suite). - `npm ci` (not `npm install`) is used deliberately: it installs exactly what's in package-lock.json and fails if the lockfile is out of sync, which is what you want in CI — a reproducible, locked dependency set. - `cache: npm` in setup-node caches node_modules based on the lockfile hash, so repeated CI runs on the same dependencies are much faster. - This workflow triggers on `pull_request`, meaning a pull request cannot be merged (if branch protection requires this check) until type checking, tests, and the build all pass — exactly the "merge gate" behavior described in the chapter.