Chapter 02Lesson 05~140 minutes

Checkpoint Lab — .gitlab-ci.yml Structure, Pipeline Compilation, Keywords, Defaults, and Configuration Validation

The checkpoint combines the chapter into one evidence-driven exercise. You will build a small multi-job configuration with a local include and defaults, validate and compile it, deliberately break syntax, then create an inheritance defect that survives static syntax checks but changes runtime behavior. You will restore a verified pipeline and preserve a compact evidence packet.

Checkpoint labEvidence packetBroken syntaxInheritance driftRecovery

Learning objectives

  • Construct and validate a multi-job configuration using one local include, safe defaults, and a deliberate inheritance exception.
  • Predict the compiled jobs and inherited values before running any pipeline.
  • Capture one syntax failure and one inheritance/runtime failure without overwriting the original evidence.
  • Restore the configuration, validate/compile it again, and optionally execute it when an eligible disposable runner is available.
  • Package evidence that ties the final state to the exact source SHA, pipeline source, configuration graph, jobs, and assumptions.
Checkpoint contract. This lab uses a disposable project/branch, a repository-local include, synthetic files, and no real credentials. A runner is optional. The mandatory evidence is configuration source + include graph + compiled configuration + validation/simulation; runtime evidence is added only when an authorized disposable runner is available.

1. Checkpoint goal and expected state

You will build two jobs in one verify stage. Both depend on a common default before_script that creates a synthetic marker file. One job also writes a tiny evidence file if runtime is available. You will prove the good compiled state, break YAML syntax, restore it, then break inheritance so one job loses the default bootstrap step.

Root configuration

.gitlab-ci.yml at one exact commit.

Include graph

One local file: /.gitlab/ci/ch02-base.yml.

Defaults

before_script, retry, interruptible.

Final jobs

compile_probe and runtime_probe.

2. Preflight and resource guards

LAB_BRANCH="ch02/checkpoint"
EVIDENCE="ch02-checkpoint-evidence"
mkdir -p "$EVIDENCE" .gitlab/ci

glab auth status
git status --short --branch
git rev-parse HEAD | tee "$EVIDENCE/head-before.txt"

test ! -e .gitlab-ci.yml || {
  echo "Existing .gitlab-ci.yml detected; use a disposable project/branch."
  exit 2
}
Stop if the project has unrelated CI/CD state. The checkpoint must not overwrite a real project pipeline, protected deployment configuration, or organization-managed policy.

3. Predict before building

Write these predictions into predictions.txt before running CI Lint:

  1. The local include should resolve from the same repository revision as the root file.
  2. Both jobs should inherit retry: 1, interruptible: true, and the common before_script.
  3. Static lint should be valid and the compiled configuration should contain two visible jobs.
  4. After the syntax break, no pipeline/job should be materialized.
  5. After the inheritance break, syntax should remain valid, but runtime_probe should no longer contain the inherited before_script; if executed, it should fail the marker check.

4. Build the good configuration

# .gitlab/ci/ch02-base.yml
default:
  retry: 1
  interruptible: true
  before_script:
    - printf 'prepared-for=%s\n' "$CI_COMMIT_SHA" > .ch02-prepared

# .gitlab-ci.yml
include:
  - local: /.gitlab/ci/ch02-base.yml

stages:
  - verify

compile_probe:
  stage: verify
  script:
    - test -f .ch02-prepared
    - printf 'compile_probe=%s\n' "$CI_COMMIT_SHA"

runtime_probe:
  stage: verify
  script:
    - test -f .ch02-prepared
    - printf 'source=%s\nsha=%s\n' "$CI_PIPELINE_SOURCE" "$CI_COMMIT_SHA" > ch02-runtime-evidence.txt
  artifacts:
    when: always
    expire_in: 1 day
    paths:
      - ch02-runtime-evidence.txt

The artifact is synthetic and contains no secret. If no runner executes the job, artifact evidence will simply be absent and the lab remains valid through compiled/simulation evidence.

5. Validate, compile, and simulate the good state

glab ci lint .gitlab-ci.yml --include-jobs \
  | tee "$EVIDENCE/01-good-lint.txt"

glab ci config compile \
  | tee "$EVIDENCE/02-good-compiled.yml"

glab ci lint .gitlab-ci.yml --dry-run --ref "$LAB_BRANCH" --include-jobs \
  | tee "$EVIDENCE/03-good-simulation.txt"

Compare the compiled configuration to your predictions. Specifically prove that both jobs contain the inherited before_script, retry, and interruptible.

6. Break syntax/structure and preserve the first failure

Copy the good file, then deliberately replace the stages list with a scalar:

cp .gitlab-ci.yml "$EVIDENCE/good-root.yml"
cp .gitlab/ci/ch02-base.yml "$EVIDENCE/good-base.yml"

# Deliberate break:
stages: verify
glab ci lint .gitlab-ci.yml \
  > "$EVIDENCE/04-broken-structure.txt" 2>&1 || true
