Skip to main content
我们经常发布文档更新,此页面的翻译可能仍在进行中。 有关最新信息,请访问英语文档
GitHub AE 目前处于受限版。

使用并发、表达式和测试矩阵

如何使用高级 GitHub Actions 功能进行持续集成 (CI)。

示例概述

本文使用示例工作流演示 GitHub Actions 的某些主要 CI 功能。触发此工作流时,它将使用 npm test 的测试组合矩阵来测试代码。

下图显示了工作流步骤的高级视图以及它们如何在作业中运行:

工作流步骤概述图

此示例中使用的功能

示例工作流演示了 GitHub Actions 的以下功能。

功能实现
从 UI 手动运行工作流workflow_dispatch

示例工作流

GitHub Docs 工程团队创建了以下工作流。 若要查看 github/docs 存储库中此文件的最新版本,请参阅 test.yml

注意:此工作流的每一行将在下一部分“了解示例”中介绍。

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 personal access token
          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 }}/

了解示例

 下表介绍了在创建 GitHub Actions 工作流时如何使用这些功能。

代码 解释
YAML
name: Node.js Tests

将显示在 GitHub 存储库的“操作”选项卡中的工作流名称。

YAML
on:

通过 on 关键字,可以定义运行工作流时触发的事件。 可在此处定义多个事件。 有关详细信息,请参阅“触发工作流程”。

YAML
  workflow_dispatch:

如果要在 UI 中手动运行此工作流,请添加 workflow_dispatch 事件。 有关详细信息,请参阅 workflow_dispatch

YAML
  pull_request:

添加 pull_request 事件,以便每次创建或更新拉取请求时,工作流都会自动运行。 有关详细信息,请参阅 pull_request

YAML
  push:
    branches:
      - main

添加 push 事件,以便每次将提交推送到匹配筛选器 main 的分支时,工作流都会自动运行。 有关详细信息,请参阅 push

YAML
permissions:
  contents: read
  pull-requests: read

修改授予 GITHUB_TOKEN 的默认权限。 这将因工作流的需求而异。 有关详细信息,请参阅“为作业分配权限”。

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

为特定事件创建并发组,并使用 || 运算符定义回退值。 有关详细信息,请参阅“使用并发”。

YAML
  cancel-in-progress: true

取消同一并发组中任何当前正在运行的作业或工作流。

YAML
jobs:

将工作流文件中运行的所有作业组合在一起。

YAML
  test:

定义 ID 为 test 的作业,该作业存储在 jobs 键中。

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

根据运行工作流的存储库,将作业配置为在 GitHub 托管的运行器或自托管运行器上运行。 在此示例中,如果存储库名为 docs-internal 且位于 github 组织内,则作业将在自托管运行器上运行。 如果存储库与此路径不匹配,则其会在由 GitHub 托管的 ubuntu-latest 运行器上运行。 有关这些选项的详细信息,请参阅“选择作业的运行器”。

YAML
    timeout-minutes: 60

设置作业在自动取消之前运行的最大分钟数。 有关详细信息,请参阅 timeout-minutes

YAML
    strategy:
本部分定义作业的生成矩阵。
YAML
      fail-fast: false

fail-fast 设置为 false 可以防止 GitHub 在矩阵作业失败时取消所有正在进行的作业。

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

创建名为 test-group 的矩阵,其中包含测试组数组。 这些值与将由 npm test 运行的测试组的名称匹配。

YAML
    steps:

将作为 test 作业一部分运行的所有步骤组合在一起。 工作流中的每个作业都有其自己的 steps 部分。

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

uses 关键字指示作业检索名为 actions/checkout 的操作。 这是检出仓库并将其下载到运行器的操作,允许针对您的代码运行操作(例如测试工具)。 只要工作流程针对仓库的代码运行,或者您使用仓库中定义的操作,您都必须使用检出操作。 使用 with 键为操作提供了一些额外的选项。

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
            }

如果当前存储库是 github/docs-internal 存储库,此步骤将使用 actions/github-script 操作运行脚本,检查是否存在名为 docs-early-access 的分支。

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 }}

如果当前存储库是 github/docs-internal 存储库,此步骤将从上一步中标识的 github/docs-early-access 签出分支。

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

如果当前存储库是 github/docs-internal 存储库,则此步骤将使用 run 关键字执行 shell 命令,将 docs-early-access 存储库的文件夹移动到主存储库的文件夹中。

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

此步骤运行一个命令,从存储库签出 LFS 对象。

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

此步骤使用 trilom/file-changes-action 操作收集拉取请求中更改的文件,以便在下一步中分析它们。 此示例使用 a6ca26c14274c33b15e6499323aac178af06ad4b SHA 固定到操作的特定版本。

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

此步骤运行 shell 命令,该命令使用上一步的输出创建文件,其中包含在拉取请求中更改的文件列表。

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

此步骤使用 actions/setup-node 操作在运行器上安装指定版本的 node 软件包,以便你可访问 npm 命令。

YAML
      - name: Install dependencies
        run: npm ci

此步骤运行 npm ci shell 命令来安装项目的 npm 软件包。

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

此步骤运行生成脚本。

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 }}/

此步骤使用 npm test 运行测试,测试矩阵为矩阵中的每个作业提供不同的 ${{ matrix.test-group }} 值。 它使用 DIFF_FILE 环境变量来识别已更改的文件,并将 CHANGELOG_CACHE_FILE_PATH 环境变量用于 changelog 缓存文件。

后续步骤