Help us learn about your current experience with the documentation. Take the survey.

CI/CD 步骤

  • Tier: Free, Premium, Ultimate
  • Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated
  • Status: Experiment

步骤是作业的可重用单元,当组合在一起时,可以替代 GitLab CI/CD 作业中使用的 script。 您不是必须使用步骤。但是,步骤的可重用性、可组合性、可测试性和独立性 使得理解和维护 CI/CD 流水线变得更加容易。

要开始使用,您可以尝试设置步骤教程。 要开始创建自己的步骤,请参阅创建自己的步骤。要了解流水线如何受益 于同时使用 CI/CD 组件和 CI/CD 步骤,请参阅组合 CI/CD 组件和 CI/CD 步骤

这个实验性功能仍在积极开发中,随时可能发生破坏性 更改。请查看变更日志 了解任何破坏性更改的完整详情。

在 GitLab Runner 17.11 及更高版本中,当您使用 Docker 执行器时,GitLab Runner 会将 step-runner 二进制文件注入到构建容器中。对于所有其他执行器,请确保 step-runner 二进制文件在执行环境中。由 step runner 团队维护的旧版 Docker 镜像 registry.gitlab.com/gitlab-org/step-runner:v0 的支持将在 GitLab 18.0 中结束。

步骤工作流

步骤要么运行一系列步骤,要么执行命令。每个步骤指定输入和输出,并且可以访问 CI/CD 作业变量、环境变量以及文件系统和网络等资源。步骤托管在本地文件系统、GitLab.com 仓库或任何其他 Git 源中。

此外,步骤:

  • 在由 Steps 团队创建的 Docker 容器中运行,您可以查看 Dockerfile。 跟踪 epic 15073 以了解 步骤何时将在 CI/CD 作业定义的环境中运行。
  • 特定于 Linux。跟踪 epic 15074 以了解步骤何时支持多个操作系统。

例如,这个作业使用 run CI/CD 关键字来运行一个步骤:

job:
  variables:
    CI_SAY_HI_TO: "Sally"
  run:
    - name: say_hi
      step: gitlab.com/gitlab-org/ci-cd/runner-tools/echo-step@v1.0.0
      inputs:
        message: "hello, ${{job.CI_SAY_HI_TO}}"

当这个作业运行时,消息 hello, Sally 会打印到作业日志中。 echo 步骤的定义是:

spec:
  inputs:
    message:
      type: string
---
exec:
  command:
    - bash
    - -c
    - echo '${{inputs.message}}'

使用 CI/CD 步骤

使用 run 关键字配置 GitLab CI/CD 作业以使用 CI 步骤。当您运行 CI/CD 步骤时, 不能在作业中使用 before_scriptafter_scriptscript

run 关键字接受要运行的步骤列表。步骤按照在列表中定义的顺序一次运行一个。 每个列表项都有一个 namestepscriptaction 中的一个。

名称只能包含字母数字字符和下划线,并且不能以数字开头。

运行步骤

通过使用 step 关键字提供步骤位置来运行步骤。

输入和环境变量可以传递给步骤,这些可以包含插值的表达式。 步骤在 CI_PROJECT_DIR 预定义变量定义的目录中运行。

例如,从 Git 仓库 gitlab.com/components/echo 加载的 echo 步骤 接收环境变量 USER: Fred 和输入 message: hello Sally

job:
  variables:
    CI_SAY_HI_TO: "Sally"
  run:
    - name: say_hi
      step: gitlab.com/components/echo@v1.0.0
      env:
        USER: "Fred"
      inputs:
        message: "hello ${{job.CI_SAY_HI_TO}}"

运行脚本

使用 script 关键字在 shell 中运行脚本。使用 env 传递给脚本的 环境变量在 shell 中设置。脚本步骤在 CI_PROJECT_DIR 预定义变量定义的目录中运行。

例如,以下脚本将 GitLab 用户打印到作业日志:

my-job:
  run:
    - name: say_hi
      script: echo hello ${{job.GITLAB_USER_LOGIN}}

脚本步骤使用 bash shell,如果找不到 bash 则回退使用 sh

运行 GitHub action

使用 action 关键字运行 GitHub actions。输入和环境变量直接传递给 action,action 输出作为步骤输出返回。Action 步骤在 CI_PROJECT_DIR 预定义变量定义的目录中运行。

运行 actions 需要 dind 服务。更多信息,请参阅 使用 Docker 构建 Docker 镜像

例如,以下步骤使用 action 使 yq 可用:

my-job:
  run:
    - name: say_hi_again
      action: mikefarah/yq@master
      inputs:
        cmd: echo ["hi ${{job.GITLAB_USER_LOGIN}} again!"] | yq .[0]

