Chapter 12Lesson 05~195 minutes

Checkpoint Lab — YAML Reuse with include, extends, Anchors, !reference, Hidden Jobs, and Template Architecture

The checkpoint lab refactors a duplicated pipeline using two reuse mechanisms, proves the intended compiled jobs are equivalent, deliberately breaks one inheritance edge, repairs it from merged-configuration evidence, and records the exact include/ref/override state required for safe reuse.

YAML reuseincludeextends!referenceTemplate trust

Learning objectives

  • Refactor a duplicated pipeline using both extends and a cross-file reuse mechanism.
  • Prove the intended compiled jobs are equivalent using expanded configuration rather than visual similarity of source files.
  • Inject and repair an inheritance failure that replaces an array unexpectedly.
  • Produce an evidence packet covering include source/ref/integrity, merged jobs, overrides, pipeline identity, and assumptions.
  • Document the controls required before converting local templates into organization-wide components in Chapter 13.

1. Checkpoint scenario and acceptance criteria

You inherit a pipeline with three nearly identical validation jobs. Refactor it so duplication is reduced using (1) a local included hidden job plus extends, and (2) a reusable rules array selected with !reference. Prove from expanded configuration that all intended final jobs remain equivalent in image/stage/rules/setup, while job-specific scripts remain explicit.

Then deliberately break one inheritance edge by overriding an array, preserve the failed evidence, repair it without hiding the root cause, and document how the same template would be pinned if moved to a shared project.

2. Preflight and assumptions

  • Disposable project glci-ch12-checkpoint and branch glci/ch12-checkpoint.
  • GitLab Free-compatible core syntax; current GitLab behavior verified 2026-09-11.
  • Any ordinary trusted runner; no privileged execution, cloud account, registry, or production credentials.
  • CI Lint/pipeline editor available for merged/expanded configuration inspection.
  • If no runner exists, completing compilation/expanded-YAML evidence plus local shell simulation is an accepted faithful path; label runtime fields “not observed.”

3. Predict state changes before editing

Prediction How to verify independently
Local include becomes part of compiled config at same source SHA Expanded YAML shows hidden template and inherited final jobs
Final jobs preserve image/stage/setup while scripts stay job-specific Compare expanded jobs key-by-key
Rules from !reference appear concretely in final jobs Expanded YAML shows resolved rules array
Deliberate child before_script override removes parent array Expanded YAML proves replacement before job execution
Repair restores intended setup without changing unrelated jobs Diff expanded config + run only disposable pipeline

4. Create the reusable local template

# ci/checkpoint.yml
.validation_base:
  stage: test
  image: alpine:3.20.3
  before_script:
    - echo "prepare:$CI_COMMIT_SHA"
    - mkdir -p evidence
  variables:
    CH12_CONTRACT: "v1"

.validation_rules:
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
    - if: '$CI_PIPELINE_SOURCE == "push"' 

The hidden job is the hash-like job template. The separate hidden rules object is selected explicitly rather than relying on array merging.

5. Build the root configuration

include:
  - local: /ci/checkpoint.yml

stages: [test]

lint_config:
  extends: .validation_base
  rules: !reference [.validation_rules, rules]
  script:
    - echo "lint:$CH12_CONTRACT" | tee evidence/lint.txt
  artifacts:
    when: always
    paths: [evidence/]
    expire_in: 1 day

unit_config:
  extends: .validation_base
  rules: !reference [.validation_rules, rules]
  script:
    - echo "unit:$CH12_CONTRACT" | tee evidence/unit.txt
  artifacts:
    when: always
    paths: [evidence/]
    expire_in: 1 day

integration_config:
  extends: .validation_base
  rules: !reference [.validation_rules, rules]
  script:
    - echo "integration:$CH12_CONTRACT" | tee evidence/integration.txt
  artifacts:
    when: always
    paths: [evidence/]
    expire_in: 1 day

6. Validate and capture the compiled contract before execution

Run CI Lint and inspect the expanded configuration. Save a text/PDF/screenshot-equivalent evidence note containing:

  • source SHA and branch,
  • root include path,
  • the fully expanded keys for all three jobs,
  • resolved rules, image, before_script, variables, script, and artifacts,
  • the validation result and timestamp.

If your GitLab provides pipeline simulation, use it to confirm expected jobs for a push. Validation success proves configuration shape, not runner execution or artifact upload.

7. Run once and capture runtime evidence

