Chapter 08Lesson 02~185 minutes

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.

Hands-onifchangesexistsEvidence

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-rules from the project default branch.
  • Record the starting commit SHA.
  • Create synthetic paths src/app.txt, docs/guide.md, and services/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?

  1. The duplicate-pipeline decision belongs in workflow:rules because it controls pipeline existence.
  2. The license-path condition belongs in the scan job’s rules:changes because it controls one job’s membership.
  3. 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-rules after 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?

What source should a New pipeline UI run show?

Why guard changes with a pipeline source in this lab?

What proves rules:exists behavior?

Where should duplicate branch/MR prevention live?

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.

Next lesson

workflow:rules, Job rules, if Expressions, changes, exists, Pipeline Sources, and Conditional Execution: Configuration, Design Choices, and Tradeoffs

Compare maintainable conditional-execution designs and decide when explicit source allowlists, diff rules, and repository capability checks are the right abstraction.

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.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.