Skip to main content
Advertisement

18.3 CI/CD Pipeline — GitHub Actions and Type Check Automation

TypeScript CI/CD Core Checklist

CI Stages:
1. Type check (tsc --noEmit)
2. Lint (ESLint)
3. Unit tests (Vitest/Jest)
4. Build validation
5. E2E tests (optional)

CD Stages:
1. Docker image build
2. Push to container registry
3. DB migrations
4. Zero-downtime deployment

Complete GitHub Actions Workflow

# .github/workflows/ci.yml
name: CI

on:
push:
branches: [main, develop]
pull_request:
branches: [main]

env:
NODE_VERSION: '20'
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}

jobs:
# 1. Code quality checks
quality:
name: Type Check & Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: TypeScript type check
run: npx tsc --noEmit

- name: ESLint
run: npm run lint

- name: Prettier check
run: npm run format:check

# 2. Tests
test:
name: Unit Tests
runs-on: ubuntu-latest
needs: quality
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'

- run: npm ci

- name: Run tests with coverage
run: npm run test:coverage

- name: Upload coverage report
uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}

# 3. Build validation
build:
name: Build Validation
runs-on: ubuntu-latest
needs: test
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'

- run: npm ci
- run: npm run build

- name: Verify build output
run: |
test -d dist || exit 1
test -f dist/main.js || exit 1

# 4. E2E tests (main branch only)
e2e:
name: E2E Tests
runs-on: ubuntu-latest
needs: build
if: github.ref == 'refs/heads/main'
services:
postgres:
image: postgres:16-alpine
env:
POSTGRES_DB: testdb
POSTGRES_USER: test
POSTGRES_PASSWORD: test
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'

- run: npm ci
- run: npx playwright install --with-deps chromium

- name: Run migrations
env:
DATABASE_URL: postgresql://test:test@localhost:5432/testdb
run: npx prisma migrate deploy

- name: Run E2E tests
env:
DATABASE_URL: postgresql://test:test@localhost:5432/testdb
JWT_SECRET: test-secret-that-is-at-least-32-characters
run: npx playwright test

- uses: actions/upload-artifact@v3
if: failure()
with:
name: playwright-report
path: playwright-report/

package.json Scripts

{
"scripts": {
"build": "tsc -p tsconfig.build.json",
"typecheck": "tsc --noEmit",
"lint": "eslint 'src/**/*.ts' --ext .ts",
"lint:fix": "eslint 'src/**/*.ts' --ext .ts --fix",
"format": "prettier --write 'src/**/*.ts'",
"format:check": "prettier --check 'src/**/*.ts'",
"test": "vitest",
"test:run": "vitest run",
"test:coverage": "vitest run --coverage",
"test:ci": "vitest run --reporter=junit --outputFile=test-results.xml",
"test:e2e": "playwright test",
"migrate": "prisma migrate deploy",
"migrate:dev": "prisma migrate dev",
"db:seed": "prisma db seed"
}
}

ESLint + TypeScript Configuration

// .eslintrc.json
{
"root": true,
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": "./tsconfig.json",
"tsconfigRootDir": "."
},
"plugins": ["@typescript-eslint"],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/recommended-requiring-type-checking"
],
"rules": {
"@typescript-eslint/no-explicit-any": "warn",
"@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/no-floating-promises": "error",
"@typescript-eslint/await-thenable": "error",
"@typescript-eslint/no-misused-promises": "error"
}
}

Cache Optimization

# npm cache + build artifact cache
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'

# TypeScript incremental build cache
- name: Cache TypeScript build
uses: actions/cache@v3
with:
path: |
.tsbuildinfo
dist
key: ts-build-${{ runner.os }}-${{ hashFiles('src/**/*.ts', 'tsconfig*.json') }}
restore-keys: |
ts-build-${{ runner.os }}-

Pro Tips

Auto Type Check Comment on PR

# .github/workflows/typecheck-comment.yml
- name: TypeScript check with annotations
run: npx tsc --noEmit 2>&1 | npx ts-to-markdown > ts-errors.md || true

- name: Comment on PR
if: failure()
uses: actions/github-script@v7
with:
script: |
const fs = require('fs')
const errors = fs.readFileSync('ts-errors.md', 'utf8')
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `## TypeScript Errors\n${errors}`,
})
Advertisement