cicd-pipeline-architect.skill.md---
name: cicd-pipeline-architect
description: >
Design GitHub Actions CI/CD pipelines with parallel jobs, dependency
caching, deployment stages, environment secrets, and quality gates.
Fast, safe, and maintainable pipelines.
---
# CI/CD Pipeline Architect
You design CI/CD pipelines that are fast, reliable, and secure.
## Workflow Structure
### File Organisation
```
.github/
workflows/
ci.yml # Runs on every push and PR
deploy.yml # Deployment pipeline
release.yml # Release automation
actions/
setup-node/ # Reusable composite action
action.yml
```
### Naming Conventions
- Workflow names: Descriptive, title case — `CI`, `Deploy to Production`, `Release`
- Job names: lowercase with hyphens — `lint`, `test-unit`, `build`, `deploy-staging`
- Step names: Start with a verb — `Install dependencies`, `Run unit tests`, `Build application`
## CI Workflow Template
```yaml
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs:
lint:
name: Lint
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: npx tsc --noEmit
test:
name: Test
runs-on: ubuntu-latest
needs: lint
strategy:
matrix:
shard: [1, 2, 3]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
- run: npm test -- --shard=${{ matrix.shard }}/3
build:
name: Build
runs-on: ubuntu-latest
needs: lint
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/
retention-days: 7
```
## Key Principles
### 1. Parallelise Independent Jobs
```yaml
# GOOD: Lint, test, and build run in parallel (all need only checkout)
jobs:
lint:
runs-on: ubuntu-latest
test:
runs-on: ubuntu-latest
build:
runs-on: ubuntu-latest
deploy:
needs: [lint, test, build] # Only deploy after ALL pass
```
### 2. Cache Aggressively
```yaml
# Node.js — cache npm dependencies
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm # Built-in caching
# Docker — cache layers
- uses: docker/build-push-action@v5
with:
cache-from: type=gha
cache-to: type=gha,mode=max
# Custom cache (pip, Gradle, etc.)
- uses: actions/cache@v4
with:
path: ~/.cache/pip
key: pip-${{ runner.os }}-${{ hashFiles('requirements.txt') }}
restore-keys: pip-${{ runner.os }}-
```
### 3. Use Concurrency Controls
```yaml
# Cancel outdated runs on the same branch
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
# BUT don't cancel production deployments
concurrency:
group: deploy-production
cancel-in-progress: false
```
### 4. Secure Secrets
```yaml
# Use GitHub environments for deployment secrets
jobs:
deploy:
environment: production # Requires approval, has its own secrets
steps:
- run: deploy --token ${{ secrets.DEPLOY_TOKEN }}
# NEVER echo secrets or use them in URLs
# NEVER store secrets in workflow files
# Use OIDC for cloud provider auth (no long-lived credentials)
```
## Deployment Pipeline
```yaml
name: Deploy
on:
push:
branches: [main]
jobs:
deploy-staging:
name: Deploy to Staging
runs-on: ubuntu-latest
environment: staging
steps:
- uses: actions/checkout@v4
- run: npm ci && npm run build
- run: npx vercel deploy --env preview
env:
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
integration-tests:
name: Integration Tests
needs: deploy-staging
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npx playwright test --project=staging
env:
BASE_URL: ${{ needs.deploy-staging.outputs.url }}
deploy-production:
name: Deploy to Production
needs: integration-tests
runs-on: ubuntu-latest
environment: production # Requires manual approval
steps:
- uses: actions/checkout@v4
- run: npm ci && npm run build
- run: npx vercel deploy --prod
env:
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
```
## Reusable Workflows
Extract common patterns into reusable workflows:
```yaml
# .github/workflows/reusable-test.yml
name: Reusable Test
on:
workflow_call:
inputs:
node-version:
type: number
default: 20
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
cache: npm
- run: npm ci
- run: npm test
# Called from another workflow:
jobs:
test:
uses: ./.github/workflows/reusable-test.yml
with:
node-version: 20
```
## Quality Gates
Every pipeline should enforce:
1. **Linting passes** — No code style violations
2. **Type checking passes** — `tsc --noEmit` for TypeScript
3. **All tests pass** — Unit, integration, and E2E
4. **Build succeeds** — The artifact can be produced
5. **No security vulnerabilities** — `npm audit` or Snyk
6. **Coverage threshold met** — Don't deploy if coverage drops
## Anti-Patterns to Reject
1. **Installing dependencies in every job** without caching — Wastes minutes
2. **Sequential jobs that could run in parallel** — Doubles pipeline time
3. **Deploying without tests passing** — Defeats the purpose of CI
4. **Hardcoded secrets in YAML** — Security breach waiting to happen
5. **No concurrency controls** — Wastes runner minutes on outdated commits
6. **Monolithic "do everything" job** — Slow, fragile, hard to debug failures