Skip to main content

Verwenden von Parallelität, Ausdrücken und einer Testmatrix

Verwenden erweiterter GitHub Actions-Features für Continuous Integration (CI).

Übersicht über das Beispiel

In diesem Artikel werden anhand eines Beispielworkflows einige der wichtigsten CI-Features von GitHub Actions vorgestellt. Wenn dieser Workflow ausgelöst wird, testet er deinen Code mithilfe einer Matrix von Testkombinationen mit npm test.

Das folgende Diagramm zeigt die allgemeinen Schritte im Workflow und wie sie im Auftrag ausgeführt werden:

Übersichtsdiagramm der Workflowschritte

In diesem Beispiel verwendete Features

Der Beispielworkflow veranschaulicht die folgenden Möglichkeiten von GitHub Actions:

FeatureImplementierung
Manuelles Ausführen eines Workflows auf der Benutzeroberfläche:workflow_dispatch

Beispielworkflow

Der folgende Workflow wurde von dem Docs Engineering-Team für GitHub erstellt. Um die neueste Version dieser Datei im Repository github/docs zu überprüfen, siehe test.yml.

Hinweis: Jede Zeile dieses Workflows wird im nächsten Abschnitt unter Grundlegendes zum Beispiel erläutert.

YAML
name: Node.js Tests

# **What it does**: Runs our tests.
# **Why we have it**: We want our tests to pass before merging code.
# **Who does it impact**: Docs engineering, open-source engineering contributors.

on:
  workflow_dispatch:
  pull_request:
  push:
    branches:
      - main

permissions:
  contents: read
  # Needed for the 'trilom/file-changes-action' action
  pull-requests: read

# This allows a subsequently queued workflow run to interrupt previous runs
concurrency:
  group: '${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}'
  cancel-in-progress: true

jobs:
  test:
    # Run on self-hosted if the private repo or ubuntu-latest if the public repo
    # See pull # 17442 in the private repo for context
    runs-on: ${{ fromJSON('["ubuntu-latest", "self-hosted"]')[github.repository == 'github/docs-internal'] }}
    timeout-minutes: 60
    strategy:
      fail-fast: false
      matrix:
        # The same array lives in test-windows.yml, so make any updates there too.
        test-group:
          [
            content,
            graphql,
            meta,
            rendering,
            routing,
            unit,
            linting,
            translations,
          ]
    steps:
      # Each of these ifs needs to be repeated at each step to make sure the required check still runs
      # Even if if doesn't do anything
      - name: Check out repo
        uses: actions/checkout@v3
        with:
          # Not all test suites need the LFS files. So instead, we opt to
          # NOT clone them initially and instead, include them manually
          # only for the test groups that we know need the files.
          lfs: ${{ matrix.test-group == 'content' }}
          # Enables cloning the Early Access repo later with the relevant PAT
          persist-credentials: 'false'

      - name: Figure out which docs-early-access branch to checkout, if internal repo
        if: ${{ github.repository == 'github/docs-internal' }}
        id: check-early-access
        uses: actions/github-script@v6
        env:
          BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
        with:
          github-token: ${{ secrets.DOCUBOT_REPO_PAT }}
          result-encoding: string
          script: |
            // If being run from a PR, this becomes 'my-cool-branch'.
            // If run on main, with the `workflow_dispatch` action for
            // example, the value becomes 'main'.
            const { BRANCH_NAME } = process.env
            try {
              const response = await github.repos.getBranch({
                owner: 'github',
                repo: 'docs-early-access',
                BRANCH_NAME,
              })
              console.log(`Using docs-early-access branch called '${BRANCH_NAME}'.`)
              return BRANCH_NAME
            } catch (err) {
              if (err.status === 404) {
                console.log(`There is no docs-early-access branch called '${BRANCH_NAME}' so checking out 'main' instead.`)
                return 'main'
              }
              throw err
            }

      - name: Check out docs-early-access too, if internal repo
        if: ${{ github.repository == 'github/docs-internal' }}
        uses: actions/checkout@v3
        with:
          repository: github/docs-early-access
          token: ${{ secrets.DOCUBOT_REPO_PAT }}
          path: docs-early-access
          ref: ${{ steps.check-early-access.outputs.result }}

      - name: Merge docs-early-access repo's folders
        if: ${{ github.repository == 'github/docs-internal' }}
        run: |
          mv docs-early-access/assets assets/images/early-access
          mv docs-early-access/content content/early-access
          mv docs-early-access/data data/early-access
          rm -r docs-early-access

      # This is necessary when LFS files where cloned but does nothing
      # if actions/checkout was run with `lfs:false`.
      - name: Checkout LFS objects
        run: git lfs checkout

      - name: Gather files changed
        uses: trilom/file-changes-action@a6ca26c14274c33b15e6499323aac178af06ad4b
        id: get_diff_files
        with:
          # So that `steps.get_diff_files.outputs.files` becomes
          # a string like `foo.js path/bar.md`
          output: ' '

      - name: Insight into changed files
        run: |

          # Must to do this because the list of files can be HUGE. Especially
          # in a repo-sync when there are lots of translation files involved.
          echo "${{ steps.get_diff_files.outputs.files }}" > get_diff_files.txt

      - name: Setup node
        uses: actions/setup-node@v3
        with:
          node-version: 16.14.x
          cache: npm

      - name: Install dependencies
        run: npm ci

      - name: Cache nextjs build
        uses: actions/cache@v3
        with:
          path: .next/cache
          key: ${{ runner.os }}-nextjs-${{ hashFiles('package*.json') }}

      - name: Run build script
        run: npm run build

      - name: Run tests
        env:
          DIFF_FILE: get_diff_files.txt
          CHANGELOG_CACHE_FILE_PATH: tests/fixtures/changelog-feed.json
        run: npm test -- tests/${{ matrix.test-group }}/

