Design wechseln

GitHub Actions Matrix-Build: Parallele Tests auf mehreren Plattformen und Versionen

Letztes Jahr meldete sich ein Open-Source-Projekt bei mir: Die CI-Konfiguration war auf über 800 Zeilen angewachsen. Beim Öffnen sah man nichts als wiederholte Job-Definitionen – Node 16 auf Ubuntu, Node 16 auf Windows, Node 16 auf macOS … dann Node 18 noch einmal, Node 20 noch einmal. Einen Testbefehl ändern? Zwölf Stellen. Eine neue Version hinzufügen? Copy-Paste für fünfzehn Minuten.

In dem Moment wurde klar: Viele pflegen Multi-Version- und Multi-Plattform-Tests noch per Hand.

Die Matrix-Funktion in GitHub Actions expandiert solche Wiederholungen automatisch. Sie definieren Betriebssysteme und Laufzeitversionen – GitHub Actions führt alle Kombinationen aus. Klingt einfach, aber in der Praxis gibt es Fallstricke: explodierende Kombinationszahlen und Rechnungen, ein fehlgeschlagener Job bricht die ganze Kette ab, bestimmte Kombinationen ausschließen ist nicht immer offensichtlich. All das habe ich selbst erlebt.

Dieser Artikel beginnt mit der Matrix-Basis-Syntax und führt Schritt für Schritt zu exclude/include, fail-fast, max-parallel und dynamischer Matrix-Generierung. Am Ende stehen fünf produktionsreife Workflow-Vorlagen zum direkten Übernehmen – von Hobbyprojekten bis Enterprise.

2. Matrix-Kernkonzept: viele Jobs mit einem Klick

Die Logik ist simpel: Sie definieren Dimensionen, GitHub Actions bildet das kartesische Produkt.

Ihr Projekt soll auf Ubuntu, Windows und macOS laufen und Node.js 18, 20 und 22 unterstützen. Klassisch schreiben Sie neun Jobs mit jeweils Umgebung, Installation und Tests. Mit Matrix reicht etwa Folgendes:

strategy:
  matrix:
    os: [ubuntu-latest, windows-latest, macos-latest]
    node: [18, 20, 22]

runs-on: ${{ matrix.os }}

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

Diese wenigen Zeilen werden zu 3 × 3 = 9 parallelen Tasks. Jeder Task erhält eigene matrix.os- und matrix.node-Werte.

Das Projekt mit 800 Zeilen CI schrumpfte nach dem Matrix-Refactoring auf etwa 120 Zeilen – über 60 % weniger Code. Eine neue Version ist nur noch eine Zahl im Array, kein Copy-Paste von Job-Blöcken.

Was Matrix leistet:

  • Multi-Plattform- und Multi-Version-Kombinationen mit einem Klick
  • Automatisches Expandieren aller Konfigurationen ohne Duplikate
  • Ungültige Kombinationen per exclude ausschließen
  • Sonderfälle per include ergänzen
  • Parallelität steuern und Kosten balancieren

Was Matrix nicht löst:

  • Schlechte Tests bleiben schlecht
  • Zu viele Kombinationen → hohe Kosten – Dimensionen bewusst wählen
  • Langsame Dependency-Installation – Caching-Strategie nötig

Matrix selbst ist leicht zu verstehen; die Kunst liegt in der Anwendung im echten Projekt. Als Nächstes die Basis-Syntax.

3. Basis-Syntax: os × version als kartesisches Produkt

Matrix folgt dem kartesischen Produkt: Jede Dimension wird mit allen anderen kombiniert.

Eine Dimension mit N Werten → N Tasks

Zwei Dimensionen mit M × N Werten → M × N Tasks

Drei Dimensionen → A × B × C Tasks

Beispiel: Python-Projekt auf Linux und Windows, Python 3.9–3.12, Datenbanken PostgreSQL und MySQL:

jobs:
  test:
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest]
        python-version: ['3.9', '3.10', '3.11', '3.12']
        database: [postgresql, mysql]
    runs-on: ${{ matrix.os }}

    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}
      - name: Setup ${{ matrix.database }}
        run: |
          # Start des jeweiligen Datenbankdienstes
          if [ "${{ matrix.database }}" = "postgresql" ]; then
            docker run -d -p 5432:5432 postgres
          else
            docker run -d -p 3306:3306 mysql
          fi
        shell: bash
      - run: pip install -r requirements.txt
      - run: pytest

