利用可重用工作流处理环境之间的相似性

已完成

将更改部署到多个环境时,部署到每个环境所涉及的步骤相似甚至完全相同。 本单元将学习如何设计工作流以避免重复并允许重复使用工作流代码。

对多个环境的部署

在与网站团队的同事交谈后,你决定为玩具公司的网站使用以下工作流:

关系图显示一系列工作流作业,并包括测试和生产部署。

  1. 该工作流运行 Bicep linter 来检查 Bicep 代码是否有效并遵循最佳做法。

    对 Bicep 代码进行 Lint 分析时无需连接到 Azure,因此部署到多少环境并不重要。 它仅运行一次。

  2. 工作流部署到测试环境并要求:

    1. 运行 Azure 资源管理器预检验证。
    2. 部署 Bicep 代码。
    3. 针对测试环境运行一些测试。
  3. 如果工作流的任何部分出现故障,则整个工作流都会停止,以便你可以调查和解决问题。 如果一切顺利,工作流将继续部署到生产环境:

    1. 工作流包括预览步骤,该阶段在生产环境中运行 What-if 操作以列出将对生产 Azure 资源进行的更改。 what-if 操作还会验证部署,因此无需为生产环境运行单独的验证步骤。
    2. 工作流暂停以进行手动验证。
    3. 如果获得批准,工作流将针对生产环境运行部署和版本验收测试。

其中一些任务在测试环境和生产环境之间重复,另一些任务仅针对特定环境运行:

任务 环境
Lint 两者都不 - Lint 分析不适用于环境
验证 仅测试
预览 仅生产
部署 两种环境
版本验收测试 两种环境

当需要在工作流中重复步骤时,复制和粘贴步骤定义并不是一个好的做法。 复制工作流的代码时,很容易意外地犯一些细微的错误或使内容不同步。 将来,需要更改步骤时,必须记住在多个位置应用更改。 更好的做法是使用可重用工作流。

可重用工作流

利用 GitHub Actions,你可以通过创建定义步骤或作业的单独的工作流 YAML 文件来创建工作流定义的可重用部分。 你可以创建 YAML 文件以在单个工作流中多次重复使用部分工作流,甚至在多个工作流中重复使用。 你重复使用的工作流是一个被调用的工作流,包含它的工作流是一个调用方工作流。 从概念上讲,你可以将它们视为与 Bicep 模块类似。

创建可重用工作流时,可以使用 workflow_call 触发器来告诉 GitHub Actions:工作流可由其他工作流调用。 下面是一个可重用工作流的基本示例,保存在名为 script.yml 的文件中:

on:
  workflow_call:

jobs:
  say-hello:
    runs-on: ubuntu-latest
    steps:
    - run: |
        echo Hello world!

在调用方工作流中,通过包含 uses: 关键字并指定当前存储库中被调用的工作流的路径,引用被调用的工作流:

on: 
  workflow_dispatch:

jobs:
  job:
    uses: ./.github/workflows/script.yml

还可以引用另一个存储库中的工作流定义文件。

被调用工作流输入和机密

你可以使用“输入”和“机密”使被调用的工作流更易于重复使用,因为每次使用工作流时,你都可以允许它们之间存在微小的差异。

当你创建一个被调用的工作流时,可以在文件的顶部指示工作流的输入和机密:

on:
  workflow_call:
    inputs:
      environmentType:
        required: true
        type: string
    secrets:
      AZURE_CLIENT_ID:
        required: true
      AZURE_TENANT_ID:
        required: true
      AZURE_SUBSCRIPTION_ID:
        required: true

可以根据需要定义任意数量的输入和机密。 但就像 Bicep 参数一样,不应过度使用工作流输入。 你应该让其他人也可以轻松地重用你的工作流,而不必指定过多设置。

输入可以有多个属性,包括:

  • 输入名称,用于在工作流定义中引用输入
  • 输入类型。 输入支持“字符串”、“数字”和“布尔”值。
  • 输入的默认值(可选)。 如果未指定默认值,则在调用方工作流中使用工作流时,必须提供一个值。

机密具有名称,但没有类型或默认值。

在该示例中,工作流定义了一个名为 environmentType 的必需字符串输入,以及三个分别名为 AZURE_CLIENT_IDAZURE_TENANT_IDAZURE_SUBSCRIPTION_ID 的必需机密。

在工作流中,使用特殊语法来引用参数的值,如以下示例中所示:

jobs:
  say-hello:
    runs-on: ubuntu-latest
    steps:
    - run: |
        echo Hello ${{ inputs.environmentType }}!

使用 with 关键字将输入的值传递给被调用的工作流。 需要在 with 部分中定义每个输入的值,但不能使用 env 关键字来引用工作流的环境变量。 使用 secrets 关键字将机密值传递给被调用的工作流。

on: 
  workflow_dispatch:

permissions:
  id-token: write
  contents: read

jobs:
  job-test:
    uses: ./.github/workflows/script.yml
    with:
      environmentType: Test
    secrets:
      AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID_TEST }}
      AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
      AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

  job-production:
    uses: ./.github/workflows/script.yml
    with:
      environmentType: Production
    secrets:
      AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID_PRODUCTION }}
      AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
      AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

从调用的工作流使用工作负载标识

使用调用的工作流时,通常会跨多个工作流定义文件定义一些部署操作。 需要向调用方工作流授予权限,权限随后可确保每个调用的工作流都能够访问工作流的标识,并向 Azure 进行身份验证:

on: 
  workflow_dispatch:

permissions:
  id-token: write
  contents: read

jobs:
  job-test:
    uses: ./.github/workflows/script.yml
    with:
      environmentType: Test
    secrets:
      AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID_TEST }}
      AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
      AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

  job-production:
    uses: ./.github/workflows/script.yml
    with:
      environmentType: Production
    secrets:
      AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID_PRODUCTION }}
      AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
      AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

条件

可以使用工作流条件来指定是否应根据你指定的规则运行步骤或作业。 你可以结合输入和工作流条件,为多种情况定制部署过程。

例如,假设你定义了一个运行脚本步骤的工作流。 你计划在每个环境中重用该模板。 当你部署生产环境时,你希望运行一个其他步骤。 下面是在该步骤中使用 if 条件来实现此目的的方法:

jobs:
  say-hello:
    runs-on: ubuntu-latest
    steps:
    - run: |
        echo Hello ${{ inputs.environmentType }}!

    - run: |
        echo This step only runs for production deployments.
      if: inputs.environmentType == 'Production'

此处的条件的含义是:如果 environmentType 参数的值等于“Production”,则运行该步骤。

尽管条件为工作流增添了灵活性,但条件过多可能会增加工作流的复杂程度化,并导致其难以理解。 如果在一个调用的工作流中设置了许多条件,可能需要考虑重新设计它。

此外,可使用 YAML 注释来解释你使用的条件以及工作流中可能需要更多说明的其他方面。 注释有助于降低工作流的理解难度和将来的使用。 在整个模块的练习中存在一些示例 YAML 注释。