CI Pipeline
GitHub Actions CI/CD patterns for reliable, fast pipelines.
When to Use
- Creating new GitHub Actions workflows
- Optimizing slow CI builds
- Debugging pipeline failures
- Implementing deployment strategies
- Adding security scanning
MCP Workflow
# 1. Find existing workflows
serena.list_dir(".github/workflows")
# 2. Check workflow patterns
serena.search_for_pattern("uses:|run:|cache:|matrix:", paths_include_glob=".github/workflows/*.yml")
# 3. Find reusable workflows
jetbrains.search_in_files_by_text("workflow_call", fileMask="*.yml")
# 4. GitHub Actions docs
context7.get-library-docs("/github/actions", "caching")
Workflow Structure
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true # Cancel outdated runs
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Job steps...
Caching Strategies
Gradle Cache
- name: Cache Gradle
uses: actions/cache@v4
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: gradle-${{ runner.os }}-
npm/pnpm Cache
- name: Cache pnpm
uses: actions/cache@v4
with:
path: ~/.pnpm-store
key: pnpm-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: pnpm-${{ runner.os }}-
# Or use setup action with built-in cache
- uses: pnpm/action-setup@v4
with:
version: 9
- uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'pnpm'
uv (Python) Cache
- name: Cache uv
uses: actions/cache@v4
with:
path: ~/.cache/uv
key: uv-${{ runner.os }}-${{ hashFiles('**/uv.lock') }}
restore-keys: uv-${{ runner.os }}-
Docker Layer Cache
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build with cache
uses: docker/build-push-action@v6
with:
context: .
push: false
cache-from: type=gha
cache-to: type=gha,mode=max
Matrix Builds
jobs:
test:
strategy:
fail-fast: false # Don't cancel other jobs on failure
matrix:
os: [ubuntu-latest, macos-latest]
node: [20, 22]
exclude:
- os: macos-latest
node: 20
runs-on: ${{ matrix.os }}
steps:
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
Job Dependencies
jobs:
build:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.value }}
steps:
- id: version
run: echo "value=$(cat version.txt)" >> $GITHUB_OUTPUT
deploy:
needs: [build, test] # Waits for both
if: success() # Only if both succeeded
runs-on: ubuntu-latest
steps:
- run: echo "Deploying ${{ needs.build.outputs.version }}"
Reusable Workflows
Define Reusable Workflow
# .github/workflows/build-and-test.yml
name: Build and Test
on:
workflow_call:
inputs:
node-version:
type: string
default: '22'
secrets:
npm-token:
required: false
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
- run: npm ci
env:
NPM_TOKEN: ${{ secrets.npm-token }}
Call Reusable Workflow
jobs:
build:
uses: ./.github/workflows/build-and-test.yml
with:
node-version: '22'
secrets:
npm-token: ${{ secrets.NPM_TOKEN }}
Security Patterns
Minimal Permissions
permissions:
contents: read
pull-requests: write # Only what's needed
jobs:
security:
runs-on: ubuntu-latest
permissions:
security-events: write # Job-level override
Dependency Scanning
- name: Scan dependencies
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
severity: 'CRITICAL,HIGH'
exit-code: '1' # Fail on findings
Secret Scanning
- name: Check for secrets
uses: trufflesecurity/trufflehog@main
with:
path: ./
base: ${{ github.event.repository.default_branch }}
head: HEAD
Deployment Strategies
Environment-based Deployment
jobs:
deploy-staging:
environment: staging
runs-on: ubuntu-latest
steps:
- run: ./deploy.sh staging
deploy-production:
needs: deploy-staging
environment:
name: production
url: https://app.example.com
runs-on: ubuntu-latest
steps:
- run: ./deploy.sh production
Manual Approval
environment:
name: production
# Requires approval in repo settings
Debugging Pipelines
Enable Debug Logging
# Set secret ACTIONS_STEP_DEBUG=true
# Or re-run with debug logging enabled in UI
SSH Debug Session
- name: Debug with tmate
if: failure()
uses: mxschmitt/action-tmate@v3
with:
limit-access-to-actor: true
Artifact for Debugging
- name: Upload logs on failure
if: failure()
uses: actions/upload-artifact@v4
with:
name: debug-logs
path: |
**/logs/
**/test-results/
Anti-Patterns
| Pattern |
Problem |
Solution |
| No cache |
Slow builds |
Add appropriate caching |
if: always() for deploy |
Deploys broken code |
Use if: success() |
| Secrets in logs |
Security risk |
Use ::add-mask:: |
| Single monolith job |
Slow, no parallelism |
Split into dependent jobs |
No concurrency |
Wasted resources |
Cancel outdated runs |
| Hardcoded versions |
Drift |
Use variables or renovate |
Speed Optimization Checklist
Quality Checklist