Ergebnis: 2 × 4 × 2 = 16 Tasks, jeweils isolierte Umgebung.

Matrix-Variablen nutzen:

  • ${{ matrix.os }} – Betriebssystem des aktuellen Tasks
  • ${{ matrix.python-version }} – Python-Version
  • ${{ matrix.database }} – Datenbanktyp

Variablen funktionieren in runs-on, steps, env usw.

Häufige Falle: Matrix löst Dependency-Installation nicht automatisch. Jeder Task ist eine frische Umgebung – Installation läuft jedes Mal neu. Bei 2 Minuten Install pro Task und 16 Tasks summiert sich die Wartezeit (bei serieller Ausführung besonders).

Zwei Hebel:

  1. Caching – pip/npm-Cache, weniger Downloads
  2. Weniger Kombinationen – exclude für unnötige Tests

Ausführlich zu Caching gibt es einen eigenen Artikel in der GitHub-Actions-Serie; hier der Fokus auf exclude/include.

4. exclude/include: Kombinationen gezielt steuern

Standardmäßig bildet Matrix alle Kombinationen. In Projekten brauchen Sie oft „diese Kombination weglassen“ oder „diese Kombination anders behandeln“.

4.1 exclude: ungültige Kombinationen

Bei einem Python-Projekt scheiterte Windows + Python 3.9 wegen einer inkompatiblen Abhängigkeit. Deployment-Ziel war Linux; Windows nur Nebensupport – der Bug lohnte sich nicht. exclude hilft:

strategy:
  matrix:
    os: [ubuntu-latest, windows-latest, macos-latest]
    python-version: ['3.9', '3.10', '3.11', '3.12']
    exclude:
      - os: windows-latest
        python-version: '3.9'
      - os: macos-latest
        python-version: '3.9'

Windows und macOS testen Python 3.9 nicht mehr. Aus 3 × 4 = 12 Tasks werden 10.

Typische exclude-Szenarien:

  1. Bekannte Inkompatibilität – Version läuft auf einem OS nicht
  2. Ressourcenlimit – wenige Self-hosted Runner
  3. Randfälle – Kombinationen, die Nutzer kaum treffen

4.2 include: Sonderkonfigurationen

include fügt Kombinationen hinzu oder ergänzt Variablen für bestimmte Fälle.

Beispiel: Coverage nur bei Python 3.12:

strategy:
  matrix:
    python-version: ['3.10', '3.11', '3.12']
    include:
      - python-version: '3.12'
        coverage: true

steps:
  - uses: actions/checkout@v4
  - uses: actions/setup-python@v5
    with:
      python-version: ${{ matrix.python-version }}
  - run: pip install -r requirements.txt
  - name: Run tests
    run: |
      if [ "${{ matrix.coverage }}" = "true" ]; then
        pytest --cov=src --cov-report=xml
      else
        pytest
      fi
    shell: bash

include kann:

  1. Neue Kombination hinzufügen
  2. Zusatzvariablen setzen – z. B. coverage: true

Typische include-Szenarien:

  1. Preview-Versionen – z. B. Python 3.13 nur auf einem OS
  2. Sonder-Env – extra Variablen für eine Kombination
  3. Seltene Fälle – gezielt statt Vollkreuzprodukt

4.3 exclude und include kombiniert

Oft beides: alle Python-3.9-Kombinationen exclude, aber Ubuntu + 3.9 minimal per include:

strategy:
  matrix:
    os: [ubuntu-latest, windows-latest]
    python-version: ['3.9', '3.10', '3.11', '3.12']
    exclude:
      - python-version: '3.9'
    include:
      - os: ubuntu-latest
        python-version: '3.9'
        minimal: true

Reihenfolge: alle Kombinationen erzeugen → exclude → include. Ergebnis: Ubuntu vier Versionen, Windows drei (ohne 3.9).

5. fail-fast: schnell abbrechen vs. vollständig debuggen

Standard: scheitert ein Matrix-Task, werden laufende andere abgebrochen (fail-fast, default true).

strategy:
  fail-fast: true  # Default, kann weggelassen werden
  matrix:
    os: [ubuntu-latest, windows-latest]
    node: [18, 20, 22]