已知问题

在 GitLab 中运行的 actions 不支持直接上传制品。 制品必须写入文件系统并缓存,并使用现有的 artifacts 关键字cache 关键字选择。

步骤位置

步骤从文件系统上的相对路径、GitLab.com 仓库 或任何其他 Git 源加载。

从文件系统加载步骤

使用以句点 . 开头的相对路径从文件系统加载步骤。 路径引用的文件夹必须包含 step.yml 步骤定义文件。 无论操作系统如何,路径分隔符必须始终使用正斜杠 /

例如:

- name: my-step
  step: ./path/to/my-step

从 Git 仓库加载步骤

通过提供仓库的 URL 和修订版(提交、分支或标签)从 Git 仓库加载步骤。 您还可以指定仓库 steps 文件夹中步骤的相对目录和文件名。 如果 URL 没有指定目录,则从 steps 文件夹加载 step.yml

例如:

  • 使用分支指定步骤:

    job:
      run:
        - name: specifying_a_branch
          step: gitlab.com/components/echo@main
  • 使用标签指定步骤:

    job:
      run:
        - name: specifying_a_tag
          step: gitlab.com/components/echo@v1.0.0
  • 使用仓库中的目录、文件名和 Git 提交指定步骤:

    job:
      run:
        - name: specifying_a_directory_file_and_commit_within_the_repository
          step: gitlab.com/components/echo/-/reverse/my-step.yml@3c63f399ace12061db4b8b9a29f522f41a3d7f25

要指定 steps 文件夹之外的文件夹或文件,请使用扩展的 step 语法:

  • 指定相对于仓库根目录的目录和文件名。

    job:
      run:
        - name: specifying_a_directory_outside_steps
          step:
            git:
              url: gitlab.com/components/echo
              rev: main
              dir: my-steps/sub-directory  # 可选,默认为仓库根目录
              file: my-step.yml            # 可选,默认为 `step.yml`

表达式

表达式是用双花括号 ${{ }} 包围的迷你语言。表达式在作业环境中 步骤执行之前立即求值,可用于:

  • 输入值
  • 环境变量值
  • 步骤位置 URL
  • 可执行命令
  • 可执行工作目录
  • 步骤序列中的输出
  • script 步骤
  • action 步骤

表达式可以引用以下变量:

变量 示例 描述
env ${{env.HOME}} 访问在执行环境或先前步骤中设置的环境变量。
export_file echo '{"name":"NAME","value":"Fred"}' >${{export_file}} 导出文件的路径。写入此文件以导出环境变量供后续运行的步骤使用。
inputs ${{inputs.message}} 访问步骤的输入。
job ${{job.GITLAB_USER_NAME}} 访问 GitLab CI/CD 变量,仅限于以 CI_DOCKER_GITLAB_ 开头的变量。
output_file echo '{"name":"meaning_life","value":42}' >${{output_file}} 输出文件的路径。写入此文件以从步骤设置输出变量。
step_dir work_dir: ${{step_dir}} 步骤已下载到的目录。用于引用步骤中的文件,或设置可执行步骤的工作目录。
steps.[step_name].outputs ${{steps.my_step.outputs.name}} 访问先前执行步骤的输出。使用步骤名称选择特定步骤。
work_dir ${{work_dir}} 正在执行的步骤的工作目录。

表达式与使用双方括号($[[ ]])并在作业生成期间求值的模板插值不同。

表达式只能访问名称以 CI_DOCKER_GITLAB_ 开头的 CI/CD 作业变量。 跟踪 epic 15073 以了解 步骤何时可以访问所有 CI/CD 作业变量。

使用先前步骤的输出

步骤输入可以通过引用步骤名称和输出变量名称来引用先前步骤的输出。

例如,如果 gitlab.com/components/random-string 步骤定义了一个名为 random_value 的输出变量:

job:
  run:
    - name: generate_rand
      step: gitlab.com/components/random
    - name: echo_random
      step: gitlab.com/components/echo
      inputs:
        message: "The random value is: ${{steps.generate_rand.outputs.random_value}}"

环境变量

步骤可以设置环境变量、导出 环境变量,并且在使用 stepscriptaction 时可以传入环境变量。

环境变量优先级,从高到低,是设置的变量:

  1. step.yml 中使用 env 关键字。
  2. 在步骤序列中传递给步骤使用 env 关键字。
  3. 在步骤序列中为所有步骤使用 env 关键字。
  4. 先前运行的步骤写入 ${{export_file}} 的位置。
  5. 由 Runner 设置。
  6. 由容器设置。

创建自己的步骤

