Skip to main content

在运行器上使用 GitHub CLI

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

示例概述

本文使用示例工作流演示 GitHub Actions 的某些主要 CI 功能。 此工作流触发后,会自动运行一个脚本,用于检查 GitHub Docs 站点是否有任何损坏的链接。 如果找到任何损坏的链接,工作流将使用 GitHub CLI 创建包含详细信息的 GitHub 问题。

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

工作流步骤概述图

此示例中使用的功能

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

功能实现
定期运行工作流:schedule

示例工作流

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

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

YAML
name: Check all English links

# **What it does**: This script once a day checks all English links and reports in issues.
# **Why we have it**: We want to know if any links break.
# **Who does it impact**: Docs content.

on:
  workflow_dispatch:
  schedule:
    - cron: '40 19 * * *' # once a day at 19:40 UTC / 11:40 PST

permissions:
  contents: read
  issues: write

jobs:
  check_all_english_links:
    name: Check all links
    if: github.repository == 'github/docs-internal'
    runs-on: ubuntu-latest
    env:
      GITHUB_TOKEN: ${{ secrets.DOCUBOT_READORG_REPO_WORKFLOW_SCOPES }}
      FIRST_RESPONDER_PROJECT: Docs content first responder
      REPORT_AUTHOR: docubot
      REPORT_LABEL: broken link report
      REPORT_REPOSITORY: github/docs-content
    steps:
      - name: Check out repo's default branch
        uses: actions/checkout@v3
      - name: Setup Node
        uses: actions/setup-node@v3
        with:
          node-version: 16.13.x
          cache: npm
      - name: npm ci
        run: npm ci
      - name: npm run build
        run: npm run build
      - name: Run script
        run: |
          script/check-english-links.js > broken_links.md

      # check-english-links.js returns 0 if no links are broken, and 1 if any links
      # are broken. When an Actions step's exit code is 1, the action run's job status
      # is failure and the run ends. The following steps create an issue for the
      # broken link report only if any links are broken, so `if: ${{ failure() }}`
      # ensures the steps run despite the previous step's failure of the job.

      - if: ${{ failure() }}
        name: Get title for issue
        id: check
        run: echo "title=$(head -1 broken_links.md)" >> $GITHUB_OUTPUT
      - if: ${{ failure() }}
        name: Create issue from file
        id: broken-link-report
        uses: peter-evans/create-issue-from-file@b4f9ee0a9d4abbfc6986601d9b1a4f8f8e74c77e
        with:
          token: ${{ env.GITHUB_TOKEN }}

          title: ${{ steps.check.outputs.title }}
          content-filepath: ./broken_links.md
          repository: ${{ env.REPORT_REPOSITORY }}
          labels: ${{ env.REPORT_LABEL }}
      - if: ${{ failure() }}
        name: Close and/or comment on old issues
        env:
          NEW_REPORT_URL: 'https://github.com/${{ env.REPORT_REPOSITORY }}/issues/${{ steps.broken-link-report.outputs.issue-number }}'
        run: |
          gh alias set list-reports "issue list \
                                       --repo ${{ env.REPORT_REPOSITORY }} \
                                       --author ${{ env.REPORT_AUTHOR }} \
                                       --label '${{ env.REPORT_LABEL }}'"

          # Link to the previous report from the new report that triggered this
          # workflow run.

          previous_report_url=$(gh list-reports \
                                  --state all \
                                  --limit 2 \
                                  --json url \
                                  --jq '.[].url' \
                                  | grep -v ${{ env.NEW_REPORT_URL }} | head -1)

          gh issue comment ${{ env.NEW_REPORT_URL }} --body "⬅️ [Previous report]($previous_report_url)"

          # If an old report is open and assigned to someone, link to the newer
          # report without closing the old report.

          for issue_url in $(gh list-reports \
                                  --json assignees,url \
                                  --jq '.[] | select (.assignees != []) | .url'); do
            if [ "$issue_url" != "${{ env.NEW_REPORT_URL }}" ]; then
              gh issue comment $issue_url --body "➡️ [Newer report](${{ env.NEW_REPORT_URL }})"
            fi
          done

          # Link to the newer report from any older report that is still open,
          # then close the older report and remove it from the first responder's
          # project board.

          for issue_url in $(gh list-reports \
                                  --search 'no:assignee' \
                                  --json url \
                                  --jq '.[].url'); do
            if [ "$issue_url" != "${{ env.NEW_REPORT_URL }}" ]; then
              gh issue comment $issue_url --body "➡️ [Newer report](${{ env.NEW_REPORT_URL }})"
              gh issue close $issue_url
              gh issue edit $issue_url --remove-project "${{ env.FIRST_RESPONDER_PROJECT }}"
            fi
          done

了解示例

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

代码 解释
YAML
name: Check all English links

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

YAML
on:
  workflow_dispatch:
  schedule:
    - cron: '40 20 * * *' # once a day at 20:40 UTC / 12:40 PST

workflow_dispatchscheduled 定义为工作流的触发器:

  • 通过 workflow_dispatch,可从 UI 手动运行此工作流。 有关详细信息,请参阅 workflow_dispatch
  • 通过 schedule 事件,可使用 cron 语法来定义自动触发工作流的定期间隔。 有关详细信息,请参阅 schedule
