mcpField
G
github-actions

github-actions

github.com/bobmatnycCloud & DevOps78764 installsUpdated 15w ago
Repository

About github-actions

github-actions is a guide to GitHub’s native CI/CD platform for developers automating software workflows. It covers YAML workflow configuration, testing, builds, deployment, and release practices.

Key features

  1. 01YAML workflows and event triggers
  2. 02Job, step, and matrix configuration
  3. 03Testing, build, and deployment flows
  4. 04Caching, artifacts, and dependency handling
  5. 05Security scans and release automation

Use cases

  1. 01Run tests automatically on pull requests
  2. 02Build and deploy after code merges
  3. 03Schedule builds or backup tasks
  4. 04Scan code and publish packages

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/bobmatnyc/claude-mpm-skills --skill github-actions

After installing, type/github-actionsin Claude Code to confirm it loaded.

Bundled filesmetadata.json

Details

From the project README

GitHub Actions CI/CD

Summary

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.

When to Use

  • Automate testing on every pull request
  • Build and deploy applications on merge to main
  • Schedule regular tasks (nightly builds, backups)
  • Publish packages to registries (npm, PyPI, Docker Hub)
  • Run security scans and code quality checks
  • Automate release processes and changelog generation

Quick Start

Basic Test Workflow

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

Complete GitHub Actions Guide

Core Concepts

Workflows

YAML files in .github/workflows/ that define automation pipelines.

Structure:

  • Name: Workflow identifier
  • Triggers: Events that start the workflow
  • Jobs: One or more jobs to execute
  • Steps: Commands/actions within each job
name: CI Pipeline
on: [push, pull_request]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: echo "Building project"
Jobs

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
Steps

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
Actions

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

Workflow Syntax

Triggers (on)
Push Events
on:
  push:
    branches:
      - main
      - 'releases/**'  # Wildcard pattern
    tags:
      - 'v*'  # All version tags
    paths:
      - 'src/**'
      - '!src/docs/**'  # Exclude docs
Pull Request Events
on:
  pull_request:
    types: [opened, synchronize, reopened]
    branches: [main, develop]
    paths-ignore:
      - '**.md'
      - 'docs/**'
Schedule (Cron)
on:
  schedule:
    # Every day at 2:30 AM UTC
    - cron: '30 2 * * *'
    # Every Monday at 9:00 AM UTC
    - cron: '0 9 * * 1'
Manual Trigger
on:
  workflow_dispatch:
    inputs:
      environment:
        description: 'Deployment environment'
        required: true
        type: choice
        options:
          - staging
          - production
      version:
        description: 'Version to deploy'
        required: false
        default: 'latest'
Multiple Triggers
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
  schedule:
    - cron: '0 0 * * 0'  # Weekly
  workflow_dispatch:  # Manual
Environment Variables
Workflow-level
env:
  NODE_ENV: production
  API_URL: https://api.example.com

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - run: echo $NODE_ENV
Job-level
jobs:
  test:
    runs-on: ubuntu-latest
    env:
      TEST_DATABASE: test_db
    steps:
      - run: pytest
Step-level
steps:
  - name: Build
    run: npm run build
    env:
      BUILD_TARGET: production
Secrets

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:

  • Never commit secrets to code
  • Use GitHub encrypted secrets
  • Limit secret access to specific environments
  • Rotate secrets regularly

Contexts

github Context

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 }}"
env Context

Access environment variables.

env:
  BUILD_ID: 12345

steps:
  - run: echo "Build ${{ env.BUILD_ID }}"
secrets Context

Access repository secrets.

- run: echo "Token exists"
  env:
    TOKEN: ${{ secrets.GITHUB_TOKEN }}
matrix Context

Access matrix values.

strategy:
  matrix:
    node: [18, 20, 22]
steps:
  - run: echo "Testing Node ${{ matrix.node }}"
needs Context

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 }}"

Runners

GitHub-hosted Runners
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.04
  • macos-latest, macos-14, macos-13
  • windows-latest, windows-2022, windows-2019
Self-hosted Runners
runs-on: self-hosted

# With labels
runs-on: [self-hosted, linux, x64, gpu]

Setup:

  1. Go to Settings → Actions → Runners
  2. Click "New self-hosted runner"
  3. Follow platform-specific instructions
  4. Add custom labels for targeting

Matrix Strategies

Basic Matrix

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 }}
Include/Exclude
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
Fail-fast
strategy:
  fail-fast: false  # Continue other jobs if one fails
  matrix:
    node: [18, 20, 22]
Max Parallel
strategy:
  max-parallel: 2  # Run only 2 jobs concurrently
  matrix:
    node: [18, 20, 22]

Common Actions

Checkout Code
- uses: actions/checkout@v4
  with:
    fetch-depth: 0  # Full history for changelog
    submodules: true  # Include submodules
Setup Node.js
- uses: actions/setup-node@v4
  with:
    node-version: '20'
    cache: 'npm'  # or 'yarn', 'pnpm'
    registry-url: 'https://registry.npmjs.org'
Setup Python
- uses: actions/setup-python@v5
  with:
    python-version: '3.11'
    cache: 'pip'
Setup Java
- uses: actions/setup-java@v4
  with:
    distribution: 'temurin'
    java-version: '17'
    cache: 'maven'
Setup Go
- uses: actions/setup-go@v5
  with:
    go-version: '1.21'
    cache: true
