fix: Align CI schema with backend keyword validation
What does this MR do and why?
This MR is the result of an audit of the pipeline editor's CI/CD JSON schema (app/assets/javascripts/editor/schema/ci.json) against the backend and the docs, comparing doc/ci/yaml/_index.md and doc/ci/yaml/artifacts_reports.md, the backend ALLOWED_KEYS constants under lib/gitlab/ci/config/ and ee/lib/gitlab/ci/config/, and the schema file. The schema is hand-maintained — nothing generates or syncs it — and the only safeguard is a non-blocking Danger warning in danger/ci_config/Dangerfile that fires when an MR touches lib/gitlab/ci/config/entry without updating the schema. Because that warning does not block and is easy to miss, the schema had drifted behind the backend on the following keywords, all wrongly rejected by the editor even though the backend accepts them and the same configuration passes CI Lint and runs:
| Keyword | Backend source |
|---|---|
spec:description (string, max 256 chars) |
lib/gitlab/ci/config/header/spec.rb:19 |
spec:include (local, remote, project only) |
lib/gitlab/ci/config/header/include.rb:11 |
spec:component (array; name, sha, version, reference) |
lib/gitlab/ci/config/header/component.rb:16 |
dast_configuration job keyword (site_profile, scanner_profile) — was absent from the schema entirely |
ee/lib/gitlab/ci/config/entry/dast_configuration.rb:14 |
artifacts:public |
lib/gitlab/ci/config/entry/artifacts.rb:17 |
artifacts:reports:api_fuzzing and artifacts:reports:coverage_fuzzing |
lib/gitlab/ci/config/entry/reports.rb:15-21 |
include:cache (remote includes only; true or a duration string) |
app/validators/ci/include_cache_validator.rb |
Alongside those additions, spec:inputs:*:rules is tightened. It was typed as a bare object that accepted anything; it is now constrained to if / options / default, and made mutually exclusive with the input-level options and default keys (lib/gitlab/ci/config/header/input/rules/rule.rb:16, lib/gitlab/ci/config/header/input.rb:23-24).
Every change in the table above is additive: it only widens what the schema accepts, so no configuration that validates today starts failing. Three changes work the other way and narrow the schema — the spec:inputs:rules tightening above, the boolean-only rulesAllowFailure on job-level rules, and the if/then guard that stops artifacts:access being combined with artifacts:public. All three are safe for the same reason: the backend already rejects those shapes, so nothing that passes today starts failing. That is what separates them from the deferred items below, which are also cases where the schema is too permissive but where tightening would newly break configuration that currently validates.
The audit also found the following, deliberately left unchanged:
-
Keywords the schema wrongly accepts (the opposite direction — schema too permissive):
default:identity— not inDefault::ALLOWED_KEYS(lib/gitlab/ci/config/entry/default.rb:16-17)artifacts:reports:license_management— dropped fromReports::ALLOWED_KEYSneeds:parallelon the cross-pipeline and cross-project variants — not allowed bylib/gitlab/ci/config/entry/need.rb:84oree/lib/ee/gitlab/ci/config/entry/need.rb:56
Not changed here: removing them would make configuration that passes validation today start showing errors, a different risk class that belongs in its own MR.
-
artifacts:reports:cluster_image_scanningandartifacts:reports:requirements_v2— allowed bylib/gitlab/ci/config/entry/reports.rb:15-21but absent fromdoc/ci/yaml/artifacts_reports.md, which documents 20 report types. Both are real features that appear elsewhere in the API and GraphQL documentation. Left out because adding undocumented keywords to editor autocomplete would advertise keywords a user cannot look up. Flagged so a reviewer can decide whether the schema or the documentation is the side that should change. -
artifacts:reports:performance— allowed by the backend, butspec/frontend/editor/schema/ci/yaml_tests/negative_tests/artifacts.ymlhas a deliberate negative test asserting it is invalid, annotated "Superseded by: artifact:reports:browser_performance". Left as-is rather than silently reversing what looks like an intentional decision. -
Root-level
include:artifact/include:job—lib/gitlab/ci/config/entry/include.rb:16shares one entry class between rootinclude:andtrigger:include, so the backend accepts these at the root, but they are documented only fortrigger:includeand appear inert there. Flagged for a reviewer rather than added, since adding them would surface them in editor autocomplete. -
image:ports/services:ports— looks like a gap but is not one.lib/gitlab/ci/config/entry/imageable.rb:24disallowsportsunless thewith_image_portsoption is set, and the only caller islib/web_ide/config.rb:14for the Web IDE terminal config. Genuinely invalid in.gitlab-ci.yml; the schema is correct as-is. -
workflow:rules:start_in,workflow:rules:allow_failureandworkflow:rules:needs- tolerated by the backend, butlib/gitlab/ci/config/entry/workflow.rb:23we have a note: "should not be allowed but we can't break this behavior now" (#436473), anddoc/ci/yaml/_index.md:818says they are not supported inworkflow:rules, have no effect, and should not be used. The backend accepting them is an acknowledged bug rather than a gap.
How to set up and validate locally
-
Run
yarn jest spec/frontend/editor/schema/ci/ci_schema_spec.jsand confirm all tests pass. -
Open the pipeline editor on any project and paste the following component header, then confirm no schema error appears on
description:spec: description: "Example component" inputs: stage: default: test --- component-job: stage: $[[ inputs.stage ]] script: - echo "hello"
New positive and negative YAML fixtures were added under spec/frontend/editor/schema/ci/yaml_tests/ for every keyword in the table above. One detail worth knowing when reviewing them: the spec runner re-wraps each top-level key of a fixture into its own document, so a fixture that nests spec: under a descriptive key is validated as a job named that key and fails for the wrong reason. The new spec: negative fixtures therefore put spec: at the top level, one case per file, mirroring the existing negative_tests/project_path/include/ layout. Each was additionally checked to fail at its intended schema path — for example /spec/description maxLength and /spec/include/0 additionalProperties: inputs — rather than incidentally.
References
- Schema contribution guide: https://docs.gitlab.com/development/cicd/schema/
- Closes #614906 (closed)