PAYATHON 2026

为什么 GitLab CI/CD downstream pipeline 的 SF_ENV input 无法使用变量?

排障赵师傅

SF_ENV 报错,是因为 GitLab 在校验 input 的 options 时,收到的仍是字符串 $SF_ENV,而允许列表中只有 dev、qual 等实际值。

DOCKER_IMAGE 能继续使用,也不能说明它在传入 input 时已经展开。这个 input 没有 options 限制,字符串 $DOCKER_IMAGE 可以通过校验;插入 image 后,还可以由后续的变量展开机制处理。

原配置中的 workflow:rules:variables 还有规则顺序和变量比较语法两个问题,需要分别修正。

为什么 job 中能读取变量,input 却校验失败?

两处处理发生在不同阶段:

  • $[[ inputs.SF_ENV ]] 用于生成 CI/CD 配置,input 在配置处理阶段接受校验。
  • script 中的 $SF_ENV 由 job 的 shell 在执行时读取。

default-pipeline 输出 dev,说明该 job 的运行环境中存在 SF_ENV=dev,但不能据此判断 input 校验时是否已经得到 dev。inputs 使用 CI/CD 变量时,受到与 include 相同的变量可用性限制。参考:CI/CD inputs

当前两个 input 的校验条件不同:

inputs:
  DOCKER_IMAGE: "$DOCKER_IMAGE"  # 是字符串,可以通过 string 类型校验
  SF_ENV: "$SF_ENV"             # 不等于 options 中的任何一个值

保留 options 时,传入明确的环境值

先在触发配置中填入实际值,排除变量展开的影响:

validate-merge-request:
  stage: validate
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "develop"'
  trigger:
    include:
      - local: "ci/sf-deploy.gitlab-ci.yml"
        inputs:
          DOCKER_IMAGE: "sfdx-20260423"
          SF_ENV: "dev"
          JOB_NAME: "validate"

这里使用 child pipeline,语法为 trigger:include:inputs,inputs 应与该条目的 local 同级。参考:trigger:include:inputs

如果需要根据 MR 目标分支选择环境,同时保留 input 的允许值校验,可以分别配置触发 job,传入对应的字面量:

.validate-trigger:
  stage: validate

validate-dev:
  extends: .validate-trigger
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "develop"'
  trigger:
    include:
      - local: "ci/sf-deploy.gitlab-ci.yml"
        inputs:
          DOCKER_IMAGE: "sfdx-20260423"
          SF_ENV: "dev"

validate-qual:
  extends: .validate-trigger
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "release/1.48"'
  trigger:
    include:
      - local: "ci/sf-deploy.gitlab-ci.yml"
        inputs:
          DOCKER_IMAGE: "sfdx-20260423"
          SF_ENV: "qual"

validate-validation:
  extends: .validate-trigger
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "release/1.47"'
  trigger:
    include:
      - local: "ci/sf-deploy.gitlab-ci.yml"
        inputs:
          DOCKER_IMAGE: "sfdx-20260423"
          SF_ENV: "validation"

validate-staging:
  extends: .validate-trigger
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "master"'
  trigger:
    include:
      - local: "ci/sf-deploy.gitlab-ci.yml"
        inputs:
          DOCKER_IMAGE: "sfdx-20260423"
          SF_ENV: "staging"

用这组 job 替换原来的 validate-merge-request。下游已将 JOB_NAME 的默认值设为 validate,因此可以省略它。

镜像标签也先写成实际值,方便确认传入内容。镜像版本的统一维护可以另行处理。

下游要明确使用 input 的值

定义 SF_ENV input 不会自动创建同名环境变量。原来的脚本即使有输出,也可能读到上游转发的 CI/CD 变量:

script:
  - echo "$SF_ENV"

可以将 input 映射到另一个变量,让日志显示最终采用的环境值:

spec:
  inputs:
    SF_ENV:
      description: "The SF environment to target"
      type: string
      options: ["dev", "qual", "staging", "validation", "production"]
    DOCKER_IMAGE:
      description: "The docker image name to use from JFrog"
      type: string
    JOB_NAME:
      description: "Job to perform"
      options: ["validate", "deploy"]
      default: "validate"
---
stages:
  - validate-merge-request