通过执行以下任务创建自己的步骤:

  1. 创建一个 GitLab 项目、Git 仓库或在 CI/CD 作业运行时可访问的 文件系统上的目录。
  2. 创建一个 step.yml 文件并将其放在项目、仓库或目录的根文件夹中。
  3. step.yml 中定义步骤的规范
  4. step.yml 中定义步骤的定义
  5. 将您的步骤使用的任何文件添加到项目、仓库或目录中。

创建步骤后,您可以在作业中使用该步骤

步骤规范

步骤规范是步骤 step.yml 中包含的两个文档中的第一个。规范定义了步骤接收和返回的输入和输出。

指定输入

输入名称只能使用字母数字字符和下划线,并且不能以数字开头。 输入必须具有类型,并且可以选择指定默认值。没有默认值的输入 是必需输入,使用步骤时必须指定。

输入必须是以下类型之一。

类型 示例 描述
array ["a","b"] 未类型化项的列表。
boolean true 真或假。
number 56.77 64 位浮点数。
string "brown cow" 文本。
struct {"k1":"v1","k2":"v2"} 结构化内容。

例如,要指定步骤接受一个名为 greeting 的可选 string 类型输入:

spec:
  inputs:
    greeting:
      type: string
      default: "hello, world"
---

在使用步骤时提供输入:

run:
  - name: my_step
    step: ./my-step
    inputs:
      greeting: "hello, another world"

指定输出

与输入类似,输出名称只能使用字母数字字符和下划线, 并且不能以数字开头。输出必须具有类型,并且可以选择指定默认值。 当步骤不返回输出时返回默认值。

输出必须是以下类型之一。

类型 示例 描述
array ["a","b"] 未类型化项的列表。
boolean true 真或假。
number 56.77 64 位浮点数。
string "brown cow" 文本。
struct {"k1":"v1","k2":"v2"} 结构化内容。

例如,要指定步骤返回一个名为 valuenumber 类型输出:

spec:
  outputs:
    value:
      type: number
---

在使用步骤时使用输出:

run:
  - name: random_generator
    step: ./random_gen
  - name: echo_number
    step: ./echo
    inputs:
      message: "Random number generated was ${{step.random_generator.outputs.value}}"

指定委托输出

输出可以完全委托给子步骤,而不是指定输出名称和类型。 子步骤返回的输出由您的步骤返回。步骤定义中的 delegate 关键字 确定步骤返回哪些子步骤输出。

例如,以下步骤返回 random_gen 步骤返回的输出。

spec:
  outputs: delegate
---
run:
  - name: random_generator
    step: ./random_gen
delegate: random_generator

指定无输入或输出

步骤可能不需要任何输入或返回任何输出。这可能是当步骤 只写入磁盘、设置环境变量或打印到 STDOUT 时。在这种情况下, spec: 为空:

spec:
---

步骤定义

步骤可以:

  • 设置环境变量
  • 执行命令
  • 运行一系列其他步骤。

设置环境变量

使用 env 关键字设置环境变量。环境变量名称只能使用 字母数字字符和下划线,并且不能以数字开头。

环境变量可用于可执行命令或所有步骤 (如果运行一系列步骤)。例如:

spec:
---
env:
  FIRST_NAME: Sally
  LAST_NAME: Seashells
run:
  # 为简洁起见省略

步骤只能访问来自 runner 环境的环境变量的子集。 跟踪 epic 15073 以了解 步骤何时可以访问所有环境变量。

执行命令

步骤通过使用 exec 关键字声明它执行命令。必须指定命令, 但工作目录(work_dir)是可选的。步骤设置的环境变量 可用于正在运行的进程。

例如,以下步骤将步骤目录打印到作业日志:

spec:
---
exec:
  work_dir: ${{step_dir}}
  command:
    - bash
    - -c
    - "echo ${PWD}"

执行步骤所需的任何依赖项也应该由步骤安装。 例如,如果步骤调用 go,它应该首先安装它。

返回输出

可执行步骤通过向 ${{output_file}} 添加一行 JSON Line 格式的内容来返回输出。 每行是一个带有 namevalue 键对的 JSON 对象。name 必须是字符串, value 必须是与步骤规范中输出类型匹配的类型:

步骤规范类型 预期 JSONL 值类型
array array
boolean boolean
number number
string string
struct object

例如,要返回名为 car 的输出,string 值为 Range Rover

spec:
  outputs:
    car:
      type: string
---
exec:
  command:
    - bash
    - -c
    - echo '{"name":"car","value":"Range Rover"}' >${{output_file}}
导出环境变量

