Chapter 06Lesson 05~185 minutes

Checkpoint Lab — CI/CD Variables, Predefined Variables, File Variables, Masking, Protection, Expansion, and Precedence

The checkpoint builds a controlled precedence matrix using only synthetic values. You will predict the winner before each run, prove the effective value in several contexts, diagnose one protection failure and one expansion/typing failure, collect evidence without leaking secrets, and clean up only the variables and branches created by the lab.

Checkpoint labPrecedence matrixFailure injectionEvidence packetCleanup

Learning objectives

  • Construct a small precedence matrix with synthetic values and predict the winner before each pipeline run.
  • Prove results across YAML-only, project-variable, optional pipeline-variable, protected/unprotected, and file-variable contexts.
  • Inject one deliberate protection failure and one expansion/typing failure, then repair only the causal configuration.
  • Produce an evidence packet containing exact source/ref/SHA, pipeline/job IDs, variable provenance/type/protection metadata, effective non-secret values, and assumptions.
  • Remove only the disposable variables/branches created for Chapter 06 and bridge the operating model into Chapter 07 secrets management.
Checkpoint boundary: every value in this lab is synthetic. The checkpoint must prove precedence/protection/file-variable behavior without exporting the entire environment or exposing any real credential. If a required project-setting mutation is unavailable to your role, use the included faithful expected-state row rather than requesting production access.

1. Checkpoint goal and evidence contract

Build a small variable matrix that answers four questions with evidence:

  1. Which YAML value wins when only YAML sources exist?
  2. What changes when the same key exists as a project variable?
  3. What changes when a higher-precedence pipeline value is allowed?
  4. How do protected and file variables differ from ordinary values?

Before each run, write your expected winner and expected protected/file state. After the run, capture the exact pipeline/job IDs, source/ref/SHA, runner ID/executor, and only the synthetic effective values or presence/hash metadata.

2. Preflight and assumptions

  • Disposable GitLab project; no proprietary source or production settings.
  • GitLab Free path is sufficient for project variables, file variables, pipelines, and protected branches when you have the required role.
  • Any runner capable of executing the simple shell commands is acceptable; record Runner version/executor if visible.
  • No real secrets; no cloud/Kubernetes/registry side effects.
  • Pipeline-variable row is optional if the project correctly restricts that capability; do not weaken a non-lab policy.
LAB_BRANCH="ch06/checkpoint"
PROTECTED_BRANCH="ch06/checkpoint-protected"
EVIDENCE_DIR="ch06-checkpoint-evidence"
mkdir -p "$EVIDENCE_DIR"
git status --short --branch
git rev-parse HEAD | tee "$EVIDENCE_DIR/head-before.txt"

3. Use one deterministic checkpoint configuration

stages: [inspect]

variables:
  LAB_WINNER: "yaml-default"
  BASE_FRAGMENT: "green"
  EXPANDED_FRAGMENT: "$BASE_FRAGMENT-release"
  LITERAL_FRAGMENT:
    value: "$BASE_FRAGMENT-release"
    expand: false

checkpoint_probe:
  stage: inspect
  variables:
    LAB_WINNER: "yaml-job"
  script:
    - printf 'source=%s\n' "$CI_PIPELINE_SOURCE"
    - printf 'pipeline=%s job=%s\n' "$CI_PIPELINE_ID" "$CI_JOB_ID"
    - printf 'ref=%s sha=%s\n' "$CI_COMMIT_REF_NAME" "$CI_COMMIT_SHA"
    - printf 'runner=%s executor_evidence=job-metadata\n' "${CI_RUNNER_ID:-unknown}"
    - printf 'winner=%s\n' "$LAB_WINNER"
    - printf 'expanded=%s\n' "$EXPANDED_FRAGMENT"
    - printf 'literal=%s\n' "$LITERAL_FRAGMENT"
    - if test -n "${LAB_PROTECTED_FLAG:-}"; then echo 'protected_present=yes'; else echo 'protected_present=no'; fi
    - if test -n "${LAB_CONFIG_FILE:-}"; then test -f "$LAB_CONFIG_FILE"; printf 'file_bytes=%s\n' "$(wc -c < "$LAB_CONFIG_FILE")"; sha256sum "$LAB_CONFIG_FILE" | sed 's/  .*/  <LAB_CONFIG_FILE>/'; else echo 'file_variable=absent'; fi

