GitHub Actions

GitHub Actions workflow structure, triggers, matrix builds, secrets, and the Domus deployment pattern.

Workflow Structure

Minimal workflow — trigger, job, steps
name: CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

permissions:
  contents: read

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: echo "Hello from CI"

Every workflow lives in .github/workflows/*.yml. The filename becomes the workflow ID.

Manual trigger with inputs — workflow_dispatch
on:
  workflow_dispatch:
    inputs:
      environment:
        description: 'Target environment'
        required: true
        default: 'staging'
        type: choice
        options:
          - staging
          - production

Triggers

Common trigger patterns
on:
  push:
    branches: [main, 'release/**']
    paths:
      - 'src/**'
      - '!src/**/*.md'         # exclude markdown changes

  pull_request:
    types: [opened, synchronize, reopened]

  schedule:
    - cron: '0 6 * * 1'       # every Monday at 06:00 UTC

  repository_dispatch:
    types: [component-updated]

Path filters skip CI for docs-only changes. Save runner minutes.

Steps and Actions

Checkout, setup, build — the standard pattern
steps:
  - name: Checkout
    uses: actions/checkout@v4

  - name: Setup Node.js
    uses: actions/setup-node@v4
    with:
      node-version: '20'
      cache: 'npm'

  - name: Install and build
    run: |
      npm ci
      npm run build

  - name: Test
    run: npm test

npm ci is faster than npm install in CI — it installs from lockfile exactly, no resolution step.

Conditional step — only run on main branch
- name: Deploy to production
  if: github.ref == 'refs/heads/main'
  run: ./deploy.sh

Secrets and Environment

Using secrets — never hardcode credentials
- name: Deploy to Cloudflare
  uses: cloudflare/pages-action@v1
  with:
    apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
    accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
    projectName: domus-docs
    directory: ./build/site

Secrets are masked in logs automatically. Set them in Settings > Secrets and variables > Actions.

Environment variables — job-level and step-level
jobs:
  build:
    runs-on: ubuntu-latest
    env:
      NODE_ENV: production
    steps:
      - name: Build
        env:
          API_URL: https://api.example.com
        run: npm run build

Matrix Builds

Test across multiple versions
jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ['3.10', '3.11', '3.12']
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}
      - run: |
          pip install -r requirements.txt
          pytest

Concurrency and Caching

Prevent parallel deploys — cancel in-progress runs
concurrency:
  group: "deploy-${{ github.ref }}"
  cancel-in-progress: true
Cache dependencies — avoid reinstalling every run
- uses: actions/cache@v4
  with:
    path: ~/.cache/pip
    key: pip-${{ runner.os }}-${{ hashFiles('requirements.txt') }}
    restore-keys: |
      pip-${{ runner.os }}-

Domus Deployment Pattern

Antora hub-and-spoke — Cloudflare Pages auto-deploys from spoke push
# Spoke repos (domus-captures, etc.):
#   Push to main → Cloudflare Pages auto-builds
#   No workflow needed in spoke repos

# Hub repo (domus-docs) backup workflow:
name: Deploy to Cloudflare Pages
on:
  push:
    branches: [main]
  repository_dispatch:
    types: [component-updated]

jobs:
  build-deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      - run: npm ci
      - run: npx antora antora-playbook.yml
      - uses: cloudflare/pages-action@v1
        with:
          apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
          projectName: domus-docs
          directory: ./build/site

gh CLI for Actions

Manage workflows from the terminal
# List recent runs
gh run list --limit 5

# View a specific run with logs
gh run view 12345 --log

# Trigger a workflow manually
gh workflow run deploy.yml -f environment=staging

# Watch a running workflow in real time
gh run watch

See Also

  • CI/CD — pipeline concepts and strategies

  • gh CLI — GitHub CLI for managing Actions from the terminal