5.1 Wann fail-fast: true (Default)

PR-Tests – schnell wissen, ob etwas kaputt ist; weitere Tasks scheitern oft aus demselben Grund.

Kostenbewusst – Free-Tier und begrenzte Self-hosted-Ressourcen.

Gewohnheit: PR mit fail-fast: true, main mit vollständiger Matrix oft false.

5.2 Wann fail-fast: false

Debug-Phase – alle fehlgeschlagenen Kombinationen sehen, nicht nur die erste.

Kompatibilitätstests – jede OS/Version-Kombination zählt.

Vollständiger Report – Status aller Kombinationen nach dem Lauf.

strategy:
  fail-fast: false
  matrix:
    os: [ubuntu-latest, windows-latest, macos-latest]
    node: [18, 20, 22]

5.3 Praxisbeispiel

Ein Projekt scheiterte nur auf Ubuntu + Node 18. Mit Default fail-fast sah man nur diesen einen Fehler. Nach Umstellung auf false: Windows + Node 18 grün, nur Ubuntu rot – Ursache war Groß-/Kleinschreibung in Pfaden.

Empfehlung: Debug mit false, stabiler Betrieb mit true für Zeit und Kosten.

6. max-parallel: Parallelität und Kosten

Matrix-Tasks laufen standardmäßig parallel (öffentliche Repos: bis 20 GitHub-hosted Runner gleichzeitig; private Free-Tier oft 2).

max-parallel begrenzt die gleichzeitige Anzahl:

6.1 Wann Parallelität begrenzen

Wenig Self-hosted-Kapazität – 4 Kerne / 8 GB: acht parallele Jobs überlasten die Maschine.

API-Rate-Limits – zu viele parallele Aufrufe → Sperre.

DB-Connection-Pool – z. B. nur zehn Verbindungen.

strategy:
  max-parallel: 4
  matrix:
    os: [ubuntu-latest, windows-latest]
    node: [18, 20, 22]

Sechs Tasks, maximal vier gleichzeitig – einer endet, der nächste startet.

6.2 Kostenbeispiel

3 OS × 4 Node-Versionen = 12 Tasks, je 10 Minuten:

Ohne Limit (genug Runner):

  • Alle parallel → ~10 Minuten Wandzeit
  • Rechenzeit gesamt: 12 × 10 = 120 Minuten

max-parallel: 4:

  • Drei Wellen → ~30 Minuten Wandzeit
  • Rechenzeit gesamt: weiter 120 Minuten

max-parallel verkürzt nicht die Gesamt-Rechenminuten, nur die Spitzenlast und Wandzeit.

Wichtig bei Self-hosted oder Peak-basierter Abrechnung: zwölf parallele DB-Verbindungen vs. vier in Wellen.

Praxis:

  • Öffentliches Repo, GitHub-hosted: meist kein max-parallel nötig
  • Privat, Free-Tier: max-parallel: 2
  • Self-hosted: nach CPU/RAM, bei 4 Kernen oft 2–4

7. Dynamische Matrix: fromJSON

Bisher statische Arrays in YAML. In Monorepos oder bei change-basierten Tests brauchen Sie dynamische Kombinationen – nur betroffene Services testen.

7.1 Zwei-Job-Workflow mit fromJSON

Keine native „dynamische Matrix“-Syntax – ein Job erzeugt JSON, der Test-Job nutzt fromJSON():

jobs:
  detect:
    runs-on: ubuntu-latest
    outputs:
      matrix: ${{ steps.set-matrix.outputs.matrix }}

    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 2

      - name: Detect changed services
        id: set-matrix
        run: |
          CHANGED_FILES=$(git diff --name-only HEAD^ HEAD)

          SERVICES="[]"
          if echo "$CHANGED_FILES" | grep -q "services/auth/"; then
            SERVICES=$(echo $SERVICES | jq '. + ["auth"]')
          fi
          if echo "$CHANGED_FILES" | grep -q "services/api/"; then
            SERVICES=$(echo $SERVICES | jq '. + ["api"]')
          fi
          if echo "$CHANGED_FILES" | grep -q "services/web/"; then
            SERVICES=$(echo $SERVICES | jq '. + ["web"]')
          fi

          if [ "$SERVICES" = "[]" ]; then
            SERVICES='["auth", "api", "web"]'
          fi

          echo "matrix={\"service\":$(echo $SERVICES)}" >> $GITHUB_OUTPUT

  test:
    needs: detect
    runs-on: ubuntu-latest
    strategy:
      matrix: ${{ fromJSON(needs.detect.outputs.matrix) }}

    steps:
      - uses: actions/checkout@v4
      - name: Test ${{ matrix.service }}
        run: |
          cd services/${{ matrix.service }}
          npm install
          npm test

