workflow:rules, Job rules, if Expressions, changes, exists, Pipeline Sources, and Conditional Execution: Guided Hands-On Workflow and Core Operations
Turn the rule model into a small disposable project. You will observe push, merge-request, and manual pipeline contexts, print only safe source/ref/SHA metadata, add jobs controlled by if, changes, and exists, and compare the resulting pipeline/job graph with your predictions before touching runner or deployment behavior.
Learning objectives
- Create a disposable rule laboratory with stable paths and synthetic files.
- Observe safe source/ref/SHA metadata in push, merge-request, and manual pipeline contexts.
- Use if, changes, changes:compare_to, and exists as progressive job-selection tools.
- Compare predicted job presence with the actual pipeline graph and preserve pipeline/job IDs when available.
- Complete a challenge that chooses the correct conditional-execution layer rather than copying a finished YAML file.
1. Lab boundary and preflight
Use a throwaway project such as glci-rules-lab or a
temporary namespace you are authorized to modify. All files are
synthetic. The mandatory path requires only Git, GitLab
Free-compatible CI features, and any available non-privileged
runner. If no runner is available, you can still complete the
configuration/rule portions with CI Lint, pipeline creation
evidence, and the local prediction worksheet; job-runtime
observations are then marked not observed.
-
Create branch
glci/ch08-rulesfrom the project default branch. - Record the starting commit SHA.
-
Create synthetic paths
src/app.txt,docs/guide.md, andservices/api/service.marker. - Do not add secrets, production URLs, protected deployment credentials, or privileged runner tags.
-
Assumption snapshot: current GitLab primary docs checked
2026-09-11; examples avoid newer optional regexp forms under
changes/exists.
2. Predict before triggering
| Context | Pipeline? | Expected jobs | Why |
|---|---|---|---|
| Feature branch push, no MR | Yes |
source_probe, source-change jobs matching diff
|
Push admitted by workflow; branch pipeline. |
| Open MR update | Yes |
source_probe, MR-only, relevant changes/exists
jobs
|
MR source admitted; push duplicate suppressed. |
| New pipeline in UI | Yes | source_probe, manual-context job |
web admitted explicitly. |
| Schedule | No in first configuration | None | Schedule intentionally rejected until checkpoint design. |
3. Build the rule laboratory incrementally
Start with source_probe plus the workflow block.
Validate it, then add one job at a time. Incremental changes make it
possible to attribute a graph difference to one rule rather than
debugging a large rule matrix all at once.
workflow:
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
- if: '$CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS && $CI_PIPELINE_SOURCE == "push"'
when: never
- if: '$CI_PIPELINE_SOURCE == "push"'
- if: '$CI_PIPELINE_SOURCE == "web"'
- when: never
stages:
- inspect
- test
source_probe:
stage: inspect
script:
- printf 'source=%s\n' "$CI_PIPELINE_SOURCE"
- printf 'ref=%s\n' "$CI_COMMIT_REF_NAME"
- printf 'sha=%s\n' "$CI_COMMIT_SHA"
- printf 'branch=%s\n' "${CI_COMMIT_BRANCH:-<none>}"
- printf 'tag=%s\n' "${CI_COMMIT_TAG:-<none>}"
mr_only:
stage: test
script: echo "MR-only check"
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
src_changed:
stage: test
script: echo "Source files changed"
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
changes:
paths:
- src/**/*
- if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH'
changes:
compare_to: 'refs/heads/main'
paths:
- src/**/*
api_capability:
stage: test
script: echo "API capability exists in repository"
rules:
- exists:
- services/api/service.marker
manual_context:
stage: test
script: echo "Created from the New pipeline UI"
rules:
- if: '$CI_PIPELINE_SOURCE == "web"'
4. Validate configuration before the first commit
Use GitLab CI Lint / Pipeline Editor validation to prove the YAML compiles. Where available, simulate pipeline creation for the intended ref. Validation proves syntax/configuration consistency, not every future diff or event context. Record the commit SHA containing the exact validated configuration.
Optional CLI path if your installed glab supports it:
glab ci lint .gitlab-ci.yml
# Optional dry-run support is version-sensitive; check `glab ci lint --help` first.
5. Push context: source evidence and changes
Commit the rule configuration plus src/app.txt. Push
glci/ch08-rules. On the first branch pipeline, record
pipeline ID, source, ref, and SHA before looking at job logs. Expect
source_probe and src_changed.
mr_only should be absent.
api_capability should exist because the marker file is
part of the repository tree.
If main is not the default branch in your disposable
project, replace the hard-coded refs/heads/main in this
teaching example with the actual baseline ref or use a controlled
variable that resolves to a trusted baseline.
6. Merge-request context: prove pipeline-class switching
Open a merge request from the lab branch to the default branch. Make
a second commit that changes src/app.txt. The same push
can be eligible to create both a branch pipeline and a merge request
pipeline. The workflow suppression rule should prevent the redundant
push pipeline when CI_OPEN_MERGE_REQUESTS is set, while
allowing the merge_request_event pipeline.
Record the MR pipeline ID and source. Confirm
mr_only is present. Confirm
source_probe reports merge_request_event.
Do not infer MR context from the branch name alone.
7. Change only docs and observe job absence
Commit a change to docs/guide.md without touching
src/**/*. In the resulting MR pipeline,
src_changed should be omitted because the MR diff does
not match its changes paths. This is an important kind
of success: the expected evidence is the absence of a job,
not a skipped runtime trace.
Capture a screenshot or job-list note showing the pipeline ID and the absent job. That becomes rule-evaluation evidence.
8. Remove the marker and observe exists
On a temporary follow-up commit, delete
services/api/service.marker. The next pipeline should
omit api_capability. Restore the marker in a subsequent
commit. This proves exists follows the repository tree
at the evaluated ref and is independent of the runner workspace.
9. Manual UI pipeline: web is not push
From Build → Pipelines → New pipeline, run the lab
branch without supplying any sensitive variables. Record
CI_PIPELINE_SOURCE=web. The
manual_context job should be present;
mr_only should be absent. The push-specific
src_changed branch does not match because the source
guard is false.
An API-created pipeline would use source api, while a
trigger-token pipeline uses trigger. Do not use a real
token for this chapter. If you need an API-like exercise, model the
expected source in the worksheet and verify it later in the
dedicated API/trigger chapters.
10. Evidence table: expected versus observed
| Pipeline ID | Source | Ref / SHA | Job | Expected | Observed / explanation |
|---|---|---|---|---|---|
| record | push | lab branch / immutable SHA | src_changed | Present after src change | Fill from pipeline graph |
| record | merge_request_event | MR source / SHA | mr_only | Present | Fill from pipeline graph |
| record | merge_request_event | MR source / docs-only SHA | src_changed | Absent | Diff paths do not match |
| record | web | lab branch / SHA | manual_context | Present | Explicit web source rule |
| record | any admitted source | ref without marker | api_capability | Absent | Repository marker does not exist |
11. Mini challenge: choose the correct layer
Requirement: “Run a license scan on merge requests only when
licenses/ changes; do not create branch pipelines when
an MR is open.” Which layer owns each condition?
-
The duplicate-pipeline decision belongs in
workflow:rulesbecause it controls pipeline existence. -
The license-path condition belongs in the scan job’s
rules:changesbecause it controls one job’s membership. - Neither condition belongs in the shell script; by the time the script runs, both pipeline and job already exist.
Write your own YAML before revealing or comparing with any solution. Then validate it and explain the source/ref/diff evidence that would prove it.
12. Cleanup and rollback
- Close the disposable merge request.
-
Delete branch
glci/ch08-rulesafter preserving the non-secret evidence you need. - Remove only synthetic files created for this lab; do not delete unrelated repository paths.
- No runner, protected setting, credential, environment, package, or external system should have been modified.
Knowledge check
Why is a missing job different from a skipped job trace?
A job omitted by rules is never added to the pipeline and has no job ID/trace; a skipped job exists as pipeline state.
What source should a New pipeline UI run show?
web.
Why guard changes with a pipeline source in this lab?
It makes the intended comparison context explicit and avoids non-push contexts accidentally satisfying diff rules.
What proves rules:exists behavior?
The repository path is present/absent at the evaluated ref and the job correspondingly appears/disappears from the graph.
Where should duplicate branch/MR prevention live?
Prefer the pipeline-creation boundary, workflow:rules, because the problem is duplicate pipelines rather than one duplicate job.
Version and compatibility note
GitLab and GitLab Runner evolve continuously. Treat version-sensitive YAML, runner/executor behavior, APIs, security features, policy controls, tiers, and deprecations as assumptions to verify against the current official GitLab documentation before production use. Preserve the exact project/ref/SHA, compiled configuration, pipeline/job IDs, runner/tool versions, and external target evidence used for any reproducible lab or incident record.
Official references and version notes
Documentation verification date: 2026-09-11. GitLab CI/CD rule semantics evolve; self-managed installations should confirm their deployed GitLab version before relying on newer syntax.
-
workflowkeyword — pipeline-creation rules, duplicate-pipeline avoidance, branch-to-MR switching, and current source examples. -
Specify when jobs run with
rules— first-match evaluation,CI_PIPELINE_SOURCEvalues, expressions, schedules,changes, and duplicate-pipeline guidance. -
CI/CD YAML syntax reference
— authoritative
rules,rules:changes,compare_to,rules:exists,when, and workflow syntax. - Predefined CI/CD variables — variable availability phases and safe source/ref/SHA metadata.
-
Debugging CI/CD pipelines
— current guidance for unexpected
changesbehavior and duplicate pipelines. - Troubleshooting merge request pipelines — evidence and repair guidance for branch + MR duplicates.
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this
address.