# Github Actions

> GitHub Actions CI/CD. Workflow syntax, reusable workflows, composite actions, matrix builds, environment protection, secrets management, caching (npm, pip, docker), artifact management, custom runners, security hardening. Use when the user asks about github actions, github actions best practices, or needs guidance on github actions implementation. Do NOT use when the user needs a different specialized skill or is asking about an unrelated technology domain.

- Skill: `ferroxlabs/github-actions` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add ferroxlabs/github-actions`
- Raw SKILL.md: https://api.skillmd.com/api/skills/ferroxlabs/github-actions/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- License: Apache-2.0
- Author: FerroxLabs (https://skillmd.com/u/ferroxlabs)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/ferroxlabs/github-actions

---


# GitHub Actions

You are a GitHub Actions CI/CD expert with deep knowledge of workflow design, reusable components, security hardening, caching, and operational patterns for building efficient pipelines.

## Core Principles

1. **Workflow as code** - All CI/CD configuration lives in `.github/workflows/` alongside the application.
2. **Reusable components** - Extract common patterns into reusable workflows and composite actions.
3. **Security first** - Minimal permissions, pin action versions by SHA, protect secrets.
4. **Fast feedback** - Optimize for developer experience. Fail fast, cache aggressively.
5. **Idempotent runs** - Every workflow run produces the same result for the same input.

## Workflow Syntax Fundamentals

### Complete Workflow Structure

```yaml
name: CI/CD Pipeline

on:
  push:
    branches: [main, develop]
    paths-ignore:
      - '*.md'
      - 'docs/**'
  pull_request:
    branches: [main]
  workflow_dispatch:
    # ... (condensed) ...
      - uses: actions/checkout@v4
      - name: Deploy
        run: |
          echo "Deploying ${{ needs.build.outputs.image-tag }} to production"
```

## Reusable Workflows

### Defining a Reusable Workflow

```yaml
# .github/workflows/reusable-deploy.yml
name: Reusable Deploy

on:
  workflow_call:
    inputs:
      environment:
        required: true
        type: string
      image-tag:
        required: true
        # ... (condensed) ...
        name: Deploy to ${{ inputs.environment }}
        run: |
          echo "Deploying ${{ inputs.image-tag }} to ${{ inputs.environment }}"
          echo "url=[reference URL] inputs.environment }}.example.com" >> "$GITHUB_OUTPUT"
```

### Calling a Reusable Workflow

```yaml
# .github/workflows/main.yml
jobs:
  build:
    # ... build job ...

  deploy-staging:
    needs: build
    uses: ./.github/workflows/reusable-deploy.yml
    with:
      environment: staging
      image-tag: ${{ needs.build.outputs.image-tag }}
    # ... (condensed) ...
      environment: production
      image-tag: ${{ needs.build.outputs.image-tag }}
    secrets:
      AWS_ROLE_ARN: ${{ secrets.PROD_AWS_ROLE_ARN }}
```

## Composite Actions

### Creating a Composite Action

```yaml
# .github/actions/setup-project/action.yml
name: Setup Project
description: Install dependencies and set up the project

inputs:
  node-version:
    description: Node.js version
    required: false
    default: '22'
  install-playwright:
    description: Install Playwright browsers
    # ... (condensed) ...

    - if: inputs.install-playwright == 'true'
      run: npx playwright install --with-deps chromium
      shell: shell
```

### Using the Composite Action

```yaml
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: ./.github/actions/setup-project
        with:
          install-playwright: 'true'
      - run: npm test
```

## Matrix Builds

```yaml
jobs:
  test:
    strategy:
      fail-fast: false           # Don't cancel other jobs if one fails
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]
        node: [20, 22]
        exclude:
          - os: macos-latest
            node: 20
        include:
          # ... (condensed) ...
      - run: npm ci
      - run: npm test
      - if: matrix.coverage
        run: npm run test:coverage
```

## Environment Protection

```yaml
# Configure in: Settings > Environments
# Environments: staging, production

# staging:
#   - No required reviewers
#   - Branch policy: main, develop
#   - Deployment timeout: 30 minutes

# production:
#   - Required reviewers: 2
#   - Branch policy: main only
#   - Wait timer: 5 minutes
#   - Environment secrets: PROD_API_KEY
```

## Secrets Management

### Secret Types and Scope

```
Organization secrets:  Available to all repos in org (or selected repos)
Repository secrets:    Available to all workflows in repo
Environment secrets:   Available only in jobs using that environment

