Github Actions
GitHub Actions workflows for CI/CD automation including reusable workflows, matrix builds, and deployment pipelines
You are an expert in GitHub Actions for continuous integration and deployment.
## Key Points
- uses: actions/cache@v4
- uses: aws-actions/configure-aws-credentials@v4
- Pin action versions to full commit SHAs for security, not just tags: `uses: actions/checkout@abcdef1234567890`.
- Set minimal `permissions` at the workflow or job level; default to `contents: read`.
- Use `concurrency` groups to prevent redundant workflow runs on rapid pushes.
- Cache dependencies aggressively with `actions/cache` or built-in setup action caching.
- Use GitHub Environments with protection rules (required reviewers, wait timers) for production deploys.
- Keep secrets out of logs; mask values with `::add-mask::`.
- Use `workflow_dispatch` inputs for manual trigger parameters.
- Split large workflows into reusable workflows for maintainability.
- Use `timeout-minutes` on jobs to prevent hung runners from burning minutes.
- Prefer `npm ci` over `npm install` in CI for reproducible builds.
## Quick Example
```yaml
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
```skilldb get cicd-patterns-skills/Github ActionsFull skill: 221 linesGitHub Actions — CI/CD
You are an expert in GitHub Actions for continuous integration and deployment.
Overview
GitHub Actions is a CI/CD platform built into GitHub that uses YAML workflow files in .github/workflows/. Workflows are triggered by events (push, pull_request, schedule, workflow_dispatch) and run jobs on GitHub-hosted or self-hosted runners. Each job contains steps that execute shell commands or reference reusable actions.
Setup & Configuration
Workflow files live in .github/workflows/ and must be valid YAML with the .yml or .yaml extension.
Basic workflow structure:
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
contents: read
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 test
Environment and secrets configuration in repository Settings > Secrets and variables > Actions. Reference secrets with ${{ secrets.SECRET_NAME }}.
Core Patterns
Matrix Builds
Run the same job across multiple configurations:
jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node-version: [18, 20, 22]
os: [ubuntu-latest, windows-latest]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm test
Reusable Workflows
Define a callable workflow:
# .github/workflows/deploy-reusable.yml
on:
workflow_call:
inputs:
environment:
required: true
type: string
secrets:
deploy-key:
required: true
jobs:
deploy:
runs-on: ubuntu-latest
environment: ${{ inputs.environment }}
steps:
- uses: actions/checkout@v4
- run: ./deploy.sh
env:
DEPLOY_KEY: ${{ secrets.deploy-key }}
Call it from another workflow:
jobs:
deploy-staging:
uses: ./.github/workflows/deploy-reusable.yml
with:
environment: staging
secrets:
deploy-key: ${{ secrets.STAGING_DEPLOY_KEY }}
Caching Dependencies
- uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
Concurrency Control
Prevent duplicate runs and enable deployment queuing:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
Conditional Jobs and Steps
jobs:
deploy:
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
needs: [build, test]
runs-on: ubuntu-latest
steps:
- run: echo "Deploying"
Job Outputs
Pass data between jobs:
jobs:
version:
runs-on: ubuntu-latest
outputs:
tag: ${{ steps.get_tag.outputs.tag }}
steps:
- id: get_tag
run: echo "tag=v1.2.3" >> "$GITHUB_OUTPUT"
deploy:
needs: version
runs-on: ubuntu-latest
steps:
- run: echo "Deploying ${{ needs.version.outputs.tag }}"
OIDC for Cloud Authentication
permissions:
id-token: write
contents: read
steps:
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsRole
aws-region: us-east-1
Core Philosophy
GitHub Actions succeeds because it meets developers where they already are — inside GitHub. The tight integration with pull requests, issues, releases, and the repository itself means CI/CD is not a separate system to configure and maintain but a natural extension of the development workflow. This proximity is also its greatest risk: the ease of adding workflows can lead to sprawling, duplicated YAML that no one fully understands. Treating workflows as code — with the same standards for modularity, testing, and review — is essential as a repository's automation grows.
Security in GitHub Actions requires explicit, intentional configuration because the defaults are permissive. The GITHUB_TOKEN can have write access to your repository, actions from the marketplace run arbitrary code in your CI environment, and secrets are available to any workflow triggered by a push. The principle of least privilege must be applied at every layer: set minimal permissions on each job, pin actions to commit SHAs (not mutable tags), scope secrets to environments with protection rules, and use OIDC instead of long-lived cloud credentials. Every default you do not override is a security decision you made implicitly.
Reusable workflows and composite actions are how GitHub Actions scales beyond a single repository. When ten repositories need the same deploy pipeline, copying the workflow YAML into each one creates a maintenance nightmare where security patches and improvements must be applied ten times. Extracting shared logic into reusable workflows (called with workflow_call) or composite actions (published in a shared repository) centralizes the logic so updates propagate automatically. The investment in building these abstractions pays off the moment the second repository needs the same pipeline.
Anti-Patterns
-
Write-all permissions by default. Not setting
permissionson workflows means jobs may run with overly broad token scopes. Always declare the minimum permissions needed at the workflow or job level, starting withcontents: readand adding only what is required. -
Tag-pinned marketplace actions. Using
actions/checkout@v4instead of a full commit SHA means a compromised or force-pushed tag could inject malicious code into your pipeline. Pin to the immutable commit SHA for any action that touches secrets or deploys to production. -
Duplicated workflow YAML across repositories. Copy-pasting the same CI workflow into every repository means a bug fix or security update must be applied N times. Extract shared logic into reusable workflows or composite actions in a central repository.
-
Secrets in fork-triggered workflows. Pull request workflows from forks do not have access to repository secrets. Designing a workflow that silently skips critical steps when secrets are missing (rather than failing explicitly) produces false confidence in fork PRs.
-
No concurrency control on busy branches. Without
concurrencygroups andcancel-in-progress, rapid pushes to a branch queue multiple redundant workflow runs that waste compute and delay results. Set concurrency groups on every workflow triggered by push or pull_request events.
Best Practices
- Pin action versions to full commit SHAs for security, not just tags:
uses: actions/checkout@abcdef1234567890. - Set minimal
permissionsat the workflow or job level; default tocontents: read. - Use
concurrencygroups to prevent redundant workflow runs on rapid pushes. - Cache dependencies aggressively with
actions/cacheor built-in setup action caching. - Use GitHub Environments with protection rules (required reviewers, wait timers) for production deploys.
- Keep secrets out of logs; mask values with
::add-mask::. - Use
workflow_dispatchinputs for manual trigger parameters. - Split large workflows into reusable workflows for maintainability.
- Use
timeout-minuteson jobs to prevent hung runners from burning minutes. - Prefer
npm ciovernpm installin CI for reproducible builds.
Common Pitfalls
- Forgetting
permissionsleads to overly broad defaultwrite-alltokens in public repos (or restricted tokens in newer repos). - Using
actions/checkoutwithoutfetch-depth: 0when you need full git history (e.g., for changelog generation). - Matrix builds with
fail-fast: true(the default) cancel sibling jobs on first failure, hiding additional errors. - Secrets are not available in pull requests from forks—workflows relying on secrets will fail silently.
GITHUB_TOKENpermissions differ between push and pull_request events.- Caching the wrong paths or using unstable cache keys leads to cache misses and slow builds.
- Not using
concurrency.cancel-in-progresscauses queued runs to pile up on busy repos. - Assuming environment variables set in one step are available in the next without using
$GITHUB_ENV.
Install this skill directly: skilldb add cicd-patterns-skills
Related Skills
Artifact Management
Build artifact handling, dependency caching, container image management, and artifact registry patterns for CI/CD
Buildkite
Buildkite pipelines including dynamic pipeline generation, agent targeting, plugins, and hybrid cloud CI/CD
Circleci
CircleCI configuration including orbs, workflows, executors, and pipeline parameterization for CI/CD
Deployment Strategies
Blue/green, canary, rolling, and feature-flag deployment strategies with platform-specific implementation patterns
Environment Management
Managing secrets, environment variables, deployment environments, and configuration across CI/CD pipelines
Gitlab CI
GitLab CI/CD pipelines including multi-stage builds, includes, rules, and Auto DevOps configuration