Cache Dependencies
- uses: actions/cache@v4
  with:
    path: |
      ~/.npm
      node_modules
    key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
    restore-keys: |
      ${{ runner.os }}-node-
Upload Artifacts
- uses: actions/upload-artifact@v4
  with:
    name: build-output
    path: dist/
    retention-days: 7
Download Artifacts
- uses: actions/download-artifact@v4
  with:
    name: build-output
    path: dist/

Conditional Execution

if Conditions
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"
Job Conditions
jobs:
  deploy:
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    steps:
      - run: ./deploy.sh

Framework-Specific Workflows

Node.js/TypeScript
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 }}
Python
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'
Docker
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
Next.js with Vercel
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'

Deployment Patterns

Vercel Deployment
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'
Netlify Deployment
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 }}
AWS S3 + CloudFront
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 "/*"
Docker Registry Push
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 }}

Testing Workflows

Unit Tests with Coverage
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
Integration Tests
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
E2E Tests with Playwright
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

Release Automation

Semantic Release
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 }}
Create Release with Changelog
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
Publish npm Package
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 }}

Security Scanning

CodeQL Analysis
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
Dependency Scanning
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
Trivy Container Scan
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'

Composite Actions

Create reusable actions in .github/actions/.

Simple Composite Action

.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'
Reusable Workflows

.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 }}

Performance Optimization

Dependency Caching
- uses: actions/cache@v4
  with:
    path: ~/.npm
    key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
    restore-keys: |
      ${{ runner.os }}-node-
Docker Layer Caching
- uses: docker/build-push-action@v5
  with:
    context: .
    cache-from: type=gha
    cache-to: type=gha,mode=max
Parallelization
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
Conditional Job Execution
jobs:
  deploy:
    # Skip deploy on draft PRs
    if: github.event.pull_request.draft == false
    runs-on: ubuntu-latest
    steps:
      - run: ./deploy.sh

Debugging Workflows

Enable Debug Logging

Set repository secrets:

  • ACTIONS_RUNNER_DEBUG: true
  • ACTIONS_STEP_DEBUG: true
Debug Step
- name: Debug Info
  run: |
    echo "Event: ${{ github.event_name }}"
    echo "Ref: ${{ github.ref }}"
    echo "SHA: ${{ github.sha }}"
    echo "Actor: ${{ github.actor }}"
    env
Interactive Debugging with tmate
- name: Setup tmate session
  if: failure()
  uses: mxschmitt/action-tmate@v3
  timeout-minutes: 15

Best Practices

Security
  • Use secrets for sensitive data
  • Pin action versions to SHA: uses: actions/checkout@8e5e7e5a...
  • Minimize token permissions
  • Use environment protection rules
  • Enable branch protection with required checks
Performance
  • Cache dependencies aggressively
  • Use matrix strategies for parallel testing
  • Minimize checkout depth when possible
  • Use artifacts for job-to-job data transfer
  • Optimize Docker builds with multi-stage builds
Maintainability
  • Use reusable workflows for common patterns
  • Create composite actions for repeated steps
  • Document workflow purpose and triggers
  • Use meaningful job and step names
  • Keep workflows focused (single responsibility)
Reliability
  • Set appropriate timeouts
  • Use continue-on-error strategically
  • Implement retry logic for flaky tests
  • Monitor workflow run times
  • Clean up old artifacts and caches

Common Patterns

PR Comment on Failure
- 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.'
      })
Auto-merge Dependabot PRs
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 }}
Notify on Deploy
- 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 }}

Troubleshooting

Common Issues

Workflow not triggering:

  • Check branch filters match actual branch names
  • Verify workflow file is in .github/workflows/
  • Ensure YAML syntax is valid

Job skipped:

  • Check if conditions
  • Verify needs dependencies succeeded
  • Check branch protection rules

Timeout:

  • Default timeout is 360 minutes
  • Set explicit timeout: timeout-minutes: 30
  • Optimize long-running steps

Permission denied:

  • Update workflow permissions:
    permissions:
      contents: write
      pull-requests: write
    

Secrets not available:

  • Verify secret names match exactly (case-sensitive)
  • Check secret scope (repo, organization, environment)
  • Ensure workflow has access to environment secrets

Local Workflow Patterns (Your Repos)

Python + uv CI (mcp-vector-search)
  • Install uv: astral-sh/setup-uv@v3 and uv python install 3.11.
  • Use uv sync --dev and run uv run ruff, uv run mypy, uv run pytest.
  • Use OS + Python version matrix and upload coverage to Codecov on linux.
Node + pnpm CI (ai-code-review)
  • Use pnpm/action-setup@v4 and actions/setup-node@v4 with pnpm cache.
  • Install with pnpm install --frozen-lockfile, then pnpm run lint, pnpm run build:types, pnpm test.
Release on Tags
  • Trigger on push tags v*.
  • Build, create GitHub Release notes, and publish to npm or PyPI.
  • Use pypa/gh-action-pypi-publish@release/v1 or NODE_AUTH_TOKEN for npm publish.
Homebrew Update Pipeline
  • Trigger on workflow_run after CI success.
  • Run scripts/update_homebrew_formula.py with HOMEBREW_TAP_TOKEN.
  • On failure, open an issue with manual update steps.

Resources

Comments

Markdown supported