Grundlegendes zum Beispiel

In der folgenden Tabelle wird erläutert, wie jedes dieser Features beim Erstellen eines GitHub Actions-Workflows verwendet wird.

Code Erklärung
YAML
name: Node.js Tests

Der Name des Workflows, der auf der Registerkarte „Aktionen“ im GitHub-Repository angezeigt wird.

YAML
on:

Mit dem on-Schlüsselwort kannst du die bei Ausführung des Workflows ausgelösten Ereignisse definieren. Du kannst hier mehrere Ereignisse definieren. Weitere Informationen findest du unter Auslösen eines Workflows.

YAML
  workflow_dispatch:

Füge das workflow_dispatch-Ereignis hinzu, wenn du diesen Workflow manuell auf der Benutzeroberfläche ausführen möchtest. Weitere Informationen findest du unter workflow_dispatch.

YAML
  pull_request:

Füge das pull_request-Ereignis hinzu, sodass der Workflow jedes Mal automatisch ausgeführt wird, wenn ein Pull Request erstellt oder aktualisiert wird. Weitere Informationen findest du unter pull_request.

YAML
  push:
    branches:
      - main

Füge das push-Ereignis hinzu, sodass der Workflow jedes Mal automatisch ausgeführt wird, wenn ein Commit an einen Branch gepusht wird, der mit dem Filter main übereinstimmt. Weitere Informationen findest du unter push.

YAML
permissions:
  contents: read
  pull-requests: read

Ändert die für GITHUB_TOKEN gewährten Standardberechtigungen. Dies variiert je nach den Anforderungen deines Workflows. Weitere Informationen findest du unter Zuweisen von Berechtigungen zu Aufträgen.

YAML
concurrency:
  group: '${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}'

Erstellt eine Parallelitätsgruppe für bestimmte Ereignisse und verwendet den ||-Operator zum Definieren von Fallbackwerten. Weitere Informationen findest du unter Verwenden von Parallelität.

YAML
  cancel-in-progress: true

Bricht alle derzeit ausgeführten Aufträge oder Workflows in derselben Parallelitätsgruppe ab.

YAML
jobs:

Gruppiert alle in der Workflowdatei ausgeführten Aufträge.

YAML
  test:

Definiert einen Auftrag mit der ID test, die innerhalb des jobs-Schlüssels gespeichert ist.

