Back to the blog
Toolingยท Jul 18, 2026ยท 7 min read

A production-grade GitHub Actions CI pipeline for TypeScript projects

Type-check, lint, test, and deploy in the right order with caching that actually works. The workflow file we use across every Appesto product, annotated.

#github-actions#ci-cd#typescript#tooling

GitHub Actions processes over 6 million workflow runs per day in 2026, and the January 2026 pricing reduction (up to 39% on hosted runners) makes it the clear default for most teams. The gap between a basic CI pipeline and a production-grade one isn't about which actions you use โ€” it's about job ordering, caching strategy, and security defaults.

Job order: fast feedback first

Put the fastest checks first so developers get feedback in under a minute for obvious failures. The order we use: lint (10s) then type-check (30s) then unit tests (60s) then build (2min) then E2E tests (5min, merge to main only). Jobs within a stage can be parallelised; stages run sequentially so a lint failure doesn't spend compute on a build.

name: CI
on:
  push:
    branches: [main]
  pull_request:
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true  # cancel redundant PR runs

jobs:
  lint-typecheck:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: '22', cache: 'npm' }
      - run: npm ci
      - run: npm run lint && npm run typecheck

  test:
    needs: lint-typecheck
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: '22', cache: 'npm' }
      - run: npm ci && npm test -- --coverage

  build:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: '22', cache: 'npm' }
      - run: npm ci && npm run build

Caching and OIDC auth

actions/setup-node with cache: 'npm' caches the npm cache directory keyed on package-lock.json โ€” a cache hit means npm ci runs but fetches packages from the cache instead of the network. For cloud deployments, use OIDC-based authentication instead of static service account keys: GitHub Actions assumes a cloud IAM role using a short-lived token, no secrets to rotate. Set id-token: write in your deployment job's permissions block when using OIDC.

Security defaults

  • Pin action versions to a full commit SHA (actions/checkout@abc1234), not a tag โ€” tags are mutable and compromised action tags are a real supply-chain threat.
  • The 2026 default repo permission is read-only โ€” explicitly request the permissions your jobs need, nothing more.
  • Run E2E tests against the deployed preview URL, not localhost โ€” catches environment variable and infrastructure misconfig that local tests miss.
  • Add branch protection rules requiring lint, typecheck, and test checks to pass before merge โ€” make it mechanically impossible to merge failing code.

Written by Appesto Engineering.