Workflows live in .github/workflows/*.yml. Each one listens for triggers, runs one or more jobs, and each job runs
a sequence of steps on a runner.
Minimal Workflow
name: CI
on:
push:
branches: [main]
pull_request:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- run: npm testTriggers
on:
push:
branches: [main]
paths: ['src/**']
pull_request:
schedule:
- cron: '0 3 * * *'
workflow_dispatch:
inputs:
environment:
type: choice
options: [staging, production]workflow_dispatch
Adds a manual "Run workflow" button in the GitHub UI, with typed inputs — the standard way to expose an on-demand deploy or maintenance job without needing a push to trigger it.
Jobs & Dependencies
jobs:
build:
runs-on: ubuntu-latest
steps: [...]
deploy:
needs: build
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps: [...]Secrets & Environments
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- run: ./deploy.sh
env:
API_TOKEN: ${{ secrets.API_TOKEN }}Environment protection rules
A GitHub environment can require manual approval or restrict which branches can deploy to it — the standard
guardrail for a production deploy job, on top of scoping which secrets that job can even see.
Matrix Builds
jobs:
test:
strategy:
matrix:
node: [18, 20, 22]
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}Caching
- uses: actions/cache@v4
with:
path: ~/.npm
key: npm-${{ hashFiles('package-lock.json') }}
restore-keys: npm-Artifacts
- uses: actions/upload-artifact@v4
with:
name: dist
path: dist/
# in a later job:
- uses: actions/download-artifact@v4
with:
name: distReusable Workflows
# .github/workflows/deploy.yml
on:
workflow_call:
inputs:
environment:
required: true
type: string
secrets:
API_TOKEN:
required: true
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- run: ./deploy.sh --env ${{ inputs.environment }}
env:
API_TOKEN: ${{ secrets.API_TOKEN }}# calling workflow
jobs:
deploy-staging:
uses: ./.github/workflows/deploy.yml
with:
environment: staging
secrets: inheritReusable workflow vs. composite action
A reusable workflow (workflow_call) is called with uses: at the job level and can define its own jobs, runners
and secrets. A composite action is called with uses: at the step level, runs inside the calling job's runner,
and is better suited to a small, shared sequence of steps rather than a whole job.
Debugging
gh run list --workflow=ci.ymlLists recent runs of a workflow from the CLI, without opening the GitHub UI.
gh run view --logPrints the full log of a run — the fastest way to grep a failure without clicking through the UI.
gh workflow run ci.yml --ref mainManually triggers a workflow_dispatch-enabled workflow from the CLI.