YAML
    runs-on: ${{ fromJSON('["ubuntu-latest", "self-hosted"]')[github.repository == 'github/docs-internal'] }}

Konfiguriert den Auftrag abhängig von dem Repository, das den Workflow ausführt, zur Ausführung auf einem von GitHub gehosteten oder selbstgehosteten Runner. In diesem Beispiel wird der Auftrag auf einem selbstgehosteten Runner ausgeführt, wenn das Repository mit docs-internal benannt ist und sich innerhalb der github-Organisation befindet. Wenn das Repository nicht mit diesem Pfad übereinstimmt, wird er auf einem von GitHub gehosteten ubuntu-latest-Runner ausgeführt. Weitere Informationen zu diesen Optionen findest du unter Auswählen des Runners für einen Auftrag.

YAML
    timeout-minutes: 60

Legt die maximale Anzahl von Minuten fest, die der Auftrag ausgeführt werden kann, bevor er automatisch abgebrochen wird. Weitere Informationen findest du unter timeout-minutes.

YAML
    strategy:
In diesem Abschnitt wird die Buildmatrix für deine Aufträge definiert.
YAML
      fail-fast: false

Einstellung fail-fast bis false verhindert, dass GitHub alle in Bearbeitung befindliche Aufträge abbricht, wenn ein Matrixauftrag fehlschlägt.

YAML
      matrix:
        test-group:
          [
            content,
            graphql,
            meta,
            rendering,
            routing,
            unit,
            linting,
            translations,
          ]

Erstellt eine Matrix namens test-group mit einem Array von Testgruppen. Diese Werte entsprechen den Namen von Testgruppen, die von npm test ausgeführt werden.

YAML
    steps:

Gruppiert alle Schritte, die als Teil des test-Auftrags ausgeführt werden. Jeder Auftrag in einem Workflow verfügt über einen eigenen steps-Abschnitt.

YAML
      - name: Check out repo
        uses: actions/checkout@v3
        with:
          lfs: ${{ matrix.test-group == 'content' }}
          persist-credentials: 'false'

Das uses-Schlüsselwort weist den Auftrag an, die Aktion mit dem Namen actions/checkout abzurufen. Mit dieser Aktion wird dein Repository ausgecheckt und in den Runner heruntergeladen. Dadurch kannst du Aktionen für deinen Code ausführen (z. B. Testtools). Du musst die Auscheckaktion jedes Mal verwenden, wenn dein Workflow mit dem Code des Repositorys ausgeführt wird, oder du eine im Repository definierte Aktion verwendest. Einige zusätzliche Optionen werden der Aktion mithilfe des with-Schlüssels zur Verfügung gestellt.

YAML
      - name: Figure out which docs-early-access branch to checkout, if internal repo
        if: ${{ github.repository == 'github/docs-internal' }}
        id: check-early-access
        uses: actions/github-script@v6
        env:
          BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
        with:
          github-token: ${{ secrets.DOCUBOT_REPO_PAT }}
          result-encoding: string
          script: |
            // If being run from a PR, this becomes 'my-cool-branch'.
            // If run on main, with the `workflow_dispatch` action for
            // example, the value becomes 'main'.
            const { BRANCH_NAME } = process.env
            try {
              const response = await github.repos.getBranch({
                owner: 'github',
                repo: 'docs-early-access',
                BRANCH_NAME,
              })
              console.log(`Using docs-early-access branch called '${BRANCH_NAME}'.`)
              return BRANCH_NAME
            } catch (err) {
              if (err.status === 404) {
                console.log(`There is no docs-early-access branch called '${BRANCH_NAME}' so checking out 'main' instead.`)
                return 'main'
              }
              throw err
            }

Wenn das aktuelle Repository das Repository github/docs-internal ist, wird in diesem Schritt Aktion actions/github-script verwendet, um ein Skript auszuführen, mit dem überprüft wird, ob ein Branch namens docs-early-access aufgerufen wird.

