GitHub Actions CI/CD
How I built the CI/CD pipelines at Binocs and on personal projects, with caching, matrix builds, and OIDC-based AWS auth.
GitHub Actions is workflow YAML that runs on push, PR, schedule, or manual dispatch. Each workflow has jobs, each job runs on a runner (ephemeral VM or container), each job has steps. The ecosystem of marketplace actions does the heavy lifting.
The pipeline I built at Binocs
Three workflows per service:
- PR validation: lint, type-check, unit tests, build, security scan.
- Main branch: above plus integration tests, build Docker image, push to ECR, update Helm values in the deployment repo.
- Tag-based release: deploy to prod via ArgoCD sync.
Anatomy of a workflow
name: CI
on:
pull_request:
push:
branches: [main]
jobs:
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 lint
- run: npm test -- --coverage
- uses: codecov/codecov-action@v4OIDC over static keys
The wrong way: store AWS_ACCESS_KEY_ID as a secret. Long-lived, leakable, no audit trail of "which workflow used it."
The right way: OIDC. GitHub issues a JWT to your workflow run. You configure an IAM role whose trust policy accepts that JWT, scoped to a specific repo and branch.
permissions:
id-token: write
contents: read
steps:
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123:role/github-actions-ci
aws-region: us-east-1No secrets to rotate, no keys to leak, CloudTrail logs which workflow run did what.
Caching that actually helps
actions/cache keyed on lock files:
- uses: actions/cache@v4
with:
path: ~/.npm
key: npm-${{ hashFiles('package-lock.json') }}
restore-keys: npm-For Docker builds: enable BuildKit, push the build cache to a registry, pull on subsequent builds.
- uses: docker/build-push-action@v5
with:
cache-from: type=registry,ref=ecr-uri/app:buildcache
cache-to: type=registry,ref=ecr-uri/app:buildcache,mode=maxReal numbers at Binocs: cold CI was 8 minutes, with caching it dropped to 2 minutes.
Matrix builds
strategy:
matrix:
node: [18, 20, 22]
os: [ubuntu-latest, macos-latest]Six parallel jobs. Useful for libraries supporting multiple versions. Excessive for app deployment.
Learn more
- Docs
- Docs
- Repo