terraform plan in CI to catch infrastructure drift--shard flags, identify and quarantine flaky tests..github/workflows/. Defines the full automation process.push, pull_request, schedule, workflow_dispatch, workflow_calluses:ubuntu-latest) or self-hosted.on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci && npm test
actions/upload-artifact / actions/download-artifact. Use for files (build outputs, test reports). Stored in GitHub, max 90 days.$GITHUB_OUTPUT and referenced with needs.job-name.outputs.key. Use for version numbers, flags, short strings.actions/cache for dependency caching (node_modules, pip packages). Keyed by hash of lockfile.# Set output
- run: echo "version=1.2.3" >> $GITHUB_OUTPUT
id: version
# Read in downstream job
- run: echo ${{ needs.build.outputs.version }}
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
node: [18, 20, 22]
fail-fast: false # don't cancel other matrix jobs on failure
steps:
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- run: npm test
fail-fast: false โ run all combinations even if one fails, so you get a full pictureinclude: / exclude: โ add specific combinations or remove invalid ones# .github/actions/setup-aws/action.yml
runs:
using: composite
steps:
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ inputs.role-arn }}
Reusable Workflows define an entire job (or jobs) that can be called by other workflows. They run on their own runner. Use for: full deployment pipelines you want to centralize across repos.
# called from another workflow:
jobs:
deploy:
uses: org/shared-workflows/.github/workflows/deploy.yml@main
with:
environment: production
token.actions.githubusercontent.comaws-actions/configure-aws-credentials with role-to-assumepermissions:
id-token: write # required for OIDC
contents: read
steps:
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789:role/github-deploy
aws-region: us-east-1
pipeline {
agent any
stages {
stage('Build') { steps { sh 'npm run build' } }
stage('Test') { steps { sh 'npm test' } }
}
}
Scripted Pipeline โ full Groovy code, maximum flexibility, no structural constraints:
node {
stage('Build') { sh 'npm run build' }
stage('Test') { sh 'npm test' }
}
script {} blocks for when you need Groovy logic inside itparallel directive inside a stage:
stage('Test') {
parallel {
stage('Unit Tests') {
steps { sh 'npm run test:unit' }
}
stage('Integration Tests') {
steps { sh 'npm run test:integration' }
}
stage('Lint') {
steps { sh 'npm run lint' }
}
}
}
Key considerations:
failFast: true inside parallel {} cancels remaining branches if one failsparallel() step with a map of closuresvars/ โ global pipeline steps callable as myStep() in any pipelinesrc/ โ Groovy classes for more complex logicresources/ โ static files accessible via libraryResource// Jenkinsfile
@Library('my-shared-lib') _
pipeline {
stages {
stage('Deploy') { steps { standardDeploy(env: 'staging') } }
}
}
credentials() binding in pipelines: environment { AWS_CREDS = credentials('aws-prod-creds') }credentials()JENKINS_HOME โ on pod restart it recovers from diskJENKINS_HOME (jobs, credentials, plugins) using the Backup plugin or snapshotting the PVCkubectl rollout undo deployment/app โ reverts to previous ReplicaSet. Previous image is cached.kubectl argo rollouts abort โ auto-rolls back canaryapp:a1b2c3dapp:a1b2c3d to dev namespace with dev config (environment variables, secrets, replicas)app:a1b2c3d to staging with staging config.gitignore for .env, key files, credentials files โ but hooks are more reliablefinally / post-pipeline stepdocker push registry/app:sha123cosign sign --key cosign.key registry/app:sha123 (or keyless via OIDC)syft, trivy sbomslsa-github-generator in GitHub Actionsretry: 2. Acceptable for known-flaky tests but masks the root cause./health or /ready:
/health (liveness) โ is the process alive? Return 200 if yes./ready (readiness) โ can the service handle traffic? Check DB connection, cache, dependencies.- name: Wait for deployment
run: kubectl rollout status deployment/app --timeout=5m
- name: Smoke test
run: |
curl -f https://staging.app.com/health
curl -f https://staging.app.com/api/v1/ping
If smoke tests fail, trigger automatic rollback before the issue reaches more traffic.
jest --shard=1/4, pytest-xdist, RSpec parallelnode_modules, .venv, Maven ~/.m2 โ keyed on lockfile hashterraform validate โ syntax and config validityterraform fmt --check โ formattingterraform plan against a test account and post the plan diff as a PR comment (Atlantis, Terraform Cloud)deferpackage-lock.json, Pipfile.lock) and use npm ci not npm installgit update-index --chmod=+x script.sh)env and pwd debug steps to print the CI environmenttmate action to SSH into a running runnerdocker run --rm -it ubuntu:latest bashact (GitHub) to run workflows locally# GitHub Actions
- uses: actions/cache@v4
with:
path: ~/.npm
key: npm-${{ hashFiles('package-lock.json') }}
restore-keys: npm-
What to cache by ecosystem:
~/.npm or node_modules (prefer npm cache dir)~/.cache/pip or the virtualenv directory~/go/pkg/mod~/.m2/repository--cache-from or BuildKit registry cache# Copy dependency files first, install, THEN copy source code COPY package-lock.json package.json ./ RUN npm ci COPY . . # source changes don't invalidate the npm install layer