YAML
      - name: Check out docs-early-access too, if internal repo
        if: ${{ github.repository == 'github/docs-internal' }}
        uses: actions/checkout@v3
        with:
          repository: github/docs-early-access
          token: ${{ secrets.DOCUBOT_REPO_PAT }}
          path: docs-early-access
          ref: ${{ steps.check-early-access.outputs.result }}

Wenn das aktuelle Repository das Repository github/docs-internal ist, wird in diesem Schritt der Branch aus dem github/docs-early-access überprüft, der im vorherigen Schritt identifiziert wurde.

YAML
      - name: Merge docs-early-access repo's folders
        if: ${{ github.repository == 'github/docs-internal' }}
        run: |
          mv docs-early-access/assets assets/images/early-access
          mv docs-early-access/content content/early-access
          mv docs-early-access/data data/early-access
          rm -r docs-early-access

Wenn das aktuelle Repository das Repository github/docs-internal ist, wird in diesem Schritt das Schlüsselwort run verwendet, um Shellbefehle auszuführen, mit denen die Ordner von Repository docs-early-access in die Ordner des Hauptrepositorys verschoben werden.

YAML
      - name: Checkout LFS objects
        run: git lfs checkout

In diesem Schritt wird ein Befehl zum Auschecken von LFS-Objekten aus dem Repository ausgeführt.

YAML
      - name: Gather files changed
        uses: trilom/file-changes-action@a6ca26c14274c33b15e6499323aac178af06ad4b
        id: get_diff_files
        with:
          # So that `steps.get_diff_files.outputs.files` becomes
          # a string like `foo.js path/bar.md`
          output: ' '

In diesem Schritt wird die Aktion trilom/file-changes-action verwendet, um die Dateien im Pull Request zu sammeln, sodass sie im nächsten Schritt analysiert werden können. Dieses Beispiel wird mit a6ca26c14274c33b15e6499323aac178af06ad4b-SHA einer bestimmten Version der Aktion angeheftet.

YAML
      - name: Insight into changed files
        run: |
          echo "${{ steps.get_diff_files.outputs.files }}" > get_diff_files.txt

In diesem Schritt wird ein Shellbefehl ausgeführt, der anhand einer Ausgabe aus dem vorherigen Schritt eine Datei erstellt, die die Liste der im Pull Request geänderten Dateien enthält.

YAML
      - name: Setup node
        uses: actions/setup-node@v3
        with:
          node-version: 16.14.x
          cache: npm

In diesem Schritt wird mit der Aktion actions/setup-node die angegebene Version des node-Softwarepakets auf dem Runner installiert. Dadurch erhältst du Zugriff auf den npm-Befehl.

YAML
      - name: Install dependencies
        run: npm ci

In diesem Schritt wird der Shellbefehl npm ci ausgeführt, um die npm-Softwarepakete für das Projekt zu installieren.

YAML
      - name: Cache nextjs build
        uses: actions/cache@v3
        with:
          path: .next/cache
          key: ${{ runner.os }}-nextjs-${{ hashFiles('package*.json') }}

In diesem Schritt wird die Aktion actions/cache verwendet, um den Next.js-Build zwischenzuspeichern, sodass der Workflow versucht, einen Cache des Builds abzurufen, statt ihn jedes Mal von Grund auf neu zu erstellen. Weitere Informationen findest du unter Zwischenspeichern von Abhängigkeiten zum Beschleunigen von Workflows.

YAML
      - name: Run build script
        run: npm run build

In diesem Schritt wird das Buildskript ausgeführt.

YAML
      - name: Run tests
        env:
          DIFF_FILE: get_diff_files.txt
          CHANGELOG_CACHE_FILE_PATH: tests/fixtures/changelog-feed.json
        run: npm test -- tests/${{ matrix.test-group }}/

In diesem Schritt werden die Tests mithilfe von npm test ausgeführt, und die Testmatrix stellt für jeden Auftrag in der Matrix einen anderen Wert für ${{ matrix.test-group }} bereit. Dabei wird anhand der Umgebungsvariable DIFF_FILE festgestellt, welche Dateien geändert wurden, und die Umgebungsvariable für die CHANGELOG_CACHE_FILE_PATH Changelog-Cachedatei verwendet.

Nächste Schritte