Use when setting up, auditing, or modernizing GitHub Actions CI/CD workflows: automated testing, Docker image builds, Kubernetes deployments, security scanning, matrix builds, reusable workflows, and approval-gated production releases. Covers workflow architecture, secret handling, caching, permissions, and environment protection rules. Excludes GitLab CI, CircleCI, Jenkins, or other non-GitHub CI systems (see `gitlab-ci-patterns` for GitLab).
84%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Passed
No findings from the security scan
Production-ready GitHub Actions workflow patterns for automated testing, Docker builds, Kubernetes deployments, security scanning, and approval-gated releases.
Always pin to a major version tag (e.g. @v7), never @latest.
| Action | Current major | Notes |
|---|---|---|
actions/checkout | @v7 | Runs on Node 24 runtime |
actions/setup-node | @v7 | Use node-version: '22' or '24' for current LTS |
actions/setup-python | @v5 | |
docker/login-action | @v3 | |
docker/metadata-action | @v6 | |
docker/build-push-action | @v7 | v5 is deprecated (Node 20 EOL) |
aws-actions/configure-aws-credentials | @v4 | Prefer OIDC over long-lived keys |
aquasecurity/trivy-action | @v0.36.0+ | v0.28–v0.34.2 had a security compromise in early 2026; use ≥ v0.35.0 |
github/codeql-action/upload-sarif | @v4 | Requires security-events: write permission |
codecov/codecov-action | @v5 | Use token: ${{ secrets.CODECOV_TOKEN }} |
snyk/actions/node | @master | Or use snyk/actions/setup for CLI-only install |
slackapi/slack-github-action | @v4 | API changed in v2+; use method: chat.postMessage |
name: Test
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [22, 24] # Current Active LTS and Current
steps:
- uses: actions/checkout@v7
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v7
with:
node-version: ${{ matrix.node-version }}
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Run linter
run: npm run lint
- name: Run tests
run: npm test
- name: Upload coverage
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./coverage/lcov.info
fail_ci_if_error: truename: Build and Push
on:
push:
branches: [main]
tags: ["v*"]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v7
- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v6
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
- name: Build and push
uses: docker/build-push-action@v7
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=maxname: Deploy to Kubernetes
on:
push:
branches: [main]
permissions:
id-token: write # Required for OIDC token exchange
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Configure AWS credentials (OIDC)
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: us-west-2
- name: Update kubeconfig
run: |
aws eks update-kubeconfig --name production-cluster --region us-west-2
- name: Deploy to Kubernetes
run: |
kubectl apply -f k8s/
kubectl rollout status deployment/my-app -n production
- name: Verify deployment
run: |
kubectl get pods -n production
kubectl describe deployment my-app -n productionSecurity note: Prefer OIDC (
role-to-assume) over long-livedAWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEYsecrets. OIDC tokens expire automatically and leave no persistent credentials in GitHub.
name: Matrix Build
on: [push, pull_request]
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ["3.10", "3.11", "3.12"]
steps:
- uses: actions/checkout@v7
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: "pip"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Run tests
run: pytest@v7, not @latest. Floating @latest
can silently break workflows on major releases.cache: "npm" / cache: "pip" shaves minutes off
every run.permissions: at the job level;
default contents: read prevents accidental write access.workflow_call instead of
copy-pasting YAML across repositories.if: failure()
so on-call engineers are paged immediately.# .github/workflows/reusable-test.yml
name: Reusable Test Workflow
on:
workflow_call:
inputs:
node-version:
required: true
type: string
secrets:
NPM_TOKEN:
required: true
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: ${{ inputs.node-version }}
cache: "npm"
- run: npm ci
- run: npm testInvoke the reusable workflow from another workflow:
jobs:
call-test:
uses: ./.github/workflows/reusable-test.yml
with:
node-version: "22"
secrets:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}name: Security Scan
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
contents: read
security-events: write # Required for uploading SARIF results
jobs:
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@v0.36.0
with:
scan-type: "fs"
scan-ref: "."
format: "sarif"
output: "trivy-results.sarif"
exit-code: "1" # Fail the job on HIGH/CRITICAL findings
severity: "HIGH,CRITICAL"
- name: Upload Trivy results to GitHub Security
uses: github/codeql-action/upload-sarif@v4
if: always() # Upload even when trivy-action fails
with:
sarif_file: "trivy-results.sarif"
- name: Run Snyk Security Scan
uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}⚠️ Trivy security note: Versions
v0.28.0–v0.34.2ofaquasecurity/trivy-actionwere compromised in early 2026. Usev0.35.0or later.
name: Deploy to Production
on:
push:
tags: ["v*"]
permissions:
contents: read
id-token: write
jobs:
deploy:
runs-on: ubuntu-latest
environment:
name: production
url: https://app.example.com # Shown in the deployment UI
steps:
- uses: actions/checkout@v7
- name: Deploy application
run: |
echo "Deploying ${{ github.ref_name }} to production..."
# Insert your deploy command here
- name: Notify Slack on success
if: success()
uses: slackapi/slack-github-action@v4
with:
method: chat.postMessage
token: ${{ secrets.SLACK_BOT_TOKEN }}
payload: |
{
"channel": "${{ secrets.SLACK_DEPLOY_CHANNEL }}",
"text": "✅ Deployment of ${{ github.ref_name }} to production completed successfully!"
}
- name: Notify Slack on failure
if: failure()
uses: slackapi/slack-github-action@v4
with:
method: chat.postMessage
token: ${{ secrets.SLACK_BOT_TOKEN }}
payload: |
{
"channel": "${{ secrets.SLACK_DEPLOY_CHANNEL }}",
"text": "🚨 Deployment of ${{ github.ref_name }} to production FAILED. Check the run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
}Configure the
productionenvironment in Settings → Environments to require one or more designated reviewers before this job runs.
deployment-pipeline-design — Pipeline architecture and stage designsecrets-management — Secrets rotation, OIDC, and vault integrationgitlab-ci-patterns — For GitLab CI/CD equivalents54a73d7
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.