github-actions

GitHub Actions CI/CD workflows for automating build, test, and deployment

INSTALLATION
npx skills add https://github.com/bobmatnyc/claude-mpm-skills --skill github-actions
Run in your project or agent environment. Adjust flags if your CLI version differs.

SKILL.md

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:

  • Go to Settings → Actions → Runners
  • Click "New self-hosted runner"
  • Follow platform-specific instructions
  • 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() &#x26;&#x26; 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

BrowserAct

Let your agent run on any real-world website

Bypass CAPTCHA & anti-bot for free. Start local, scale to cloud.

Explore BrowserAct Skills →

Stop writing automation&scrapers

Install the CLI. Run your first Skill in 30 seconds. Scale when you're ready.

Start free
free · no credit card