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_script、after_script 或 script。
run 关键字接受要运行的步骤列表。步骤按照在列表中定义的顺序一次运行一个。
每个列表项都有一个 name 和 step、script 或 action 中的一个。
名称只能包含字母数字字符和下划线,并且不能以数字开头。
运行步骤
通过使用 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}}"环境变量
步骤可以设置环境变量、导出
环境变量,并且在使用 step、script 或 action 时可以传入环境变量。
环境变量优先级,从高到低,是设置的变量:
- 在
step.yml中使用env关键字。 - 在步骤序列中传递给步骤使用
env关键字。 - 在步骤序列中为所有步骤使用
env关键字。 - 先前运行的步骤写入
${{export_file}}的位置。 - 由 Runner 设置。
- 由容器设置。
创建自己的步骤
通过执行以下任务创建自己的步骤:
- 创建一个 GitLab 项目、Git 仓库或在 CI/CD 作业运行时可访问的 文件系统上的目录。
- 创建一个
step.yml文件并将其放在项目、仓库或目录的根文件夹中。 - 在
step.yml中定义步骤的规范。 - 在
step.yml中定义步骤的定义。 - 将您的步骤使用的任何文件添加到项目、仓库或目录中。
创建步骤后,您可以在作业中使用该步骤。
步骤规范
步骤规范是步骤 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"} |
结构化内容。 |
例如,要指定步骤返回一个名为 value 的 number 类型输出:
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 格式的内容来返回输出。
每行是一个带有 name 和 value 键对的 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 格式的内容来导出环境变量。
每行是一个带有 name 和 value 键对的 JSON 对象。name 和 value 都必须是字符串。
例如,要将变量 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