gitlab ci 的 include 是静态 yaml 合并而非 python 式导入,路径错误会静默跳过导致 job 消失;include 路径须以项目根目录为基准,extends 仅限单文件内生效,变量合并规则复杂,需谨慎使用 before_script、cache 和 variables。

include 路径写错导致 job 找不到
GitLab CI 的 include 不是“导入 Python 模块”,它只做 YAML 合并,路径错误时不会报错,而是静默跳过——job 就凭空消失了。最常见的是相对路径没对准 .gitlab-ci.yml 所在位置。
实操建议:
立即学习“Python免费学习笔记(深入)”;
-
include中的local路径始终以项目根目录为基准,不是以当前文件所在目录为基准 - 用
include: local: '/ci/templates/deploy.yml',别写成'./templates/deploy.yml'(点开头会被忽略) - CI 配置里加个 dummy job 测试 include 是否生效:
test-include:+script: echo "loaded",放在被 include 的文件里 - GitLab 15.0+ 支持
include: template,但仅限官方模板;自定义模板必须用local或file+project
extends 不能跨文件直接引用
extends 只在单个 YAML 文件内有效。很多人把基础 job 定义在 /ci/base.yml,然后在主文件里写 extends: .base-job,结果报错 Could not find anchor .base-job。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 必须先
include含有 anchor 的文件,再在**同一层级或下游文件**中extends - anchor 名称要带点前缀避免冲突,比如统一用
.template-job、.retry-strategy - 如果多个子文件都要复用同一组 anchor,把它们全收进一个
/ci/common.yml,主文件include它一次就够了 - 注意 YAML 解析顺序:GitLab 先合并所有
include,再解析extends,所以不能在include之前就用extends
include + extends 组合后变量覆盖混乱
当 base job 定义了 variables:,而 extending job 也写了同名变量,实际行为是“深层合并”而非“覆盖”——比如 PATH 会拼接,但 ENV 这种标量会被后写的完全替换。容易误以为没生效。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 用
!reference替代部分extends场景,它支持跨文件引用 job 内容(GitLab 14.6+),且变量行为更可预测 - 避免在 base 中设全局
variables,改用default:variables或显式传参:variables: { APP_ENV: $APP_ENV } - 调试时加
script: env | grep -E '^(APP|CI)_'看最终变量值 -
before_script和after_script是浅合并(后声明覆盖前),和variables行为不一致,别混着依赖
Python 项目里 include 复用 pytest 配置的坑
想把 pytest 相关 job 抽到 /ci/python-test.yml,但发现 cache: 路径不对、artifacts: 没上传、甚至 poetry install 报错找不到 pyproject.toml——问题出在工作目录和上下文丢失。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 所有被 include 的 job 必须显式声明
before_script切工作目录:- cd $CI_PROJECT_DIR -
cache:的key别用$CI_COMMIT_REF_SLUG,改用$CI_PROJECT_ID-$CI_COMMIT_TAG避免跨分支污染 - Python 依赖缓存建议用
pip+requirements.txt方式,比poetry更稳定;若必须用 poetry,确保POETRY_HOME和cache:paths对齐 - 不要在 base job 里写
script: pytest ...,改用script: $TEST_CMD,让 extending job 通过variables: { TEST_CMD: "pytest tests/" }注入,更灵活
GitLab CI 的 include 和 extends 看似简单,但 YAML 合并规则、解析时机、作用域边界这些细节,一旦组合起来就很容易漏掉一层上下文。最常被忽略的是:include 不等于 import,extends 不等于 inherit,它们没有运行时概念,只有静态合并逻辑。










