GitHub Actions CI Pipeline Setup
Continuous Integration (CI) is the practice of automatically testing every code change as soon as it is committed. A CI pipeline catches bugs early, enforces code quality standards, and gives the team confidence that the codebase stays in a healthy state. This topic walks through building a complete, production-ready CI pipeline from scratch.
What a CI Pipeline Does
A CI pipeline runs automatically on every pull request and push. It checks that the new code:
- Compiles without errors
- Passes all automated tests
- Meets code style and formatting standards
- Contains no known security vulnerabilities
- Has acceptable test coverage
Developer pushes code
↓
CI pipeline starts automatically
↓
┌─────────────┬──────────────┬──────────────┐
│ lint job │ test job │ security job │
└─────────────┴──────────────┴──────────────┘
↓ (all must pass)
┌─────────────────────────────┐
│ build job │
└─────────────────────────────┘
↓
Pull request shows: ✓ All checks passed
↓
Team can safely merge the code
Building a Node.js CI Pipeline
name: Node.js CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
lint:
name: Code Quality Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm run lint
- run: npm run format:check
test:
name: Run Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- name: Run tests with coverage
run: npm test -- --coverage
- name: Upload coverage report
uses: actions/upload-artifact@v4
with:
name: coverage-report
path: ./coverage
retention-days: 5
build:
name: Build Application
needs: [lint, test]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm run build
- uses: actions/upload-artifact@v4
with:
name: build-output
path: ./dist
Understanding the Pipeline Flow
Trigger: pull_request → main
│
├── lint job starts ──────────────────────────────┐
│ │
└── test job starts ───────────────────────────── ┤ (both run in parallel)
│
build job waits for both
│
build job runs after lint + test pass
The needs: [lint, test] line on the build job creates this gate. The build only runs when both quality checks succeed. This saves compute time by not building code that already has lint or test failures.
Setting Branch Protection Rules
Connect your CI pipeline to GitHub's branch protection feature to enforce passing checks before merging:
- Go to Settings → Branches in your repository
- Click Add branch protection rule
- Set the branch name pattern to
main - Enable Require status checks to pass before merging
- Search for and select your workflow jobs:
lint,test,build - Enable Require branches to be up to date before merging
- Click Save changes
Once configured, GitHub blocks the merge button on any pull request where CI checks fail or have not yet completed.
Adding a Python CI Pipeline
name: Python CI
on: [push, pull_request]
jobs:
quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: 'pip'
- run: pip install -r requirements-dev.txt
- name: Lint with flake8
run: flake8 src/ tests/
- name: Type check with mypy
run: mypy src/
- name: Run pytest with coverage
run: pytest --cov=src --cov-report=xml
- name: Upload coverage to artifact
uses: actions/upload-artifact@v4
with:
name: coverage-xml
path: coverage.xml
CI Status Badges
GitHub generates a status badge image for any workflow. Add it to your README so anyone visiting the repository can see the current CI status at a glance.
Badge URL format:
https://github.com/{owner}/{repo}/actions/workflows/{filename}.yml/badge.svg
In README.md:

Badge appearance:
passing → green badge with "passing"
failing → red badge with "failing"
Common CI Best Practices
- Run lint and tests in parallel — no reason for them to wait on each other
- Gate the build job on all quality checks with
needs - Use
npm ciinstead ofnpm install— it uses exact lockfile versions - Cache dependencies to keep runs under 2 minutes
- Upload test coverage reports as artifacts for review
- Set branch protection to enforce CI before merging
- Pin all action versions to exact SHAs in production repositories
Measuring CI Pipeline Performance
Target metric | Goal
---------------------|---------------------------
Total pipeline time | Under 5 minutes
Test job time | Under 3 minutes
Cache hit rate | Above 80%
Flaky test rate | Below 1%
Pipeline success rate| Above 95%
Monitor these metrics in the GitHub Actions tab. Consistently slow or failing pipelines hurt developer productivity and undermine confidence in the CI process.