cat "$EVIDENCE/04-broken-structure.txt"
cp "$EVIDENCE/good-root.yml" .gitlab-ci.yml

Record: failure layer = configuration validation; expected pipeline ID = none; runner evidence = not applicable.

7. Break inheritance without breaking syntax

Now add this block to runtime_probe:

runtime_probe:
  stage: verify
  inherit:
    default: false
  script:
    - test -f .ch02-prepared
    - printf 'source=%s\nsha=%s\n' "$CI_PIPELINE_SOURCE" "$CI_COMMIT_SHA" > ch02-runtime-evidence.txt

Static syntax can still be valid. Compile the configuration and prove runtime_probe no longer contains the default before_script. If an authorized runner executes it, the test -f .ch02-prepared command should fail. Preserve both compiled and runtime evidence before repair.

glab ci lint .gitlab-ci.yml --include-jobs \
  | tee "$EVIDENCE/05-inheritance-lint.txt"

glab ci config compile \
  | tee "$EVIDENCE/06-inheritance-compiled.yml"

8. Restore and prove the repaired state

Remove inherit: default: false (or deliberately select the required defaults if you want a justified exception). Re-run lint, compile, and simulation. The final evidence must show the inherited bootstrap restored.

cp "$EVIDENCE/good-root.yml" .gitlab-ci.yml

glab ci lint .gitlab-ci.yml --include-jobs \
  | tee "$EVIDENCE/07-restored-lint.txt"

glab ci config compile \
  | tee "$EVIDENCE/08-restored-compiled.yml"

glab ci lint .gitlab-ci.yml --dry-run --ref "$LAB_BRANCH" --include-jobs \
  | tee "$EVIDENCE/09-restored-simulation.txt"

9. Optional disposable execution

If and only if the project has an authorized disposable runner, commit the restored configuration and push the lab branch. Do not register an employer/production runner or change runner policy for this checkpoint.

git switch -c "$LAB_BRANCH"
git add -- .gitlab-ci.yml .gitlab/ci/ch02-base.yml
git diff --cached --check
git diff --cached
git commit -m "ch02: checkpoint compiled configuration"
LAB_SHA="$(git rev-parse HEAD)"
printf '%s\n' "$LAB_SHA" | tee "$EVIDENCE/final-sha.txt"
# Optional authorized side effect:
# git push -u origin "$LAB_BRANCH"

If executed, record pipeline source/ref/SHA, pipeline ID, both job IDs, runner identity, final status, and the synthetic artifact. A green job proves only those commands completed; it does not prove deployment or external health because this checkpoint has no deployment.

10. Evidence packet and verification checklist

  • Source: final commit SHA, root CI file, local include path.
  • Compiled state: good, inheritance-broken, and restored compiled configurations.
  • Validation: good lint, structural failure output, restored lint.
  • Simulation: expected two-job graph for the selected ref/context.
  • Runtime if available: pipeline/job IDs, runner/executor identity, first failure, restored success, artifact content.
  • Assumptions: GitLab offering/tier, current glab availability, whether a runner was available, and verification date.
Acceptance test: another engineer should be able to explain why the syntax failure occurred, why the inheritance failure was different, and how the compiled configuration proves the repair without relying on memory or screenshots alone.

11. Cleanup / rollback

Remove only the disposable resources created by this lab. Keep the evidence ZIP/local folder if you want it for study, but do not commit secrets or large diagnostic dumps.

git status --short
# If a disposable remote branch was pushed, verify exact identity first:
git ls-remote --heads origin "$LAB_BRANCH"
# Then, only if authorized and still disposable:
# git push origin --delete "$LAB_BRANCH"
# git switch main
# git branch -D "$LAB_BRANCH"

12. Chapter checkpoint result

You now have a repeatable method for GitLab CI configuration: identify the exact source revision, map the include graph, inspect the compiled configuration, distinguish defaults from job overrides and inheritance exceptions, validate statically, simulate pipeline creation when source context matters, preserve first-failure evidence, and execute only when runtime evidence is actually needed.

Next lesson

Chapter 03 — Jobs, Stages, Scripts, Images, Services, before_script, after_script, and Exit Behavior

Chapter 03 moves from configuration compilation into job execution semantics: what a job actually runs, how stages order work, how scripts and lifecycle hooks behave, and how container/service execution changes runtime state.

Knowledge check

Why does the checkpoint deliberately include both a syntax break and an inheritance break?

What evidence proves the inheritance bug before a runner executes anything?

Why is a runner optional in this checkpoint?

What should be preserved before repairing the structural error?

After restoration, what must be true before moving to Chapter 03?

Official references and version notes

Version and compatibility note

Version-sensitive statements in this chapter were rechecked against current primary GitLab documentation on 2026-09-11. Core CI Lint, Pipeline Editor validation, default, inherit, and local includes are documented across GitLab Free, Premium, and Ultimate on GitLab.com, GitLab Self-Managed, and GitLab Dedicated. The labs deliberately avoid depending on paid policy features, production credentials, external cloud accounts, or privileged runner administration.

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.