可执行步骤通过向 ${{export_file}} 添加一行 JSON Line 格式的内容来导出环境变量。 每行是一个带有 namevalue 键对的 JSON 对象。namevalue 都必须是字符串。

例如,要将变量 GOPATH 设置为值 /go

spec:
---
exec:
  command:
    - bash
    - -c
    - echo '{"name":"GOPATH","value":"/go"}' >${{export_file}}

运行一系列步骤

步骤通过使用 steps 关键字声明它运行一系列步骤。步骤按照在列表中定义的顺序一次运行一个。 此语法与 run 关键字相同。

步骤必须有一个仅包含字母数字字符和下划线的名称,并且不能以数字开头。

例如,此步骤安装 Go,然后运行第二个期望 Go 已经 安装的步骤:

spec:
---
run:
  - name: install_go
    step: ./go-steps/install-go
    inputs:
      version: "1.22"
  - name: format_go_code
    step: ./go-steps/go-fmt
    inputs:
      code: path/to/go-code
返回输出

通过使用 outputs 关键字从一系列步骤返回输出。 输出中的值类型必须与步骤规范中输出的类型匹配。

例如,以下步骤将安装的 Java 版本作为输出返回。 这假设 install_java 步骤返回一个名为 java_version 的输出。

spec:
  outputs:
    java_version:
      type: string
---
run:
  - name: install_java
    step: ./common/install-java
outputs:
  java_version: "the java version is ${{steps.install_java.outputs.java_version}}"

或者,可以使用 delegate 关键字返回子步骤的所有输出。 例如:

spec:
  outputs: delegate
---
run:
  - name: install_java
    step: ./common/install-java
delegate: install_java

组合 CI/CD 组件和 CI/CD 步骤

CI/CD 组件是可重用的单一流水线配置单元。它们在创建流水线时 包含在流水线中,向流水线添加作业和配置。来自组件项目的文件(如通用脚本或程序) 不能从 CI/CD 作业中引用。

CI/CD 步骤是作业的可重用单元。当作业运行时,引用的步骤被下载到 执行环境或镜像中,同时带来与步骤一起包含的任何额外文件。 步骤的执行替代了作业中的 script

组件和步骤很好地协同工作,为 CI/CD 流水线创建解决方案。步骤处理 作业组合的复杂性,并自动检索运行作业所需的文件。组件提供 导入作业配置的方法,但对用户隐藏底层作业组合。

步骤和组件使用不同的表达式语法来帮助区分表达式类型。 组件表达式使用方括号 $[[ ]] 并在流水线创建期间求值。 步骤表达式使用花括号 ${{ }} 并在作业执行期间,在执行步骤之前立即求值。

例如,项目可以使用一个组件来添加格式化 Go 代码的作业:

  • 在项目的 .gitlab-ci.yml 文件中:

    include:
    - component: gitlab.com/my-components/go@main
      inputs:
        fmt_packages: "./..."
  • 在内部,组件使用 CI/CD 步骤来组合作业,该作业安装 Go 然后运行 格式化程序。在组件的 templates/go.yml 文件中:

    spec:
      inputs:
        fmt_packages:
          description: The Go packages that will be formatted using the Go formatter.
        go_version:
          default: "1.22"
          description: The version of Go to install before running go fmt.
    ---
    
    format code:
      run:
        - name: install_go
          step: ./languages/go/install
          inputs:
            version: $[[ inputs.go_version ]]                    # version set to the value of the component input go_version
        - name: format_code
          step: ./languages/go/go-fmt
          inputs:
            go_binary: ${{ steps.install_go.outputs.go_binary }} # go_binary set to the value of the go_binary output from the previous step
            fmt_packages: $[[ inputs.fmt_packages ]]             # fmt_packages set to the value of the component input fmt_packages

在此示例中,CI/CD 组件对组件作者隐藏了步骤的复杂性。

故障排除

从 HTTPS URL 获取步骤

诸如 tls: failed to verify certificate: x509: certificate signed by unknown authority 之类的错误消息表示 操作系统无法识别或信任托管步骤的服务器。

一个常见原因是当步骤在没有安装任何受信任根证书的 Docker 镜像的作业中运行时。 通过在容器中安装证书或将它们烘焙到作业 image 中来解决问题。

您可以使用 script 步骤在获取任何步骤之前在容器中安装依赖项。 例如:

ubuntu_job:
  image: ubuntu:24.04
  run:
    - name: install_certs  # 首先安装受信任的证书
      script: apt update && apt install --assume-yes --no-install-recommends ca-certificates
    - name: echo_step      # 有了受信任的证书,使用 HTTPS 不会出错
      step: https://gitlab.com/user/my_steps/hello_world@main