YAML
permissions:
  contents: read
  issues: write

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

YAML
jobs:

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

YAML
  check_all_english_links:
    name: Check all links

定义 ID 为 check_all_english_links、名称为 Check all links 的作业,该作业存储在 jobs 密钥中。

YAML
if: github.repository == 'github/docs-internal'

仅当存储库名为 docs-internal 且位于 github 组织内时,才运行 check_all_english_links 作业。 否则,作业会被标记为“跳过”。

YAML
runs-on: ubuntu-latest

配置作业在 Ubuntu Linux 运行器上运行。 这意味着作业将在 GitHub. 托管的新虚拟机上执行。 有关使用其他运行器的语法示例,请参阅“GitHub Actions 的工作流语法”。

YAML
    env:
      GITHUB_TOKEN: ${{ secrets.DOCUBOT_READORG_REPO_WORKFLOW_SCOPES }}
      REPORT_AUTHOR: docubot
      REPORT_LABEL: broken link report
      REPORT_REPOSITORY: github/docs-content

创建自定义环境变量,并重新定义内置的 GITHUB_TOKEN 变量以使用自定义机密。 稍后将在工作流中引用这些变量。

YAML
    steps:

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

YAML
      - name: Check out repo's default branch
        uses: actions/checkout@v3

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

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

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

YAML
      - name: Run the "npm ci" command
        run: npm ci
      - name: Run the "npm run build" command
        run: npm run build

run 关键字指示作业在运行器上执行命令。 在这种情况下,npm cinpm run build 命令作为单独的步骤运行,用于在存储库中安装和生成 Node.js 应用程序。

YAML
      - name: Run script
        run: |
          script/check-english-links.js > broken_links.md

run 命令执行存储库 (script/check-english-links.js) 中存储的脚本,并通过管道将输出传递给名为 broken_links.md 的文件。

YAML
      - if: ${{ failure() }}
        name: Get title for issue
        id: check
        run: echo "title=$(head -1 broken_links.md)" >> $GITHUB_OUTPUT

如果 check-english-links.js 脚本检测到损坏的链接并返回非零(失败)退出状态,则使用工作流命令设置具有 broken_links.md 文件第一行值的输出(下一步中会用到它)。

YAML
      - if: ${{ failure() }}
        name: Create issue from file
        id: broken-link-report
        uses: peter-evans/create-issue-from-file@b4f9ee0a9d4abbfc6986601d9b1a4f8f8e74c77e
        with:
          token: ${{ env.GITHUB_TOKEN }}

          title: ${{ steps.check.outputs.title }}
          content-filepath: ./broken_links.md
          repository: ${{ env.REPORT_REPOSITORY }}
          labels: ${{ env.REPORT_LABEL }}

使用 peter-evans/create-issue-from-file 操作创建新的 GitHub 问题。 此示例使用 b4f9ee0a9d4abbfc6986601d9b1a4f8f8e74c77e SHA 固定到操作的特定版本。

YAML
      - if: ${{ failure() }}
        name: Close and/or comment on old issues
        env:
          NEW_REPORT_URL: 'https://github.com/${{ env.REPORT_REPOSITORY }}/issues/${{ steps.broken-link-report.outputs.issue-number }}'
        run: |
          gh alias set list-reports "issue list \
                                       --repo ${{ env.REPORT_REPOSITORY }} \
                                       --author ${{ env.REPORT_AUTHOR }} \
                                       --label '${{ env.REPORT_LABEL }}'"
          previous_report_url=$(gh list-reports \
                                  --state all \
                                  --limit 2 \
                                  --json url \
                                  --jq '.[].url' \
                                  | grep -v ${{ env.NEW_REPORT_URL }} | head -1)

          gh issue comment ${{ env.NEW_REPORT_URL }} --body "⬅️ [Previous report]($previous_report_url)"

使用 gh issue list 查找之前在早期运行中创建的问题。 在后续步骤中,为了简化处理,这将别名设置为 gh list-reports。 若要获取问题 URL,jq 表达式将处理生成的 JSON 输出。

然后,使用 gh issue comment 向链接到上一个问题的新问题添加注释。

YAML
          for issue_url in $(gh list-reports \
                                  --json assignees,url \
                                  --jq '.[] | select (.assignees != []) | .url'); do
            if [ "$issue_url" != "$" ]; then
              gh issue comment $issue_url --body "➡️ [Newer report]($)"
            fi
          done

如果上一次运行中的问题已打开且已分配给某人,则使用 gh issue comment 添加注释并附上指向新问题的链接。

YAML
          for issue_url in $(gh list-reports \
                                  --search 'no:assignee' \
                                  --json url \
                                  --jq '.[].url'); do
            if [ "$issue_url" != "${{ env.NEW_REPORT_URL }}" ]; then
              gh issue comment $issue_url --body "➡️ [Newer report](${{ env.NEW_REPORT_URL }})"
              gh issue close $issue_url
              gh issue edit $issue_url --remove-project "${{ env.FIRST_RESPONDER_PROJECT }}"
            fi
          done

如果上一次运行中的问题已打开但未分配给任何人,则:

后续步骤