All cheatsheets

Cheatsheets

GitHub Actions

Workflows, triggers, matrix builds, caching, secrets, permissions, and OIDC.

Visualize

GitHub Actions — the job DAG

needs: orders jobs; independent jobs (test ∥ lint) run in parallel.

buildcheckout · npm ci && buildtestnpm testlinteslint .deployvercel deploy
queuedrunningpassed

Press play: a push triggers the workflow, then jobs run by dependency order.

46 entries

Workflow basics6

.github/workflows/*.yml

Where workflow files live

name: CI

Human-readable workflow name shown in the UI

on: [push, pull_request]

Which events trigger the workflow

jobs:\n build:\n runs-on: ubuntu-latest

Declare one or more jobs

# minimal CI workflow

Checkout + Node setup + install + lint + test + build

actions/checkout@v4

Clone the repository onto the runner

Triggers8

on:\n push:\n branches: [main]\n paths: ['src/**']

Run on push, filtered by branch and path

on:\n pull_request:\n types: [opened, synchronize, reopened]

Run on pull request activity

on:\n workflow_dispatch:\n inputs: { ... }

Manual trigger with typed inputs

on:\n schedule:\n - cron: '0 6 * * 1'

Run on a cron schedule

on:\n workflow_call:\n inputs: { ... }\n secrets: { ... }

Make this workflow reusable (called by others)

on:\n release:\n types: [published]

Run when a release is published

on:\n issue_comment:\n types: [created]

ChatOps — react to issue/PR comments

pull_request vs pull_request_target

SECURITY: secrets and fork PRs

Jobs & steps7

runs-on: ubuntu-latest

Choose the runner OS/label

steps:\n - uses: actions/checkout@v4\n - run: npm ci

uses vs run — the two kinds of step

needs: [build, test]

Declare job dependencies (ordering)

outputs:\n sha: \${{ steps.meta.outputs.sha }}

Pass values from one job to another

if: \${{ github.ref == 'refs/heads/main' }}

Conditional execution + contexts

continue-on-error: true

Let a step/job fail without failing the run

timeout-minutes: 15

Cap how long a job (or step) may run

Matrix builds5

strategy:\n matrix:\n node: [18, 20, 22]

Run a job across multiple configurations

matrix:\n include: [ ... ]\n exclude: [ ... ]

Add or remove specific matrix combinations

strategy:\n fail-fast: false

Don't cancel sibling matrix jobs on first failure

matrix: \${{ fromJSON(needs.setup.outputs.matrix) }}

Dynamic matrix generated at runtime

strategy:\n max-parallel: 2

Throttle how many matrix legs run at once

Caching & artifacts5

actions/cache@v4

Cache dependencies between runs (key + restore-keys)

setup-node:\n with:\n cache: npm

Built-in dependency caching in setup-* actions

actions/upload-artifact@v4

Persist files/outputs from a run

actions/download-artifact@v4

Pull artifacts into a later job

echo "## Summary" >> "$GITHUB_STEP_SUMMARY"

Write rich Markdown to the run summary

Secrets & security5

env:\n TOKEN: \${{ secrets.MY_TOKEN }}

Read a repository/org secret

permissions:\n contents: read

Least-privilege GITHUB_TOKEN

environment:\n name: production

Environment secrets + protection rules

# NEVER echo a secret

Masking is not bulletproof — don't print secrets

permissions:\n id-token: write

OIDC — short-lived cloud creds, no stored keys

Reusable & composite5

uses: ./.github/workflows/build.yml

Call a reusable workflow

secrets: inherit

Forward all caller secrets to a reusable workflow

# action.yml -> runs.using: composite

Composite action — bundle steps into one reusable action

uses: actions/checkout@<sha>

Pin marketplace actions to a SHA (supply chain)

uses: docker/build-push-action@v6\n with: { ... }

Marketplace action with typed inputs

Control & performance5

concurrency:\n group: ci-\${{ github.ref }}\n cancel-in-progress: true

Cancel superseded runs (one per branch/PR)

environment:\n name: production\n url: ...

Deployment gates & tracking

services:\n postgres:\n image: postgres:16

Service containers (DB/cache sidecars)

runs-on: [self-hosted, linux, x64]

Self-hosted vs GitHub-hosted runners

# deploy only on main + environment

Conditional deploy job (gated to main)