Ablauf: detect ermittelt geänderte Verzeichnisse, schreibt Matrix-JSON, test parst mit fromJSON().

7.2 Typische Anwendungen

Monorepo – nur geänderte Services

Deploy nach Diff – nur geänderte Images bauen

Matrix optimieren – volle Versionstests nur bei package.json-Änderung

Fallstricke:

  1. fromJSON() nur als Wert von strategy.matrix
  2. JSON muss gültiges Matrix-Format sein, z. B. {"service": ["auth", "api"]}
  3. Leere Matrix → Workflow-Fehler; Default-Wert setzen

8. Vorlagen: 5 produktionsreife Workflows

Fünf kopierbare Templates – von Basis bis Enterprise.

8.1 Vorlage 1: Node.js Multi-Version (Basis)

name: Node.js CI

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

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        node-version: [18, 20, 22, 23]

    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
          cache: 'npm'

      - run: npm ci
      - run: npm run build --if-present
      - run: npm test

      - name: Upload coverage
        if: matrix.node-version == 22
        uses: codecov/codecov-action@v4

Punkte: npm ci für Lockfile, Coverage nur Node 22, cache: 'npm'.

8.2 Vorlage 2: Python Multi-Plattform (Mittel)

name: Python CI

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

jobs:
  test:
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        python-version: ['3.10', '3.11', '3.12']
        exclude:
          - os: windows-latest
            python-version: '3.10'

    steps:
      - uses: actions/checkout@v4

      - name: Setup Python ${{ matrix.python-version }}
        uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}
          cache: 'pip'

      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install -r requirements.txt

      - name: Run tests
        run: pytest -v

      - name: Lint check
        run: |
          pip install ruff
          ruff check .

Punkte: exclude, pip-Cache, ruff.

8.3 Vorlage 3: exclude/include (Fortgeschritten)

name: Advanced Matrix

on:
  push:
    branches: [main]

jobs:
  test:
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest, windows-latest]
        python-version: ['3.10', '3.11', '3.12']
        exclude:
          - os: windows-latest
            python-version: '3.10'
        include:
          - os: ubuntu-latest
            python-version: '3.13-dev'
            experimental: true
          - python-version: '3.12'
            coverage: true

    continue-on-error: ${{ matrix.experimental == true }}

    steps:
      - uses: actions/checkout@v4

      - name: Setup Python ${{ matrix.python-version }}
        uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}
          cache: 'pip'

      - run: pip install -r requirements.txt

      - name: Run tests
        run: |
          if [ "${{ matrix.coverage }}" = "true" ]; then
            pytest --cov=src --cov-report=xml
          else
            pytest
          fi
        shell: bash

Punkte: continue-on-error für Experimente, shell: bash für einheitliche Skripte.

8.4 Vorlage 4: Dynamische Matrix + Caching

name: Dynamic Matrix CI

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

