GitLab CI "rules:changes" Not Matching - Job Always or Never Runs
rules:changes runs a job only when matching files changed. It misfires when the glob is wrong, when there is no comparison base, or when the pipeline source has no diff to evaluate.
What this error means
A job guarded by rules:changes runs on every pipeline (even when its files did not change) or never runs (even when they did). The path glob and the compare base are the usual culprits.
# Expected to run only when src/ changed, but it runs every push:
build:
rules:
- changes:
- src/* # only top-level of src/, misses src/app/main.tsCommon causes
Glob does not match nested paths
src/* matches only the immediate children of src/. To match any depth you need src/**/*. A too-narrow glob makes the rule never match real changes.
No comparison base on branch pipelines
On a branch pipeline, changes compares against the previous commit. On the first pipeline for a new branch, or a pipeline with no diff, GitLab treats everything as changed, so the job always runs.
Wrong pipeline source for changes
For reliable MR diffs, the job should run in a merge request pipeline. In a plain branch pipeline the compare base is the prior commit, which can differ from the MR diff you intended.
How to fix it
Use recursive globs and an MR source
Match nested paths and evaluate changes against the MR target for predictable diffs.
build:
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
changes:
- src/**/*
- package-lock.jsonSet a compare target for branch pipelines
Pin what changes compares against so the first-pipeline "everything changed" behavior is controlled.
build:
rules:
- changes:
paths:
- src/**/*
compare_to: 'refs/heads/main'How to prevent it
- Prefer
src/**/*recursive globs over single-levelsrc/*. - Evaluate
changesin merge request pipelines for stable diffs. - Use
compare_toto control the base on branch pipelines.