toolchains/universal/infrastructure/github-actions/SKILL.md
GitHub Actions CI/CD workflows for automating build, test, and deployment
npx skillsauth add bobmatnyc/claude-mpm-skills github-actionsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
GitHub Actions is GitHub's native CI/CD platform for automating software workflows. Define workflows in YAML files to build, test, and deploy code directly from your repository with event-driven automation.
Create .github/workflows/test.yml:
name: Test
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm ci
- run: npm test
YAML files in .github/workflows/ that define automation pipelines.
Structure:
name: CI Pipeline
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: echo "Building project"
Independent execution units that run in parallel by default.
jobs:
lint:
runs-on: ubuntu-latest
steps:
- run: npm run lint
test:
runs-on: ubuntu-latest
needs: lint # Wait for lint to complete
steps:
- run: npm test
deploy:
runs-on: ubuntu-latest
needs: [lint, test] # Wait for both
steps:
- run: ./deploy.sh
Sequential commands or actions within a job.
steps:
# Use pre-built action
- uses: actions/checkout@v4
# Run shell command
- run: npm install
# Named step with environment
- name: Run tests
run: npm test
env:
NODE_ENV: test
Reusable units of code (from marketplace or custom).
# Official action
- uses: actions/checkout@v4
# Third-party action
- uses: docker/build-push-action@v5
with:
context: .
push: true
tags: user/app:latest
# Local action
- uses: ./.github/actions/custom-action
on:
push:
branches:
- main
- 'releases/**' # Wildcard pattern
tags:
- 'v*' # All version tags
paths:
- 'src/**'
- '!src/docs/**' # Exclude docs
on:
pull_request:
types: [opened, synchronize, reopened]
branches: [main, develop]
paths-ignore:
- '**.md'
- 'docs/**'
on:
schedule:
# Every day at 2:30 AM UTC
- cron: '30 2 * * *'
# Every Monday at 9:00 AM UTC
- cron: '0 9 * * 1'
on:
workflow_dispatch:
inputs:
environment:
description: 'Deployment environment'
required: true
type: choice
options:
- staging
- production
version:
description: 'Version to deploy'
required: false
default: 'latest'
on:
push:
branches: [main]
pull_request:
branches: [main]
schedule:
- cron: '0 0 * * 0' # Weekly
workflow_dispatch: # Manual
env:
NODE_ENV: production
API_URL: https://api.example.com
jobs:
build:
runs-on: ubuntu-latest
steps:
- run: echo $NODE_ENV
jobs:
test:
runs-on: ubuntu-latest
env:
TEST_DATABASE: test_db
steps:
- run: pytest
steps:
- name: Build
run: npm run build
env:
BUILD_TARGET: production
Store sensitive data in repository settings.
steps:
- name: Deploy
run: ./deploy.sh
env:
API_KEY: ${{ secrets.API_KEY }}
DATABASE_URL: ${{ secrets.DATABASE_URL }}
Best Practices:
Repository and workflow information.
steps:
- name: Print context
run: |
echo "Repository: ${{ github.repository }}"
echo "Ref: ${{ github.ref }}"
echo "SHA: ${{ github.sha }}"
echo "Actor: ${{ github.actor }}"
echo "Event: ${{ github.event_name }}"
echo "Branch: ${{ github.ref_name }}"
Access environment variables.
env:
BUILD_ID: 12345
steps:
- run: echo "Build ${{ env.BUILD_ID }}"
Access repository secrets.
- run: echo "Token exists"
env:
TOKEN: ${{ secrets.GITHUB_TOKEN }}
Access matrix values.
strategy:
matrix:
node: [18, 20, 22]
steps:
- run: echo "Testing Node ${{ matrix.node }}"
Access outputs from dependent jobs.
jobs:
build:
outputs:
version: ${{ steps.get_version.outputs.version }}
steps:
- id: get_version
run: echo "version=1.2.3" >> $GITHUB_OUTPUT
deploy:
needs: build
steps:
- run: echo "Deploying ${{ needs.build.outputs.version }}"
jobs:
ubuntu:
runs-on: ubuntu-latest # ubuntu-22.04
macos:
runs-on: macos-latest # macOS 14
windows:
runs-on: windows-latest # Windows 2022
specific:
runs-on: ubuntu-20.04 # Specific version
Available Runners:
ubuntu-latest, ubuntu-22.04, ubuntu-20.04macos-latest, macos-14, macos-13windows-latest, windows-2022, windows-2019runs-on: self-hosted
# With labels
runs-on: [self-hosted, linux, x64, gpu]
Setup:
Test across multiple versions.
strategy:
matrix:
node: [18, 20, 22]
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
strategy:
matrix:
node: [18, 20, 22]
os: [ubuntu-latest, windows-latest]
include:
# Add specific combination
- node: 22
os: macos-latest
experimental: true
exclude:
# Remove specific combination
- node: 18
os: windows-latest
strategy:
fail-fast: false # Continue other jobs if one fails
matrix:
node: [18, 20, 22]
strategy:
max-parallel: 2 # Run only 2 jobs concurrently
matrix:
node: [18, 20, 22]
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Full history for changelog
submodules: true # Include submodules
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm' # or 'yarn', 'pnpm'
registry-url: 'https://registry.npmjs.org'
- uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'pip'
- uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'
cache: 'maven'
- uses: actions/setup-go@v5
with:
go-version: '1.21'
cache: true
- uses: actions/cache@v4
with:
path: |
~/.npm
node_modules
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- uses: actions/upload-artifact@v4
with:
name: build-output
path: dist/
retention-days: 7
- uses: actions/download-artifact@v4
with:
name: build-output
path: dist/
steps:
- name: Deploy to production
if: github.ref == 'refs/heads/main'
run: ./deploy.sh
- name: Deploy to staging
if: github.ref == 'refs/heads/develop'
run: ./deploy-staging.sh
- name: Only on PR
if: github.event_name == 'pull_request'
run: echo "This is a PR"
- name: On success
if: success()
run: echo "Previous steps succeeded"
- name: On failure
if: failure()
run: echo "A step failed"
- name: Always run
if: always()
run: echo "Cleanup tasks"
jobs:
deploy:
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- run: ./deploy.sh
name: Node.js CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18, 20, 22]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- run: npm ci
- run: npm run lint
- run: npm run type-check
- run: npm test
env:
CI: true
- run: npm run build
- uses: codecov/codecov-action@v4
if: matrix.node-version == 20
with:
token: ${{ secrets.CODECOV_TOKEN }}
name: Python CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.10', '3.11', '3.12']
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
- run: pip install -r requirements.txt
- run: pip install pytest pytest-cov mypy ruff
- run: ruff check .
- run: mypy .
- run: pytest --cov=. --cov-report=xml
- uses: codecov/codecov-action@v4
if: matrix.python-version == '3.11'
name: Docker Build
on:
push:
branches: [main]
tags: ['v*']
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- uses: docker/metadata-action@v5
id: meta
with:
images: user/app
tags: |
type=ref,event=branch
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
- uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
name: Next.js CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build:
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 run build
- run: npm test
deploy-preview:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v4
- uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
deploy-production:
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v4
- uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
vercel-args: '--prod'
name: Deploy to Vercel
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
vercel-args: '--prod'
name: Deploy to Netlify
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm ci
- run: npm run build
- uses: netlify/actions/cli@master
with:
args: deploy --prod --dir=dist
env:
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
name: Deploy to AWS
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm ci
- run: npm run build
- uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- run: aws s3 sync dist/ s3://${{ secrets.S3_BUCKET }} --delete
- run: |
aws cloudfront create-invalidation \
--distribution-id ${{ secrets.CLOUDFRONT_DISTRIBUTION_ID }} \
--paths "/*"
name: Publish Docker Image
on:
release:
types: [published]
jobs:
push:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/build-push-action@v5
with:
context: .
push: true
tags: |
ghcr.io/${{ github.repository }}:latest
ghcr.io/${{ github.repository }}:${{ github.event.release.tag_name }}
name: Test Coverage
on: [push, pull_request]
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 test -- --coverage
- uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./coverage/coverage-final.json
fail_ci_if_error: true
name: Integration Tests
on: [push, pull_request]
jobs:
integration:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:15
env:
POSTGRES_PASSWORD: postgres
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
redis:
image: redis:7
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 6379:6379
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm ci
- run: npm run test:integration
env:
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/test
REDIS_URL: redis://localhost:6379
name: E2E Tests
on: [push, pull_request]
jobs:
e2e:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npx playwright install --with-deps
- run: npm run build
- run: npm run test:e2e
- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
path: playwright-report/
retention-days: 30
name: Release
on:
push:
branches: [main]
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Full history for changelog
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm ci
- run: npx semantic-release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
name: Create Release
on:
push:
tags:
- 'v*'
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Generate changelog
id: changelog
run: |
# Generate changelog from commits
CHANGELOG=$(git log $(git describe --tags --abbrev=0 HEAD^)..HEAD --pretty=format:"- %s (%h)" --no-merges)
echo "changelog<<EOF" >> $GITHUB_OUTPUT
echo "$CHANGELOG" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref_name }}
release_name: Release ${{ github.ref_name }}
body: |
## Changes
${{ steps.changelog.outputs.changelog }}
draft: false
prerelease: false
name: Publish to npm
on:
release:
types: [published]
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
registry-url: 'https://registry.npmjs.org'
- run: npm ci
- run: npm test
- run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
name: CodeQL
on:
push:
branches: [main]
pull_request:
branches: [main]
schedule:
- cron: '0 0 * * 1' # Weekly
jobs:
analyze:
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: ['javascript', 'python']
steps:
- uses: actions/checkout@v4
- uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
- uses: github/codeql-action/autobuild@v3
- uses: github/codeql-action/analyze@v3
name: Dependency Check
on:
push:
branches: [main]
schedule:
- cron: '0 0 * * 1'
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm audit --audit-level=moderate
- run: npx snyk test
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
continue-on-error: true
name: Container Security Scan
on:
push:
branches: [main]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: docker build -t myapp:${{ github.sha }} .
- uses: aquasecurity/trivy-action@master
with:
image-ref: 'myapp:${{ github.sha }}'
format: 'sarif'
output: 'trivy-results.sarif'
- uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: 'trivy-results.sarif'
Create reusable actions in .github/actions/.
.github/actions/setup-project/action.yml:
name: 'Setup Project'
description: 'Install dependencies and cache'
inputs:
node-version:
description: 'Node.js version'
required: false
default: '20'
runs:
using: 'composite'
steps:
- uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
cache: 'npm'
- run: npm ci
shell: bash
Usage:
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup-project
with:
node-version: '20'
.github/workflows/reusable-deploy.yml:
name: Reusable Deploy
on:
workflow_call:
inputs:
environment:
required: true
type: string
secrets:
deploy-token:
required: true
jobs:
deploy:
runs-on: ubuntu-latest
environment: ${{ inputs.environment }}
steps:
- uses: actions/checkout@v4
- run: ./deploy.sh
env:
DEPLOY_TOKEN: ${{ secrets.deploy-token }}
ENVIRONMENT: ${{ inputs.environment }}
Usage:
name: Deploy Production
on:
push:
branches: [main]
jobs:
deploy:
uses: ./.github/workflows/reusable-deploy.yml
with:
environment: production
secrets:
deploy-token: ${{ secrets.PRODUCTION_TOKEN }}
- uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- uses: docker/build-push-action@v5
with:
context: .
cache-from: type=gha
cache-to: type=gha,mode=max
jobs:
lint:
runs-on: ubuntu-latest
steps:
- run: npm run lint
test-unit:
runs-on: ubuntu-latest
steps:
- run: npm run test:unit
test-integration:
runs-on: ubuntu-latest
steps:
- run: npm run test:integration
# All run in parallel
jobs:
deploy:
# Skip deploy on draft PRs
if: github.event.pull_request.draft == false
runs-on: ubuntu-latest
steps:
- run: ./deploy.sh
Set repository secrets:
ACTIONS_RUNNER_DEBUG: trueACTIONS_STEP_DEBUG: true- name: Debug Info
run: |
echo "Event: ${{ github.event_name }}"
echo "Ref: ${{ github.ref }}"
echo "SHA: ${{ github.sha }}"
echo "Actor: ${{ github.actor }}"
env
- name: Setup tmate session
if: failure()
uses: mxschmitt/action-tmate@v3
timeout-minutes: 15
uses: actions/checkout@8e5e7e5a...continue-on-error strategically- name: Comment on PR
if: failure() && github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: '❌ Tests failed. Please check the workflow logs.'
})
name: Auto-merge Dependabot
on:
pull_request:
types: [opened, synchronize]
jobs:
auto-merge:
if: github.actor == 'dependabot[bot]'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm test
- uses: gh enable-auto-merge --merge
if: success()
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Slack Notification
if: always()
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
text: 'Deploy to ${{ inputs.environment }}: ${{ job.status }}'
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
Workflow not triggering:
.github/workflows/Job skipped:
if conditionsneeds dependencies succeededTimeout:
timeout-minutes: 30Permission denied:
permissions:
contents: write
pull-requests: write
Secrets not available:
astral-sh/setup-uv@v3 and uv python install 3.11.uv sync --dev and run uv run ruff, uv run mypy, uv run pytest.pnpm/action-setup@v4 and actions/setup-node@v4 with pnpm cache.pnpm install --frozen-lockfile, then pnpm run lint, pnpm run build:types, pnpm test.push tags v*.pypa/gh-action-pypi-publish@release/v1 or NODE_AUTH_TOKEN for npm publish.workflow_run after CI success.scripts/update_homebrew_formula.py with HOMEBREW_TAP_TOKEN.development
Axum (Rust) web framework patterns for production APIs: routers/extractors, state, middleware, error handling, tracing, graceful shutdown, and testing
development
Optimize web performance using Core Web Vitals, modern patterns (View Transitions, Speculation Rules), and framework-specific techniques
development
Best practices for documenting APIs and code interfaces, eliminating redundant documentation guidance per agent.
development
Comprehensive API design patterns covering REST, GraphQL, gRPC, versioning, authentication, and modern API best practices