3. .gitlab-ci.yml 基础语法
在 GitLab CI/CD 中,.gitlab-ci.yml 是整条流水线的“源代码”。它决定了每次提交后,GitLab Runner 会执行哪些作业、按什么顺序执行、在什么条件下执行,以及执行时使用哪些变量和环境。理解这份文件的基础语法,是后续编写自动化测试与发布流水线的前提。本节围绕最核心的八个关键字展开:stages、jobs、script、before_script、after_script、variables、rules 与 only/except,并重点说明它们的执行顺序与优先级关系。
一、stages 与 jobs:流水线的骨架与执行单元
stages 定义流水线的阶段顺序,是一个顶层关键字,值为字符串数组。默认情况下,如果未声明 stages,GitLab 会使用 .pre、build、test、deploy、.post 五个阶段。阶段之间是串行关系:只有当前阶段的所有作业成功完成,下一阶段的作业才会开始。同一阶段内的多个作业默认并行执行。
作业(job)是流水线的最小执行单元,写在顶层,名字不能是保留关键字(如 stages、variables、before_script 等)。每个作业至少要包含 script,否则该作业不会被执行。作业通过 stage 关键字归属到某个阶段,未指定时默认属于 test 阶段。
stages:
- build
- test
- deploy
build_app:
stage: build
script:
- echo "编译应用"
- make build
unit_test:
stage: test
script:
- echo "运行单元测试"
- make test
上面这段配置中,build_app 先执行,成功后 unit_test 才会启动。如果 build_app 失败,unit_test 不会运行,整个流水线标记为失败。
二、script、before_script、after_script:作业内的执行顺序
每个作业内部,命令的执行顺序是固定的:before_script → script → after_script。before_script 常用于准备环境,例如安装依赖、登录镜像仓库、设置环境变量;script 是作业的主体逻辑;after_script 用于清理工作,例如停止服务、删除临时文件、上传日志。
需要特别注意:after_script 无论作业成功还是失败都会执行,但它执行失败不会影响作业本身的最终状态。也就是说,即使 after_script 报错,只要 script 成功,作业仍算成功。此外,before_script 和 after_script 都可以定义在顶层作为全局默认值,作业内定义的同名关键字会覆盖全局值。
before_script:
- echo "全局准备:安装依赖"
- npm install
after_script:
- echo "全局清理:删除临时目录"
- rm -rf tmp/
test_job:
stage: test
before_script:
- echo "本作业覆盖:只安装测试依赖"
- npm install --only=dev
script:
- npm run test
after_script:
- echo "本作业清理:收集覆盖率报告"
- cp coverage/lcov.info ./artifacts/
在这个例子中,test_job 实际执行的是作业内定义的 before_script,而不是顶层的全局版本。这就是“就近覆盖”原则:作业级配置优先于全局配置。
三、variables:变量定义与作用域
variables 用于定义变量,可以在顶层定义全局变量,也可以在作业内定义局部变量。变量在 script、before_script、after_script 中都可以通过 $变量名 或 ${变量名} 引用。作业级变量会覆盖同名的全局变量。
variables:
APP_ENV: "staging"
NODE_VERSION: "18"
build_job:
stage: build
variables:
APP_ENV: "production"
script:
- echo "当前环境:$APP_ENV"
- echo "Node 版本:$NODE_VERSION"
执行 build_job 时,APP_ENV 输出为 production,因为作业级变量覆盖了全局变量;而 NODE_VERSION 仍为 18,因为它只在全局定义。变量还可以在 GitLab 项目设置中预定义,优先级通常为:作业级变量 > 项目/组级变量 > 全局变量。
四、rules 与 only/except:控制作业是否执行
rules 和 only/except 都用于控制作业在什么条件下被加入流水线,但两者不能在同一作业中混用。only/except 是较早期的语法,基于分支名、标签、事件类型进行简单匹配;rules 是推荐的新语法,支持更细粒度的条件判断,包括分支、事件、变量、文件变更等,并且可以按顺序匹配,一旦命中就停止后续判断。
deploy_job:
stage: deploy
script:
- echo "部署到生产环境"
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
when: manual
- if: '$CI_COMMIT_BRANCH =~ /^release\/.*/'
when: on_success
- when: never
这段配置的含义是:当提交到 main 分支时,deploy_job 以手动方式触发;当提交到 release/ 开头的分支时,自动执行;其他情况一律不执行。rules 按从上到下的顺序匹配,第一条命中的规则决定作业行为,因此最后一条 when: never 起到了兜底作用。
相比之下,only/except 的写法更简单,但表达能力有限:
deploy_job:
stage: deploy
script:
- echo "部署到生产环境"
only:
- main
- /^release\/.*/
except:
- schedules
它表示只在 main 分支或 release/ 开头的分支上执行,但定时任务触发的流水线除外。由于 rules 更灵活且是官方推荐方向,新项目应优先使用 rules。
五、优先级与执行顺序总览
把上述关键字放在一起,可以形成一条清晰的判断链:首先由 rules 或 only/except 决定作业是否进入流水线;进入后,按 stages 定义的阶段顺序串行推进;同一阶段内作业并行;每个作业内部按 before_script → script → after_script 执行;变量则按“作业级覆盖全局级”的规则生效。下面这张图概括了从配置解析到作业执行的完整关系。
六、常见易错点与配置建议
第一,rules 与 only/except 不能同时出现在一个作业中,否则 GitLab 会报配置错误。第二,after_script 中不能使用 before_script 或 script 中定义的临时变量,因为它运行在独立的 shell 上下文中。第三,rules 的 when 值包括 on_success、on_failure、always、manual、delayed 和 never,其中 never 常用于排除特定条件。第四,如果作业没有 script,即使写了 rules 也不会执行,因为 GitLab 会直接忽略该作业。
before_script 只保留最通用的环境准备命令,把与具体作业强相关的依赖安装放到作业级 before_script 中,避免每个作业重复执行不必要的步骤。同时,用 rules 替代 only/except,可以让流水线的触发条件更清晰、更易维护。