jobs:
  detect:
    runs-on: ubuntu-latest
    outputs:
      matrix: ${{ steps.set-matrix.outputs.matrix }}

    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 2

      - name: Detect changed packages
        id: set-matrix
        run: |
          CHANGED_FILES=$(git diff --name-only HEAD^ HEAD)

          PACKAGES="[]"
          for dir in packages/*/; do
            pkg=$(basename $dir)
            if echo "$CHANGED_FILES" | grep -q "^packages/$pkg/"; then
              PACKAGES=$(echo $PACKAGES | jq ". + [\"$pkg\"]")
            fi
          done

          if [ "$PACKAGES" = "[]" ]; then
            PACKAGES='["core", "utils", "cli"]'
          fi

          echo "matrix={\"package\":$(echo $PACKAGES)}" >> $GITHUB_OUTPUT

  test:
    needs: detect
    runs-on: ubuntu-latest
    strategy:
      matrix: ${{ fromJSON(needs.detect.outputs.matrix) }}

    steps:
      - uses: actions/checkout@v4

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

      - name: Install dependencies
        run: npm ci

      - name: Build
        run: npm run build --if-present

      - name: Test ${{ matrix.package }}
        run: |
          cd packages/${{ matrix.package }}
          npm test

Punkte: fetch-depth: 2, jq, Default bei leerer Änderungsliste.

8.5 Vorlage 5: Self-hosted + max-parallel (Enterprise)

name: Enterprise CI

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

jobs:
  test:
    runs-on: [self-hosted, linux, x64]
    strategy:
      fail-fast: true
      max-parallel: 4
      matrix:
        java-version: [11, 17, 21]
        database: [postgresql, mysql]

    services:
      postgres:
        image: postgres:15
        env:
          POSTGRES_PASSWORD: postgres
        ports:
          - 5432:5432
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

      mysql:
        image: mysql:8
        env:
          MYSQL_ROOT_PASSWORD: root
        ports:
          - 3306:3306
        options: >-
          --health-cmd "mysqladmin ping"
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

    steps:
      - uses: actions/checkout@v4

      - name: Setup Java ${{ matrix.java-version }}
        uses: actions/setup-java@v4
        with:
          java-version: ${{ matrix.java-version }}
          distribution: 'temurin'
          cache: 'maven'

      - name: Run tests with ${{ matrix.database }}
        env:
          DB_TYPE: ${{ matrix.database }}
          DB_HOST: localhost
          DB_PORT: ${{ matrix.database == 'postgresql' && 5432 || 3306 }}
        run: mvn test -Dspring.profiles.active=${{ matrix.database }}

      - name: Archive test results
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: test-results-${{ matrix.java-version }}-${{ matrix.database }}
          path: target/surefire-reports

Punkte: Self-hosted-Labels, max-parallel: 4, Service-Container, if: always() für Artefakte.

9. Fallstricke und Best Practices

9.1 Falle: Kombinationsexplosion

Extremfall: 4 OS × 5 Versionen × 3 DB × 2 Cache = 120 Tasks, 45 Minuten Laufzeit, hohe Kosten.

Gegenmaßnahmen:

  • Volle Matrix nur auf main, PR mit Kernkombinationen
  • exclude für Randfälle
  • Jede Dimension hinterfragen – brauchen Sie wirklich vier OS?
on:
  pull_request:
    branches: [main]

jobs:
  test:
    strategy:
      matrix:
        os: [ubuntu-latest]
        node: [20]

9.2 Falle: fail-fast blockiert Debug

Default true bricht bei erstem Fehler ab – unvollständiges Bild.

Lösung: Debug mit false, danach wieder true. Oder:

strategy:
  fail-fast: ${{ github.event_name == 'pull_request' }}

9.3 Falle: kein Caching

Zwölf Kombinationen × 2 Minuten Install = 24 Minuten nur Installation.

- uses: actions/setup-node@v4
  with:
    node-version: ${{ matrix.node-version }}
    cache: 'npm'

9.4 Best Practices

PR klein, main voll – bedingte Matrix per fromJSON möglich (siehe Vorlagen in der Doku).

exclude für bekannte Probleme – TODO zum späteren Fix.

Caching – größter Hebel nach Kombinationszahl.

Sinnvolle Namen:

jobs:
  test:
    name: Test (${{ matrix.os }}, Node ${{ matrix.node }})
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest]
        node: [18, 20, 22]

10. Fazit

GitHub Actions Matrix ist das Werkzeug für Multi-Plattform- und Multi-Version-Tests: Dimensionen definieren, Kombinationen steuern, Parallelität begrenzen, dynamisch erzeugen.

Viele Projekte duplizieren noch CI per Hand – ein Befehl an zwölf Stellen. Matrix komprimiert Hunderte Zeilen auf Dutzende.

Kurzüberblick:

  1. Basis: matrix.os und matrix.node als kartesisches Produkt
  2. Steuerung: exclude/include
  3. Strategie: fail-fast je nach Phase
  4. Parallelität: max-parallel für Self-hosted und Kosten
  5. Dynamik: fromJSON für change-basierte Tests

Die fünf Vorlagen decken Node-Basis bis Enterprise ab. Start mit Vorlage 1, dann exclude/include, zuletzt dynamische Matrix – Schritt für Schritt.

Fragen gerne in den Kommentaren; die offizielle GitHub-Actions-Dokumentation vertieft Details. Eigene Matrix-Erfahrungen sind willkommen.

GitHub Actions Matrix für Multi-Plattform-Tests konfigurieren

Matrix-Strategie von null einrichten für automatisierte Tests über Plattformen und Versionen

⏱️ Estimated time: 30 min

  1. 1

    Step 1: Matrix-Dimensionen definieren

    strategy.matrix im Job setzen:

    ```yaml
    strategy:
    matrix:
    os: [ubuntu-latest, windows-latest]
    node: [18, 20, 22]
    ```

    Erzeugt 2 × 3 = 6 parallele Tasks.
  2. 2

    Step 2: Matrix-Variablen verwenden

    In runs-on und steps referenzieren:

    ```yaml
    runs-on: ${{ matrix.os }}
    steps:
    - uses: actions/setup-node@v4
    with:
    node-version: ${{ matrix.node }}
    ```

    Jeder Task erhält passende os- und node-Werte.
  3. 3

    Step 3: Kombinationen ausschließen (optional)

    exclude für bekannte Problemkombinationen:

    ```yaml
    strategy:
    matrix:
    os: [ubuntu-latest, windows-latest]
    node: [18, 20, 22]
    exclude:
    - os: windows-latest
    node: 18
    ```

    Windows + Node 18 entfällt → 5 Tasks.
  4. 4

    Step 4: Fehlerstrategie wählen

    fail-fast je nach Kontext:

    - PR: fail-fast: true (schnell, günstiger)
    - Debug: fail-fast: false (alle Fehler sehen)
    - main: oft fail-fast: false (vollständiger Report)

    ```yaml
    strategy:
    fail-fast: false
    matrix:
    # ...
    ```
  5. 5

    Step 5: Parallelität begrenzen (optional)

    Bei Self-hosted oder knappen Ressourcen:

    ```yaml
    strategy:
    max-parallel: 4
    matrix:
    # ...
    ```

    Maximal vier Tasks gleichzeitig – Runner entlasten.

FAQ

Gibt es ein Limit für Matrix-Kombinationen?
GitHub setzt weiche Grenzen: öffentliche Repos bis etwa 256 Tasks, private je nach Plan. Praxis: unter 20 Kombinationen bleiben, sonst lange Laufzeiten und hohe Kosten. 4 OS × 5 Versionen × 3 DB = 60 Tasks sind bereits viel.
Was ist der Default von fail-fast?
Default ist true: ein Fehler bricht andere laufende Tasks ab. Zum Debuggen false setzen; in Produktion oft true für Geschwindigkeit und Kosten.
In welcher Reihenfolge laufen exclude und include?
Erst alle Kombinationen erzeugen, dann exclude anwenden, dann include. So können Sie alle Python-3.9-Kombinationen exclude und per include nur Ubuntu + 3.9 minimal testen.
Wo darf fromJSON() bei dynamischer Matrix stehen?
Nur als Wert von strategy.matrix. JSON muss gültiges Matrix-Format sein, z. B. {"os": ["ubuntu", "windows"]}. Leere Matrix führt zum Fehler – Default-Wert vorsehen.
Verringert max-parallel die Gesamt-Rechenzeit?
Nein. Es begrenzt nur gleichzeitige Tasks. 12 Tasks × 10 Minuten = 120 Rechenminuten unabhängig von max-parallel. Nutzen: geringere Spitzenlast, Connection-Pools schonen, Self-hosted entlasten.
Teilen sich Matrix-Tasks einen Cache?
Ja. Actions-Cache ist repo-weit. cache in setup-node/setup-python aktivieren – erster Task legt an, folgende nutzen ihn.
Wie benennt man Matrix-Tasks benutzerfreundlich?
Job-name mit Matrix-Variablen:

• name: Test (${{ matrix.os }}, Node ${{ matrix.node }})

In der UI z. B. „Test (ubuntu-latest, Node 20)“ statt generischem Jobnamen.

10 Min. Lesezeit · Veröffentlicht am: 28. Apr. 2026 · Aktualisiert am: 9. Juli 2026

Kommentare

Melde dich mit GitHub an, um einen Kommentar zu hinterlassen

Easton BlogEaston Blog