yaml DevOps February 1, 2026

CI/CD Quality Gate Config

A GitHub Actions workflow that enforces code quality gates — linting, type checking, tests, coverage thresholds, and build verification before merging.

ci-cdgithub-actionsquality-gatetestingautomation

Description

A comprehensive GitHub Actions CI pipeline that acts as a quality gate for pull requests. It runs lint, type checks, unit tests with coverage, and build verification in parallel — blocking merges that don’t meet quality standards.

Features

  • Parallel jobs — lint, typecheck, test, and build run simultaneously
  • Coverage threshold — fails if coverage drops below 80%
  • Dependency caching — npm cache for fast installs
  • PR comments — posts coverage report as a PR comment
  • Branch protection — designed to be a required status check

The Workflow

# .github/workflows/quality-gate.yml
name: Quality Gate

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

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

env:
  NODE_VERSION: "20"

jobs:
  # ── Lint ──
  lint:
    name: 🔍 Lint
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

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

      - run: npm ci
      - run: npm run lint -- --max-warnings=0

  # ── Type Check ──
  typecheck:
    name: 🔷 Type Check
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

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

      - run: npm ci
      - run: npx tsc --noEmit

  # ── Tests + Coverage ──
  test:
    name: 🧪 Test
    runs-on: ubuntu-latest
    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 --coverageReporters=json-summary --coverageReporters=text

      - name: Check coverage threshold
        run: |
          COVERAGE=$(node -e "
            const report = require('./coverage/coverage-summary.json');
            const { lines, branches, functions, statements } = report.total;
            const avg = (lines.pct + branches.pct + functions.pct + statements.pct) / 4;
            console.log(Math.round(avg * 100) / 100);
          ")
          echo "Coverage: ${COVERAGE}%"
          if (( $(echo "$COVERAGE < 80" | bc -l) )); then
            echo "❌ Coverage ${COVERAGE}% is below 80% threshold"
            exit 1
          fi
          echo "✅ Coverage ${COVERAGE}% meets threshold"

      - name: Comment coverage on PR
        if: github.event_name == 'pull_request'
        uses: actions/github-script@v7
        with:
          script: |
            const fs = require('fs');
            const report = JSON.parse(fs.readFileSync('coverage/coverage-summary.json', 'utf8'));
            const { lines, branches, functions, statements } = report.total;

            const body = `## 📊 Coverage Report

            | Metric | Coverage |
            |--------|----------|
            | Lines | ${lines.pct}% |
            | Branches | ${branches.pct}% |
            | Functions | ${functions.pct}% |
            | Statements | ${statements.pct}% |

            ${lines.pct >= 80 ? '✅' : '❌'} Threshold: 80%`;

            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body,
            });

  # ── Build ──
  build:
    name: 📦 Build
    runs-on: ubuntu-latest
    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: Check build output
        run: |
          if [ ! -d "dist" ]; then
            echo "❌ Build output directory 'dist' not found"
            exit 1
          fi
          echo "✅ Build successful — $(find dist -type f | wc -l) files generated"

  # ── Gate ──
  quality-gate:
    name: ✅ Quality Gate
    needs: [lint, typecheck, test, build]
    runs-on: ubuntu-latest
    if: always()
    steps:
      - name: Check all jobs
        run: |
          if [ "${{ needs.lint.result }}" != "success" ] || \
             [ "${{ needs.typecheck.result }}" != "success" ] || \
             [ "${{ needs.test.result }}" != "success" ] || \
             [ "${{ needs.build.result }}" != "success" ]; then
            echo "❌ Quality gate failed"
            echo "   Lint: ${{ needs.lint.result }}"
            echo "   Types: ${{ needs.typecheck.result }}"
            echo "   Tests: ${{ needs.test.result }}"
            echo "   Build: ${{ needs.build.result }}"
            exit 1
          fi
          echo "✅ All quality checks passed!"

Branch Protection Setup

In GitHub repository settings → Branches → Branch protection rules:

  1. Require status checks before merging
  2. Add ✅ Quality Gate as a required check
  3. Require branches to be up to date
  4. Optionally require PR reviews

Usage

# The workflow runs automatically on PRs to main/develop
# No manual setup needed after adding the file

# To test locally before pushing:
npm run lint -- --max-warnings=0
npx tsc --noEmit
npm test -- --coverage
npm run build