Defer config error when workflow:rules exist
What does this MR do and why?
Fixes an issue where a pipeline with 0 jobs is persisted as failed (red X on MR) when include:rules: filter out all includes, even though workflow:rules would have blocked the pipeline entirely.
Root cause
The pipeline chain evaluates steps in order:
Config::Process: Processes the YAML config. Wheninclude:rules:filter out all includes and no inline jobs exist, the YAML processor reports"jobs config should contain at least one visible job"as aconfig_error. This is a persistable failure reason, so the pipeline is saved to the database as failed.EvaluateWorkflowRules: Evaluatesworkflow:rules. This step is never reached because the chain breaks atConfig::Process.
If workflow:rules would have blocked the pipeline (e.g., when: never for the current source), the pipeline should not be persisted at all (filtered_by_workflow_rules is a non-persistable reason). But because Config::Process errors first, workflow:rules is never evaluated.
Fix
-
YamlProcessor::Result: Always extract workflow attributes (workflow_rules,root_variables) and empty defaults forstages/jobsfrom the CI config, even when the result is invalid. Addsonly_no_visible_jobs_error?so we defer only this specific error, never syntax or other config errors. -
Config::Entry::Jobs: Extract the error string into aNO_VISIBLE_JOBS_ERRORconstant so the predicate matches on a shared constant, not a duplicated string. -
Config::Process: When the only error is "no visible jobs" andworkflow:rulesare present, setyaml_processor_resulton the command and store the error asdeferred_config_errorinstead of failing. This letsEvaluateWorkflowRulesrun. -
EvaluateWorkflowRules: Ifworkflow:rulespass and adeferred_config_erroris present, surface the originalconfig_error(preserving the exact message and persistable behaviour). Ifworkflow:rulesfilter the pipeline, it stays non-persistable (filtered_by_workflow_rules) and no pipeline is created.
Behaviour matrix
| Scenario | Before | After |
|---|---|---|
workflow:rules block + 0 visible jobs (include:rules filtered all) |
persisted config_error (red X) |
not persisted (filtered_by_workflow_rules) |
workflow:rules allow + 0 visible jobs |
persisted config_error |
unchanged — persisted config_error with original message |
| Syntax / other config errors | persisted config_error |
unchanged |
| Valid config | normal | unchanged |
How to reproduce
- Project
.gitlab-ci.yml:workflow: rules: - if: $CI_PIPELINE_SOURCE == 'push' && $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH when: never - if: $CI_PIPELINE_SOURCE == 'merge_request_event' - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE == 'push' include: - component: $CI_SERVER_FQDN/group/component/template@main rules: - if: $CI_PIPELINE_SOURCE == 'merge_request_event' - Push to a non-default branch
- Before: Push pipeline created with 0 jobs,
config_error, persisted as failed (red X) - After: Pipeline not persisted (
filtered_by_workflow_rules)
Reproduced on GitLab.com: https://gitlab.com/oyakovenko-group/broken-pipeline-591780/-/pipelines/2685242385
References
- Closes #591780 (closed)
MR acceptance checklist
- Unit tests for
Config::Process,EvaluateWorkflowRules, andYamlProcessor::Result - Integration tests for the full pipeline chain
- All existing specs pass
- RuboCop clean