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 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 buildCaching 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.