Elke GitHub Actions-workflow die je hebt geschreven is een YAML-bestand. Alles — de triggers, de jobs, de steps, de omgevingsvariabelen, de matrix strategy — is uitgedrukt in YAML. Wat betekent dat YAML diepgaand begrijpen niet alleen academisch is: het is het verschil tussen een CI die betrouwbaar werkt en een CI die mysterieus kapotgaat op vrijdagmiddag om 16:00 wanneer je een release probeert te shippen.

GitHub Actions heeft uitstekende workflow-syntaxisdocumentatie, maar die richt zich op de Actions-functies in plaats van de onderliggende YAML-mechanismen. Dit artikel behandelt beide — de workflowstructuur en de YAML-specifieke patronen (en valkuilen) die CI-configs maken of breken.

Workflowstructuur: de sleutels op het hoogste niveau

Een GitHub Actions-workflowbestand bevindt zich in .github/workflows/ en heeft drie verplichte sleutels op het hoogste niveau. Het officiële workflowoverzicht legt uit waar het bestand in je repository staat en hoe GitHub het ontdekt:

yaml
name: CI Pipeline          # optional but shown in the Actions UI

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

jobs:                      # the actual work
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci && npm test
Let op on als YAML-boolean: In YAML 1.1 wordt het blote woord on geïnterpreteerd als boolean true. GitHub's parser verwerkt dit correct, maar als je ooit ziet dat je workflowbestand wordt gemarkeerd door een generieke YAML-linter, is dit de reden. De veilige vorm is het te quoteren: "on": — hoewel GitHub Actions zelf de aanhalingstekens niet vereist.

Triggers: de on:-sleutel

De on:-sleutel bepaalt wanneer je workflow wordt uitgevoerd. De volledige lijst met triggergebeurtenissen dekt alles van pull request-reviews tot repository dispatches. Hier zijn de meest voorkomende patronen:

yaml
on:
  # Push to specific branches
  push:
    branches:
      - main
      - "release/**"       # glob pattern — quotes needed for /**
    paths-ignore:
      - "docs/**"
      - "*.md"

  # PRs targeting main
  pull_request:
    branches: [main]
    types: [opened, synchronize, reopened]

  # Scheduled (cron syntax)
  schedule:
    - cron: "0 2 * * 1"   # every Monday at 2am UTC

  # Manual trigger with input
  workflow_dispatch:
    inputs:
      environment:
        description: "Target environment"
        required: true
        default: staging
        type: choice
        options: [staging, production]

Meerregelige run:-commando's en YAML-blokscalars

De run:-sleutel is waar YAML's literal block scalar (|) zijn nut bewijst — zie de yaml-multiline.info-referentie voor elke variatie van blok- en folded-scalars. Zonder dit zou je commando's moeten koppelen met && op één regel, wat onleesbaar is:

yaml
steps:
  # Single line — works but hard to read
  - name: Build
    run: npm ci && npm run lint && npm run test && npm run build

  # Multi-line with literal block scalar — much better
  - name: Build
    run: |
      npm ci
      npm run lint
      npm run test -- --coverage
      npm run build

  # Folded scalar (>) joins lines with spaces — NOT what you want for shell
  # This would run as a single command with spaces where the newlines are:
  - name: Wrong for shell
    run: >
      npm ci
      npm test
Gebruik altijd | voor meerregelige shell-scripts, nooit >. De folded scalar vouwt newlines samen tot spaties, wat betekent dat je shell-commando's worden samengevoegd tot één lange string. Dat veroorzaakt cryptische fouten. De literal block scalar | behoudt newlines exact, zodat elke regel als een apart shell-commando wordt uitgevoerd.

Omgevingsvariabelen en geheimen

Omgevingsvariabelen in GitHub Actions gebruiken YAML in combinatie met de Actions-context- en expressiesyntaxis. Hier is hoe ze samenkomen:

yaml
env:
  NODE_ENV: production         # workflow-level env var
  API_VERSION: "2.1"          # quoted to force string type

jobs:
  deploy:
    runs-on: ubuntu-latest
    env:
      DEPLOY_ENV: staging      # job-level env var (overrides workflow-level)

    steps:
      - name: Deploy to staging
        env:
          API_KEY: ${{ secrets.STAGING_API_KEY }}     # step-level, from secret
          DATABASE_URL: ${{ vars.STAGING_DB_URL }}    # step-level, from variable
        run: |
          echo "Deploying to $DEPLOY_ENV"
          ./scripts/deploy.sh

