D

GitHub Actions

GitHub Actions workflow syntax — jobs, steps, secrets, matrices and reusable workflows.

Updated 2026-09-03

On this page

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 test

Triggers

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: dist

Reusable 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: inherit

Reusable 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.yml

Lists recent runs of a workflow from the CLI, without opening the GitHub UI.

gh run view --log

Prints the full log of a run — the fastest way to grep a failure without clicking through the UI.

gh workflow run ci.yml --ref main

Manually triggers a workflow_dispatch-enabled workflow from the CLI.

Official documentation