validate-$[[ inputs.SF_ENV ]]:
  image: "registry.example.com/sf-ci:$[[ inputs.DOCKER_IMAGE ]]"
  tags:
    - aws-docker
  stage: validate-merge-request
  variables:
    SF_ENV_FROM_INPUT: "$[[ inputs.SF_ENV ]]"
  rules:
    - if: '"$[[ inputs.JOB_NAME ]]" == "validate"'
  environment:
    name: "$[[ inputs.SF_ENV ]]"
  script:
    - echo "$SF_ENV_FROM_INPUT"

将 registry.example.com 替换为实际镜像仓库地址。child pipeline 使用自己的配置,上游的 default:tags 不会自动成为下游默认配置,因此这里单独声明了 Runner 标签。

workflow 中按分支设置变量,需要怎样修改?

workflow:rules:variables 可以给整条 pipeline 设置默认变量。rules 按顺序匹配,命中后就停止。

原配置开头已放行 master、develop 和 release/ 分支。对应的 pipeline 命中这些规则后,便不会再执行后面设置 SF_ENV 的规则。

比较两个变量时,应写成:

if: '$CI_COMMIT_BRANCH == $DEV_BRANCH'

右侧不要写成 "$DEV_BRANCH"。在 rules:if 表达式中,带引号的内容按字符串处理,变量比较应直接使用 $VARIABLE_1 == $VARIABLE_2。参考:CI/CD variable expressions

将环境映射规则放在前面:

variables:
  SF_ENV: "dev"
  DEV_BRANCH: "develop"
  QUAL_BRANCH: "release/1.48"
  VALIDATION_BRANCH: "release/1.47"

workflow:
  rules:
    - if: '$CI_MERGE_REQUEST_TARGET_BRANCH_NAME == $DEV_BRANCH || $CI_COMMIT_BRANCH == $DEV_BRANCH'
      variables:
        SF_ENV: "dev"

    - if: '$CI_MERGE_REQUEST_TARGET_BRANCH_NAME == $QUAL_BRANCH || $CI_COMMIT_BRANCH == $QUAL_BRANCH'
      variables:
        SF_ENV: "qual"

    - if: '$CI_MERGE_REQUEST_TARGET_BRANCH_NAME == $VALIDATION_BRANCH || $CI_COMMIT_BRANCH == $VALIDATION_BRANCH'
      variables:
        SF_ENV: "validation"

    - if: '$CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "master" || $CI_COMMIT_BRANCH == "master"'
      variables:
        SF_ENV: "staging"

    - if: '$CI_COMMIT_BRANCH =~ /^release\// || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME =~ /^release\//'

    - if: '$CI_PIPELINE_SOURCE == "schedule"'

    - when: never

这份配置仍允许其他 release/ 分支和 schedule 创建 pipeline。未匹配到明确环境映射时,继续使用默认值 dev。如果不希望回退到 dev,应删除对应的放行规则,或补齐分支映射。

MR pipeline 通过 CI_MERGE_REQUEST_TARGET_BRANCH_NAME 判断目标分支,普通分支 pipeline 通过 CI_COMMIT_BRANCH 判断当前分支。匹配规则设置的 workflow 变量可供各 job 使用。不过,修正规则顺序仍无法解决当前 input 的 options 校验问题。参考:workflow:rules:variables

如果希望只维护一份分支到环境的映射

可以将 SF_ENV 作为普通 CI/CD 变量传给下游,取消它的 input 声明:

  1. 保留修正后的 workflow:rules:variables。
  2. 删除触发配置中的 inputs.SF_ENV。
  3. 删除下游 spec:inputs.SF_ENV。
  4. 下游使用固定 job 名,并通过 $SF_ENV 设置环境:
validate:
  image: "registry.example.com/sf-ci:$[[ inputs.DOCKER_IMAGE ]]"
  stage: validate-merge-request
  environment:
    name: "$SF_ENV"
  script:
    - |
      case "$SF_ENV" in
        dev|qual|staging|validation|production) ;;
        *) echo "Invalid SF_ENV: $SF_ENV"; exit 1 ;;
      esac
    - echo "$SF_ENV"

在默认转发设置下,workflow 定义的变量会通过 trigger job 传给下游。如果配置了 inherit:variables 或 trigger:forward,需要确认 SF_ENV 没有被排除。参考:workflow 变量转发

这样会将允许值检查移到 job 执行阶段。如果必须在创建 pipeline 时拒绝非法环境,就继续使用带 options 的 input,并传入明确值。

仅删除 options 会让字符串通过校验,无法解决变量展开问题。expand_vars 也受变量可用性限制,不能保证任意 YAML 变量都能展开,也无法补救已经失败的 input 校验。参考:expand_vars

备注:内容仅供参考。