4. Prediction matrix: write this before executing

Run Variable sources Expected winner Protected flag File variable
A YAML default + YAML job yaml-job Absent unless separately configured. Absent unless separately configured.
B Run A + project LAB_WINNER=project-setting project-setting Same as context. Same as context.
C (optional) Run B + allowed pipeline LAB_WINNER=manual-pipeline manual-pipeline Same as ref context. Same.
D Project protected flag on unprotected branch Project winner no Present if configured.
E Same config on exact protected lab branch Project winner yes Present if configured.

Also predict expanded=green-release and literal=$BASE_FRAGMENT-release for every run unless you deliberately inject the expansion failure later.

5. Run A: YAML-only baseline

Ensure none of the four LAB_ project variables exist yet. Commit/push the checkpoint branch and preserve the SHA:

git switch -c "$LAB_BRANCH"
git add -- .gitlab-ci.yml
git diff --cached --check
git commit -m "ch06: checkpoint variable matrix"
CHECKPOINT_SHA="$(git rev-parse HEAD)"
printf '%s\n' "$CHECKPOINT_SHA" | tee "$EVIDENCE_DIR/checkpoint-sha.txt"
git push -u origin "$LAB_BRANCH"

Expected: winner=yaml-job. Record pipeline/job identifiers and compare the job SHA with CHECKPOINT_SHA.

6. Run B: project variable overrides YAML

Create project variable LAB_WINNER=project-setting. Rerun on the same ref/SHA when possible or record the new SHA if configuration changed. Expected: winner=project-setting.

This run is the minimum proof that variable source precedence changes behavior without changing the job definition. Preserve both A and B job URLs/IDs.

7. Run C (optional): pipeline variable overrides project variable

If the disposable project permits pipeline variables for your role, start a manual pipeline with LAB_WINNER=manual-pipeline. Expected: the pipeline value wins. Record the pipeline source (normally web for a Run pipeline UI action), ref/SHA, and variable-setting context.

If pipeline variables are disabled, record not executed due to project security setting and retain the current documented expected precedence. Do not lower an organization security setting to manufacture a green lab.

8. Add file and protected variables, then prove access boundaries

Create:

  • LAB_CONFIG_FILE — File type, fake content mode=checkpoint source=synthetic.
  • LAB_PROTECTED_FLAG — ordinary variable, Protect variable enabled, arbitrary synthetic value.

Run on the unprotected checkpoint branch. Expected: file metadata/hash is present; protected flag is absent. Then create and protect the exact ch06/checkpoint-protected branch in the disposable project and run the same SHA/configuration there where practical. Expected: protected_present=yes.

Never print either raw variable content. The checkpoint proves interface and authorization, not value disclosure.

9. Failure injection 1: break expansion/typing deliberately

Choose one safe failure:

  • Expansion failure: change EXPANDED_FRAGMENT to the object form with expand: false while still expecting green-release. Observe the literal value, diagnose the YAML expansion setting, then restore it.
  • Typing failure: add LAB_NUMERIC_ID: 012345 unquoted, observe the parsed value in a synthetic-only job, then repair to "012345".

Preserve the failing pipeline/job ID before the repair. Do not combine both failures in one run; one causal change per experiment keeps evidence interpretable.

10. Failure injection 2: treat absence as a diagnostic, not a reason to weaken protection

On the unprotected branch, the protected flag is deliberately absent. Write a hypothetical consumer that requires it and fails closed:

require_protected_flag:
  script:
    - test -n "${LAB_PROTECTED_FLAG:-}" || { echo 'required protected capability unavailable'; exit 3; }
    - echo 'protected capability present (value not printed)' 

Run it on the unprotected lab branch and preserve the failure. The repair is to run that capability from the protected lab branch (or redesign the workflow), not to clear the variable’s protection.

11. Verification checklist

  • Every executed row records project/path, pipeline source, ref, SHA, pipeline ID, job ID, and runner ID/description where visible.
  • Run A proves job YAML beats top-level YAML.
  • Run B proves project setting beats YAML.
  • Run C either proves allowed pipeline-variable precedence or documents why the security setting prevented the experiment.
  • File variable is proven by temporary-file existence, byte count, and hash—never content.
  • Protected variable is proven by presence/absence across controlled ref contexts—never content.
  • One expansion/typing failure and one protection failure retain their first-failure evidence and causal repairs.

