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.
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.
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.
.gitlab-ci.yml at one exact commit.
One local file: /.gitlab/ci/ch02-base.yml.
before_script, retry,
interruptible.
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
}
3. Predict before building
Write these predictions into predictions.txt before
running CI Lint:
- The local include should resolve from the same repository revision as the root file.
-
Both jobs should inherit
retry: 1,interruptible: true, and the commonbefore_script. - Static lint should be valid and the compiled configuration should contain two visible jobs.
- After the syntax break, no pipeline/job should be materialized.
-
After the inheritance break, syntax should remain valid, but
runtime_probeshould no longer contain the inheritedbefore_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
glabavailability, whether a runner was available, and verification date.
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.
Knowledge check
Why does the checkpoint deliberately include both a syntax break and an inheritance break?
They fail at different layers: the syntax/structure error blocks valid compilation, while the inheritance change can compile successfully yet alter the executable job and cause a runtime failure.
What evidence proves the inheritance bug before a runner executes anything?
The expanded/compiled runtime_probe job showing
that the expected default before_script is absent.
Why is a runner optional in this checkpoint?
The chapter’s mandatory objective is configuration compilation and validation. Runner execution is a later layer and should not force unsafe or paid infrastructure changes.
What should be preserved before repairing the structural error?
The broken source/configuration and original lint error, tied to the exact revision/context.
After restoration, what must be true before moving to Chapter 03?
The root/include graph is understood, lint succeeds, compiled jobs match predictions, simulation produces the intended graph, and any optional runtime evidence is tied to the exact source SHA.
Official references and version notes
-
CI/CD YAML syntax reference
— current global/job keyword semantics, including
default,inherit, andinclude. - Use CI/CD configuration from other files — include resolution, merge order, overrides, nesting, and snapshot behavior.
- Validate GitLab CI/CD configuration — CI Lint, syntax checking, and pipeline simulation.
- Pipeline editor — included-file tree, visualization, continuous validation, and Full configuration.
-
Deprecated CI/CD keywords
— current migration guidance, including global
image/services/cache/before_script/after_scriptand legacyonly/except. -
glab ci lintandglab ci config— current CLI surfaces for validation and compiled configuration inspection.
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.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this
address.