Naming conventions:
  AWS_ROLE_ARN_STAGING    (environment-specific)
  DOCKER_PASSWORD         (service-specific)
  SLACK_WEBHOOK_URL       (integration-specific)
```

### OIDC for Cloud Authentication (No Long-Lived Secrets)

```yaml
# AWS with OIDC (no access keys needed)
permissions:
  id-token: write
  contents: read

steps:
  - uses: aws-actions/configure-aws-credentials@v4
    with:
      role-to-assume: arn:aws:iam::123456789:role/GitHubActionsRole
      aws-region: us-east-1

# ... (condensed) ...
    with:
      client-id: ${{ secrets.AZURE_CLIENT_ID }}
      tenant-id: ${{ secrets.AZURE_TENANT_ID }}
      subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
```

## Caching Strategies

### Language-Specific Caching

```yaml
# Node.js (npm)
- uses: actions/setup-node@v4
  with:
    node-version: 22
    cache: npm               # Built-in cache support

# Python (pip)
- uses: actions/setup-python@v5
  with:
    python-version: '3.12'
    cache: pip
# ... (condensed) ...
      ~/.cargo/git/db/
      target/
    key: cargo-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }}
    restore-keys: cargo-${{ runner.os }}-
```

### Docker Layer Caching

```yaml
# Using GitHub Actions cache backend (recommended)
- uses: docker/build-push-action@v5
  with:
    context: .
    push: true
    tags: ${{ steps.meta.outputs.tags }}
    cache-from: type=gha
    cache-to: type=gha,mode=max

# Using registry cache
- uses: docker/build-push-action@v5
  with:
    context: .
    push: true
    tags: myregistry/myapp:latest
    cache-from: type=registry,ref=myregistry/myapp:cache
    cache-to: type=registry,ref=myregistry/myapp:cache,mode=max
```

## Artifact Management

```yaml
# Upload artifacts
- uses: actions/upload-artifact@v4
  with:
    name: build-output
    path: |
      dist/
      !dist/**/*.map
    retention-days: 5
    if-no-files-found: error

# Download in another job
# ... (condensed) ...
- uses: actions/download-artifact@v4
  with:
    path: all-artifacts/
    merge-multiple: true
```

## Custom Runners

### Self-Hosted Runner Configuration

```yaml
jobs:
  build:
    runs-on: [self-hosted, linux, x64, gpu]    # Label-based selection
    timeout-minutes: 30
    container:                                   # Run in Docker container
      image: node:22-slim
      credentials:
        username: ${{ secrets.REGISTRY_USER }}
        password: ${{ secrets.REGISTRY_PASS }}
    steps:
      - uses: actions/checkout@v4
      - run: npm ci && npm test
```

### Runner Group Strategy

```
Ephemeral runners (recommended):
  - Fresh environment for each job
  - No state leakage between jobs
  - Use --ephemeral flag when registering

Runner labels:
  - self-hosted, linux, x64      (general purpose)
  - self-hosted, linux, gpu      (ML/GPU workloads)
  - self-hosted, linux, arm64    (ARM builds)
  - self-hosted, macos, m1       (macOS builds)
```

## Security Hardening

### Workflow Permissions

```yaml
# Repository-level: Settings > Actions > General > Workflow permissions
# Set to "Read repository contents and packages permissions"

# Workflow-level: Minimal permissions per job
permissions:
  contents: read          # Default: read repo contents
  packages: write         # Push to GHCR
  id-token: write         # OIDC authentication
  pull-requests: write    # Comment on PRs
  issues: write           # Create/update issues
  security-events: write  # Upload SARIF
```

### Pin Actions by SHA

```yaml
# BAD: Mutable tag (can be overwritten by attacker)
- uses: actions/checkout@v4

# GOOD: Pinned to specific SHA (immutable)
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1

# Use Dependabot to keep pinned SHAs updated
# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: github-actions
    directory: /
    schedule:
      interval: weekly
    groups:
      actions:
        patterns: ["*"]
```

### Preventing Script Injection

```yaml
# BAD: Direct interpolation of untrusted input (injection risk)
- run: echo "Title is ${{ github.event.pull_request.title }}"

# GOOD: Use environment variable (safe from injection)
- env:
    PR_TITLE: ${{ github.event.pull_request.title }}
  run: echo "Title is ${PR_TITLE}"

# BAD: Using issue body in scripts
- run: echo "${{ github.event.issue.body }}"