12. Exact cleanup and rollback

Before deleting anything, list the exact lab resources in your notes. Then:

  • Delete only project variables LAB_WINNER, LAB_CONFIG_FILE, and LAB_PROTECTED_FLAG (plus LAB_MASKED_DEMO if you created it earlier).
  • Remove only the exact protection rule for ch06/checkpoint-protected if it was created solely for this lab.
  • Delete only the two Chapter 06 lab branches after confirming their names and remote SHAs.
  • Restore any disposable project pipeline-variable setting you changed; preferably do not change it at all.
  • Keep non-secret evidence/notes; do not retain temporary variable files or secret-like raw values in artifacts.

13. Required evidence packet

Source identity

Project/path, pipeline source, ref, immutable SHA, pipeline ID.

Job identity

Job ID/name/status and first-failure job IDs before repair.

Runner context

Runner ID/description/version/executor where visible; enough to show execution context.

Variable provenance

For each LAB_ key: source/scope/type/protection/visibility/expansion metadata, never real sensitive value.

Effective value

Only synthetic LAB_WINNER/expansion values and boolean presence for protected data.

File evidence

Temporary file path existence, byte count, hash, and type metadata; no raw content.

Failure evidence

Expansion/typing failure plus protected-availability failure, each with causal repair.

Assumptions

Offering/tier, role, pipeline-variable restriction, protected-branch permissions, runner availability.

Cleanup

Proof that only the disposable variables/protection rules/branches were removed.

14. What Chapter 06 adds to the operating model

You can now treat CI/CD variables as governed data rather than magical environment strings: identify their provenance, availability phase, type, visibility/protection, expansion mechanism, and precedence; prove the effective non-secret value; and diagnose failures without dumping secrets.

Chapter 07 moves from variable mechanics into secrets management: external providers, protected data, rotation, and least-privilege retrieval. The key bridge is that masking/protection are useful controls but are not a complete secret-lifecycle strategy.

Next lesson

Next: Secrets Management, External Secret Providers, Protected Data, Rotation, and Least-Privilege Patterns: Concepts, Architecture, and Mental Model

Continue with the next lesson in the course sequence and carry forward the evidence-first GitLab CI/CD operating model.

Knowledge check

What is the minimum proof that project variables outrank YAML in the checkpoint?

Why does the file-variable evidence packet contain a hash instead of content?

If Run C cannot set a pipeline variable because the project forbids it, is the checkpoint incomplete?

What is the safe repair for a job that requires a protected variable but runs on an unprotected branch?

What concept from this chapter is most important before Chapter 07?

Official references and version notes

  • GitLab CI/CD variables — project/group/instance variables, visibility, masking, hiding, protection, file variables, expansion, precedence, pipeline-variable restrictions, and security guidance.
  • Predefined CI/CD variables reference — current pre-pipeline, pipeline, and job-only availability phases and variable definitions.
  • Where variables can be used — GitLab, GitLab Runner, and execution-shell expansion mechanisms and keyword-specific behavior.
  • CI/CD YAML syntax reference — variables, job variables, variables:expand, rules:variables, workflow:rules:variables, and inheritance semantics.
  • CI/CD inputs — typed/configuration inputs, interpolation, and the current recommendation to prefer inputs over ad-hoc pipeline variables for newer parameterized pipelines where applicable.
  • Protected branches — current protected-ref behavior that underpins protected-variable availability.
Version and compatibility note

Variable behavior in this chapter was rechecked against current primary GitLab documentation on 2026-09-11. The current documentation orders policy variables above manual-job and pipeline variables, then project/group/instance values, dotenv values, YAML variables, deployment variables, and most predefined variables. It also documents project/group variable visibility as Masked by default since GitLab 18.3, UI variable-reference expansion disabled by default since GitLab 18.6, and pipeline inputs as the preferred parameter mechanism over pipeline variables for newer designs where applicable. These are version-sensitive details: verify them again before publishing or automating production policy.

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.