Push the disposable branch. Record pipeline source/ref/SHA, pipeline ID, each job ID/status, runner ID/version/executor, and the three small evidence artifacts. Do not store secrets in the artifacts.

printf 'source=%s
ref=%s
sha=%s
pipeline=%s
job=%s
runner=%s
'   "$CI_PIPELINE_SOURCE" "$CI_COMMIT_REF_NAME" "$CI_COMMIT_SHA"   "$CI_PIPELINE_ID" "$CI_JOB_ID" "${CI_RUNNER_ID:-unknown}"

8. Deliberately break one inheritance edge

Change only unit_config:

unit_config:
  extends: .validation_base
  rules: !reference [.validation_rules, rules]
  before_script:
    - echo "unit-only-setup"
  script:
    - test -d evidence
    - echo "unit:$CH12_CONTRACT" | tee evidence/unit.txt

Predict the result before running: the child before_script replaces the parent array, so evidence/ is not created and the first test -d evidence fails. Preserve the expanded job showing the replacement and the first failing job trace.

9. Repair the causal configuration, not the symptom

Do not add mkdir -p evidence into the job’s script merely to make it pass; that would hide the inheritance misunderstanding. Remove the child before_script override, or define a deliberately reusable setup array and reference it explicitly. Then create a new pipeline and prove the expanded configuration again.

10. Model the shared-project promotion path

If ci/checkpoint.yml later moves to a shared project, document this reviewed consumer form:

include:
  - project: 'platform/ci-library'
    ref: 'fedcba9876543210fedcba9876543210fedcba98'
    file: '/templates/validation.yml' 

The exact project path and SHA become part of the reusable dependency evidence. Chapter 13 will turn this idea into a versioned component/interface model with explicit inputs.

11. Required evidence packet

Evidence Minimum content
Source identity Project, branch/ref, exact source SHA, pipeline source
Configuration dependency Root file + local include path; simulated future project path/full SHA
Compiled configuration Expanded lint/unit/integration jobs before, broken, and repaired states
Pipeline/job records Pipeline ID, job IDs/statuses, runner/executor if observed
Artifacts Small non-secret evidence files and expiry
Failure record First broken unit_config trace + expanded before_script replacement
Assumptions/limits GitLab offering/version, no privileged/paid/cloud dependency, runtime-not-observed if simulation only

12. Verification checklist and cleanup

  • Exactly three intended jobs exist for the chosen pipeline source.
  • All three expanded jobs have the same intended image/stage/setup/contract variable.
  • Each job’s script remains explicit and unique.
  • The deliberate failure is explained by array replacement, not runner state.
  • The repaired pipeline restores the parent setup and succeeds without unrelated changes.
  • No real token, secret, privileged runner, production URL, registry, or external deployment appears anywhere.

After saving the evidence packet, delete only the disposable branch/project created for this lab.

13. What Chapter 12 adds to the production operating model

You can now treat shared YAML as executable, versioned operational code: its provenance is explicit, its merge contract is inspectable, and failures are diagnosed from the compiled configuration. Chapter 13 builds on this foundation with CI/CD components, the Component Catalog, typed inputs, versioned reuse, and organization-wide building blocks.

Knowledge check

The broken unit job fails because evidence/ is missing. Which layer is causal?

Why is adding mkdir to script a weak repair?

What proves the refactor preserved intended configuration before any runner executes?

Which identity must be added to the evidence packet if the local include becomes include:project?

Why bridge next to CI/CD components rather than keep growing hidden-job conventions forever?

Next lesson

Chapter 13 — CI/CD Components and versioned building blocks

Turn reusable YAML conventions into stronger versioned interfaces with components, catalog distribution, and explicit inputs.

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. CI reuse and include behavior evolves across GitLab releases, especially include subkeys, integrity/caching, inputs/components, and template deprecations. Re-check the deployed GitLab version before relying on newer include features on Self-Managed or Dedicated installations.

Current behavior used by this chapter: includes are resolved first and the root configuration is merged afterward; all include resolution has a 30-second time limit and the default nested-include limit is 150. A project include can pin a full 40-character commit SHA. Remote includes are public HTTP(S) GETs without authentication and can use integrity with a base64 SHA-256. YAML anchors cannot cross files. extends reverse-deep-merges hashes but array values are replaced, not concatenated. GitLab supports up to eleven inheritance levels but recommends avoiding deep inheritance; !reference can select configuration from included files and has parsing constraints with CI/CD inputs.

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.