Resolve !reference tags passed as CI/CD input values
What does this MR do and why?
A YAML tag such as !reference passed as a CI/CD input value (in include: inputs:) was never resolved. YAML tag resolution runs after input interpolation in Gitlab::Ci::Config#build_config, but the input value was stringified during interpolation, so the unresolved Gitlab::Ci::Config::Yaml::Tags::Reference object leaked into the configuration, for example:
echo '[#<Gitlab::Ci::Config::Yaml::Tags::Reference:0x... @data={...}>, my-specific]'This MR preserves the tag through input interpolation so the existing tag resolution pass (Config::Yaml::Tags::Resolver) resolves it against the fully merged configuration. It covers scalar (string) and array inputs, both when the input is the whole value of a node and when it is embedded inside a larger string.
The behavior is behind the ci_resolve_reference_tags_in_inputs feature flag, disabled by default. When the flag is disabled, behavior is unchanged.
Resolves #606739 (closed)
Implementation
lib/ci/inputs/string_input.rb:coerced_valueno longer callsto_son a YAML tag object (flag-gated), so a scalar tag survives interpolation and reaches the resolver.lib/gitlab/ci/config/interpolation/deferred_string.rb(new): when a string node interpolates a value that still carries an unresolved tag, the substitution is deferred and emitted as an object that the tag resolution pass resolves and then stringifies.lib/gitlab/ci/config/interpolation/template.rb:interpolate_string_node!emits aDeferredStringwhen a block value carries an unresolved tag (flag-gated; the flag is only evaluated when a tag is present, to avoid overhead on the common path).lib/ci/inputs/base_input.rb:run_validationsskipsoptionsandregexvalidation when the coerced value still carries an unresolved tag, because the value cannot be validated until the tag is resolved.
Feature flag
ci_resolve_reference_tags_in_inputs(gitlab_com_derisk, disabled by default).- Both enabled and disabled states are tested.
How to set up and validate locally
# list.yml
.my-shared-list:
- value1
- value2
# template.yml
spec:
inputs:
a:
type: array
---
job:
script:
- echo '$[[ inputs.a ]]' > my_script.sh
# .gitlab-ci.yml
include:
- local: /list.yml
- local: /template.yml
inputs:
a:
- !reference [.my-shared-list]
- my-specificWith the flag enabled, the Full Configuration tab shows the resolved values instead of a dumped Reference object.
Documentation
doc/ci/inputs/_index.md(Array type): describes that resolving a!referencetag in an input value is available behind the feature flag.doc/ci/yaml/yaml_optimization.md(!referencetags): corrects the description of when!referencepaths are read relative to input interpolation, and notes the flag-gated support.
Test coverage
DeferredStringunit spec (detection and resolution).- Interpolator integration spec (scalar and array, whole value and embedded, flag enabled and disabled).
- End-to-end
Gitlab::Ci::Configspec with real local includes, including a string input that definesoptions:, and the flag-disabled fallback.
MR acceptance checklist
- Feature flag added, disabled by default; both states tested.
- Unit, integration, and end-to-end tests.
- RuboCop, markdownlint, Vale, and fast-spec-helper validation passing.
- Technical Writer review of the documentation changes (can be post-merge).