De expressiesyntaxis ${{ }} is GitHub Actions' eigen sjabloontaal bovenop YAML. Het wordt in sommige contexten geëvalueerd vóór het parsen van YAML, wat onverwacht kan interageren met YAML's eigen quoting-regels. Bij twijfel, omsluit waarden die beginnen met ${{ met dubbele aanhalingstekens.

Matrix strategy: bouwen over meerdere configuraties

De matrix strategy is een van de krachtigste functies van GitHub Actions — en het is volledig uitgedrukt in YAML. Het voert je job één keer uit voor elke combinatie van matrixwaarden:

yaml
jobs:
  test:
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false         # don't cancel other matrix jobs on failure
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        node: ["18", "20", "22"]
        exclude:
          - os: windows-latest
            node: "18"         # skip this combination

    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node }}
      - run: npm ci && npm test

Deze enkele jobdefinitie produceert tot 9 parallelle uitvoeringen (3 OS × 3 Node-versies, minus 1 uitgeslotene). De YAML-matrixwaarden zijn gewone arrays — het enige om op te letten is dat versienummers zoals 18 gequoteerd moeten worden als "18" om ze als strings te bewaren. Zonder aanhalingstekens parseert YAML ze als integers en sommige Actions kunnen klagen.

Voorwaardelijke steps met if:

De if:-sleutel laat je steps overslaan op basis van voorwaarden. Het gebruikt GitHub's expressietaal, geen gewone YAML-waarden:

yaml
steps:
  - name: Run tests
    run: npm test

  - name: Upload coverage
    if: success() && github.ref == 'refs/heads/main'
    uses: codecov/codecov-action@v4

  - name: Notify on failure
    if: failure()
    uses: slackapi/slack-github-action@v1
    with:
      slack-message: "Build failed on ${{ github.ref }}"
      channel-id: "C12345ABC"
    env:
      SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}

  - name: Deploy (only on tag push)
    if: startsWith(github.ref, 'refs/tags/v')
    run: ./scripts/deploy.sh production

Herbruikbare workflows

Voor het elimineren van duplicatie over repositories heen (niet alleen binnen een bestand), ondersteunt GitHub Actions herbruikbare workflows. De aangeroepen workflow gebruikt on: workflow_call: en de aanroeper gebruikt uses: op het joblevel — niet het steplevel:

yaml
# .github/workflows/reusable-test.yml (the reusable workflow)
on:
  workflow_call:
    inputs:
      node-version:
        required: false
        type: string
        default: "20"
    secrets:
      NPM_TOKEN:
        required: true

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ inputs.node-version }}
      - run: npm ci && npm test
        env:
          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
yaml
# .github/workflows/ci.yml (the caller)
on: [push, pull_request]

jobs:
  run-tests:
    uses: my-org/shared-workflows/.github/workflows/reusable-test.yml@main
    with:
      node-version: "22"
    secrets:
      NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

De meest voorkomende YAML-fouten in GitHub Actions

  • Verkeerde inspringing bij steps. Steps moeten consistent worden ingesprongen onder steps: met spaties. Eén spatie extra of minder verplaatst een step buiten zijn job.
  • Ongenoteerde versienummers. node-version: 20 geeft een integer door; sommige actions verwachten een string. Gebruik node-version: "20" om zeker te zijn.
  • > gebruiken in plaats van | voor shell-scripts. Folded scalars vouwen newlines samen. Je meerregelige script wordt één lange string en mislukt.
  • Ontbrekende aanhalingstekens bij globpatronen. Patronen zoals release/** bevatten * die YAML kan interpreteren als een alias. Citeer altijd globpatronen.
  • Geheimen gebruikt in if:-voorwaarden. GitHub maskeert geheimen in logs maar staat ze niet toe in if:-expressies. Gebruik in plaats daarvan een omgevingsvariabele.
  • Vergeten fail-fast: false te zetten in matrixjobs. Standaard worden alle andere matrixjobs geannuleerd als één matrixjob mislukt. Dat wil je meestal niet bij het debuggen.

Samenvatting

GitHub Actions-workflows zijn fundamenteel YAML-bestanden — het begrijpen van YAML's literal block scalars, quoting-regels en type-coercering vertaalt zich direct naar het schrijven van betrouwbaardere CI. De meest voorkomende fouten komen voort uit inspringfouten, ongenoteerde waarden die naar het verkeerde type worden gedwongen, en het gebruik van de verkeerde meerregelige string-operator voor shell-scripts. Zorg dat die drie dingen goed zitten en de meeste workflow-debugging wordt eenvoudig. Gebruik de YAML Validator om syntaxisfouten op te vangen vóór het pushen, en de YAML Formatter om consistente inspringing af te dwingen in je workflowbestanden.