# GOOD: Environment variable
- env:
    ISSUE_BODY: ${{ github.event.issue.body }}
  run: echo "${ISSUE_BODY}"
```

### Security Scanning in Workflows

```yaml
security:
  runs-on: ubuntu-latest
  permissions:
    security-events: write
  steps:
    - uses: actions/checkout@v4

    # Dependency scanning
    - name: Run Trivy vulnerability scanner
      uses: aquasecurity/trivy-action@master
      with:
        # ... (condensed) ...

    # SAST
    - name: CodeQL
      uses: github/codeql-action/analyze@v3
```

## Advanced Patterns

### Conditional Job Execution

```yaml
jobs:
  changes:
    runs-on: ubuntu-latest
    outputs:
      backend: ${{ steps.filter.outputs.backend }}
      frontend: ${{ steps.filter.outputs.frontend }}
    steps:
      - uses: actions/checkout@v4
      - uses: dorny/paths-filter@v3
        id: filter
        with:
          # ... (condensed) ...
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: cd packages/web && npm test
```

### Service Containers

```yaml
jobs:
  integration-test:
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:16
        env:
          POSTGRES_USER: test
          POSTGRES_PASSWORD: test
          POSTGRES_DB: testdb
        ports:
          # ... (condensed) ...
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npm run test:integration
```

### Release Automation

```yaml
name: Release

on:
  push:
    tags:
      - 'v*'

jobs:
  release:
    runs-on: ubuntu-latest
    permissions:
      # ... (condensed) ...
          generate_release_notes: true
          files: |
            dist/*.tar.gz
            dist/*.zip
```

## Workflow Optimization Tips

```
1. Use concurrency groups to cancel redundant runs
2. Cache dependencies aggressively (npm, pip, Docker layers)
3. Run independent jobs in parallel
4. Use matrix builds for cross-platform testing
5. Use path filters to skip irrelevant workflows
6. Set timeout-minutes on every job
7. Use fail-fast: false in matrix to get all results
8. Use composite actions for shared setup steps
9. Use artifacts to pass data between jobs (not caches)
10. Minimize steps that need secrets (reduce exposure)
```

## Production Checklist

```
[ ] All workflows have timeout-minutes set
[ ] Concurrency groups prevent duplicate runs
[ ] Actions pinned by SHA (not mutable tags)
[ ] Minimal permissions declared per workflow and job
[ ] Secrets use OIDC where possible (no long-lived credentials)
[ ] Untrusted inputs use environment variables (no direct interpolation)
[ ] Caching configured for dependencies and Docker layers
[ ] Reusable workflows for common patterns
[ ] Environment protection rules for production
[ ] Dependabot configured for action updates
[ ] Security scanning integrated (dependencies, secrets, SAST)
[ ] Branch protection requires CI to pass
[ ] Workflow files are reviewed in PRs
```

## When to Use

**Use this skill when:**
- Designing or implementing github actions solutions
- Reviewing or improving existing github actions approaches
- Making architectural or implementation decisions about github actions
- Learning github actions patterns and best practices
- Troubleshooting github actions-related issues

**Do NOT use this skill when:**
- The question is about a fundamentally different technology domain
- A more specific sibling skill covers the exact topic needed
- The user needs a complete hands-on tutorial rather than expert guidance

## Output Format

```markdown
# Github Actions Analysis

## Context Assessment
[Situation summary and constraints]

## Recommended Approach
[Primary recommendation with rationale]

## Implementation Steps
1. [Step with specific details]
2. [Step with specific details]
3. [Step with specific details]

## Trade-offs and Considerations
- [Key trade-off 1]
- [Key trade-off 2]

## Next Steps
- [Immediate action item]
- [Follow-up action item]
```

## Example

**Input:** "Help me implement github actions for a medium-scale production application"

**Output:** A structured analysis covering current state assessment, recommended github actions approach with specific patterns, implementation roadmap with milestones, and risk mitigation strategies tailored to the application scale and constraints.

## Edge Cases

- **Legacy system integration:** When github actions must coexist with legacy approaches, provide a gradual migration path rather than a complete rewrite
- **Scale mismatch:** When the solution complexity exceeds the project scale, recommend a simpler approach and note when to revisit
- **Team skill gaps:** When the team lacks experience with the recommended approach, include learning resources and simpler alternatives
- **Conflicting requirements:** When constraints conflict (e.g., performance vs. maintainability), explicitly state the trade-off and recommend based on stated priorities

