Checkpoint Lab — Continuous Integration and Delivery Foundations, GitLab Pipeline Architecture, and Delivery Flow
Integrate Chapter 01 into one reproducible checkpoint. You will author a minimal two-stage pipeline, predict its state transitions before execution, validate the compiled graph, trigger the same commit from push and the GitLab web interface, verify explicit artifact transfer across jobs, preserve an evidence packet, and explain what every successful state proves—and what remains outside its scope.
Learning objectives
Checkpoint objectives
- Predict pipeline/configuration/job/runner/artifact state changes before executing the lab.
- Validate a two-stage configuration that intentionally accepts only push and web-created pipelines.
- Capture source/ref/SHA, pipeline/job IDs, runner identity, and artifact evidence for two controlled pipeline sources.
- Prove that the second job receives an explicitly persisted artifact rather than relying on a shared runner filesystem.
- Produce a concise evidence packet, perform guarded cleanup, and bridge the mental model to Chapter 02 configuration compilation.
1. Scenario and acceptance criteria
Build a two-stage pipeline in a disposable project. Stage one captures non-secret identity evidence and uploads it as an artifact. Stage two verifies that the artifact arrived, compares its recorded SHA with the current pipeline SHA, and records the second job's runner identity. You will trigger this pipeline once with a Git push and once through New pipeline for the same branch.
The checkpoint is complete only when you can answer all of these questions with evidence:
- Which two pipeline sources were observed?
- Which immutable SHA did each pipeline evaluate?
- Which pipeline and job IDs identify each execution?
- Which runner executed each job, if a runner was available?
- How did the evidence file move from stage one to stage two?
- What did a green pipeline prove?
- Which deployment/external-system claims are explicitly not part of this lab?
2. Preflight and exact disposable identity
Choose a project you are authorized to modify and record its namespace/path. Use a dedicated branch so cleanup can target one exact ref. Verify local state before creating it.
LAB_BRANCH="glci/ch01-checkpoint"
git status --short --branch
git remote -v
git rev-parse HEAD
git log -1 --oneline --decorate
# Confirm the lab branch does not already exist remotely.
git ls-remote --heads origin "$LAB_BRANCH"
If the final command already returns a ref, choose a different disposable branch name rather than deleting an existing branch you did not create. In GitLab, inspect available runners read-only. If the only runners are privileged, production-connected, or outside your authority, choose the simulation track.
3. Predict the state changes before writing the file
| Action | Prediction | Evidence after action |
|---|---|---|
| Validate configuration | GitLab accepts two jobs in two stages for push context. | CI Lint/simulation result. |
| Push lab branch |
A pipeline with source push is created for
LAB_SHA.
|
Pipeline ID/source/ref/SHA. |
| Run capture job |
Runner executes job and uploads
evidence/identity.txt.
|
Job ID/runner/log/artifact. |
| Run verify job | Later-stage job receives the prior artifact and verifies its SHA. | Second job trace and runner metadata. |
| Create New pipeline |
A second pipeline with source web targets the
same SHA if branch is unchanged.
|
New pipeline/job IDs and source. |
Record your predictions before execution. A checkpoint is more valuable when you can compare expected and observed state rather than merely documenting what happened after the fact.
4. Author the two-stage checkpoint pipeline
Save this configuration as .gitlab-ci.yml. The
workflow:rules deliberately bounds pipeline creation to
the two sources used by this checkpoint. Later chapters will teach
rule design in depth.
workflow:
rules:
- if: '$CI_PIPELINE_SOURCE == "push"'
- if: '$CI_PIPELINE_SOURCE == "web"'
- when: never
stages:
- observe
- verify
capture_identity:
stage: observe
script:
- mkdir -p evidence
- printf 'source=%s\n' "$CI_PIPELINE_SOURCE" > evidence/identity.txt
- printf 'ref=%s\n' "$CI_COMMIT_REF_NAME" >> evidence/identity.txt
- printf 'sha=%s\n' "$CI_COMMIT_SHA" >> evidence/identity.txt
- printf 'pipeline_id=%s\n' "$CI_PIPELINE_ID" >> evidence/identity.txt
- printf 'producer_job_id=%s\n' "$CI_JOB_ID" >> evidence/identity.txt
- printf 'producer_runner_id=%s\n' "${CI_RUNNER_ID:-unknown}" >> evidence/identity.txt
- test "$CI_COMMIT_SHA" = "$(git rev-parse HEAD)"
- cat evidence/identity.txt
artifacts:
name: "glci-ch01-$CI_PIPELINE_ID-$CI_JOB_ID"
paths:
- evidence/identity.txt
expire_in: 1 day
verify_identity:
stage: verify
script:
- test -s evidence/identity.txt
- expected="sha=$CI_COMMIT_SHA"
- found=0
- |
while IFS= read -r line; do
if [ "$line" = "$expected" ]; then
found=1
fi
done < evidence/identity.txt
test "$found" = "1"
- printf 'consumer_job_id=%s\n' "$CI_JOB_ID"
- printf 'consumer_runner_id=%s\n' "${CI_RUNNER_ID:-unknown}"
- printf 'verified_sha=%s\n' "$CI_COMMIT_SHA"
Current GitLab artifact behavior downloads artifacts from jobs in
earlier stages into later-stage jobs by default when
dependencies or needs has not changed that
behavior. Therefore verify_identity can read the
uploaded file even if it executes on a different runner. The file is
transferred through an explicit GitLab artifact mechanism, not
through an assumption that two jobs share a workspace.
5. Validate and inspect the graph before commit
Use Build → Pipeline editor → Validate. Confirm the
configuration is valid and the simulated push pipeline contains
capture_identity followed by
verify_identity. Preserve the final configuration text
or commit diff as part of your evidence packet.
If you already use an authenticated GitLab CLI, the optional equivalent is:
glab ci lint
# Once the branch exists remotely:
glab ci lint --dry-run --include-jobs --ref "$LAB_BRANCH"
6. Create and observe the push pipeline
git switch -c "$LAB_BRANCH"
git add -- .gitlab-ci.yml
git diff --cached --check
git diff --cached
git commit -m "glci ch01: add checkpoint pipeline"
LAB_SHA="$(git rev-parse HEAD)"
printf 'lab_sha=%s\n' "$LAB_SHA"
git push -u origin "$LAB_BRANCH"
Open the resulting pipeline by its branch/SHA. Record:
- pipeline ID and source
push; - ref and exact SHA;
-
capture_identityandverify_identityjob IDs/statuses; - runner ID/description/version or other non-secret runtime metadata exposed by the jobs;
- the producer artifact name and contents;
- the consumer verification log.
If the jobs run on different runners, that strengthens the demonstration: the consumer still sees the explicit artifact because GitLab/Runner transferred it. If the same runner happens to execute both, the artifact declaration remains the contract; do not infer shared workspace semantics from scheduler coincidence.
7. Prove the producer/consumer boundary
Download or browse the producer artifact. It should identify the
producer pipeline/job/SHA and contain no secret values. Then compare
the consumer job's verified_sha line to the pipeline
SHA. The consumer's job ID and runner ID should be treated as
independent identities.
flowchart TD
A[capture_identity job] --> B[evidence/identity.txt]
B --> C[GitLab job artifact storage]
C --> D[verify_identity job downloads prior-stage artifact]
D --> E[Consumer verifies sha = CI_COMMIT_SHA]
This is the first concrete example of durable pipeline evidence. Chapter 10 will explore artifacts, reports, retention, and dependency controls in depth.
8. Trigger the second source without changing the branch
After the push pipeline finishes, do not add another commit. Open
Build → Pipelines → New pipeline, choose the exact
lab branch, and create a pipeline without adding arbitrary
variables. Record its pipeline ID and source. The expected source is
web.
Compare it with the push pipeline. If the branch has not moved, the two pipelines should have the same commit SHA and different pipeline/job IDs. Runner assignment may be the same or different. Artifact names also differ because they include pipeline/job IDs.
| Field | Push pipeline | Manual web pipeline | Interpretation |
|---|---|---|---|
| Source | push |
web |
Why each pipeline exists differs. |
| Ref | Lab branch | Same lab branch | Human-friendly Git reference can be the same. |
| SHA | LAB_SHA |
LAB_SHA if unchanged |
Immutable source can be identical. |
| Pipeline ID | Unique | Different unique ID | They are distinct executions. |
| Job IDs | Unique per job | New IDs | Job evidence belongs to one pipeline execution. |
| Runner ID | Observed assignment | May differ | Scheduling is independent of source identity. |
9. Assemble the Chapter 01 evidence packet
Create a local note or folder outside the repository containing only non-secret evidence. Do not export tokens or complete environment dumps. A useful packet contains:
| Evidence item | Required content | Why preserve it |
|---|---|---|
| Assumptions | Date, GitLab offering/tier if known, runner availability, shell/runtime limitations. | Makes compatibility claims reviewable. |
| Source identity | Project path, lab branch, LAB_SHA. |
Anchors all execution evidence. |
| Configuration |
Final .gitlab-ci.yml and successful
lint/simulation result.
|
Shows desired and compiled graph inputs. |
| Push execution | Pipeline/job IDs, source/ref/SHA, statuses, runner metadata. | Proves live execution for source push. |
| Web execution | Same fields for source web. |
Separates trigger source from revision. |
| Artifact |
Producer job identity and
identity.txt contents.
|
Proves explicit durable cross-job data. |
| Consumer proof |
Consumer job ID and verified_sha log line.
|
Shows the later job verified the transferred identity. |
| External state | None by design. | Prevents accidental claims about deployment/cloud health. |
10. Explain every green status precisely
| Green state | What this checkpoint proves | What it does not prove |
|---|---|---|
| CI Lint/simulation valid | GitLab can accept/build the simulated graph. | Runner or shell success. |
capture_identity green |
A runner executed the producer script and GitLab received successful job result. | That the consumer ran. |
| Artifact present | GitLab stored the declared producer file. | That arbitrary workspace state is shared. |
verify_identity green |
The later job received the artifact and found the expected SHA line. | Any deployment occurred. |
| Pipeline green | All required jobs in this pipeline completed successfully. | That omitted jobs existed, that production changed, or that an external service is healthy. |
11. Optional controlled diagnosis drill
Without committing a broken file, copy the configuration into CI
Lint and temporarily change
verify_identity to use
stage: verification while the declared stages remain
observe and verify. Observe the validation
failure, preserve the error text, then discard the broken copy and
validate the original configuration again.
This drill proves that configuration failure can be diagnosed before runner execution. It has no repository side effect and does not contaminate the clean live evidence packet.
12. Simulation-only completion path
If no eligible disposable runner exists, complete the checkpoint through CI Lint/pipeline simulation for the push context and document the expected web-source comparison from the configuration. Your evidence packet must mark job IDs, runner IDs, artifact upload, and consumer verification as not observed. Do not fabricate values.
{
"completion_mode": "validation-and-simulation",
"source_sha": "captured-lab-sha",
"compiled_jobs": ["capture_identity", "verify_identity"],
"live_job_ids": "not observed",
"runner_identity": "not observed",
"artifact_transfer": "not observed",
"external_side_effects": "none"
}
A transparent limitation is stronger engineering evidence than pretending simulation equals live execution.
13. Guarded cleanup and final verification
Before deletion, verify the branch name and remote ref one last time. Preserve the pipeline IDs and evidence packet locally. Then delete only the lab branch you created.
git fetch origin --prune
git ls-remote --heads origin "$LAB_BRANCH"
# Proceed only after confirming the exact disposable branch.
git push origin --delete "$LAB_BRANCH"
git switch -
git branch -D "$LAB_BRANCH"
git fetch origin --prune
git status --short --branch
Do not delete runners, change runner settings, remove unrelated artifacts, or delete the entire project unless the whole project was intentionally created for this checkpoint and you have verified its exact namespace/path and contents.
14. Operational review and Chapter 02 handoff
Chapter 01 established the causal model: source/ref/SHA, configuration compilation, pipeline creation, job graph, runner assignment, executor execution, durable evidence, and external state are different objects. You have now observed two sources for the same revision and moved evidence explicitly between jobs.
Chapter 02 focuses on the configuration layer itself: .gitlab-ci.yml Structure, Pipeline Compilation, Keywords, Defaults, and Configuration Validation. You will expand the brief CI Lint experience from this checkpoint into a precise model of how GitLab reads, validates, inherits, and compiles pipeline configuration before any job runs.
Knowledge check
Why does the checkpoint use both push and
web pipelines for the same branch?
To prove that pipeline source and source revision are separate dimensions. The same SHA can be evaluated by distinct pipeline executions created for different reasons.
If verify_identity runs on a different runner, why
can it still read evidence/identity.txt?
Because the producer explicitly uploaded the file as a job artifact and later-stage jobs download earlier-stage artifacts by default in this configuration. It does not rely on a shared runner filesystem.
What must you write in the evidence packet if no safe runner is available?
Mark live job IDs, runner identity, artifact transfer, and runtime verification as not observed. Preserve the validation/simulation result without inventing runtime evidence.
A green checkpoint pipeline proves production is healthy. True or false?
False. This checkpoint has no deployment target at all. Its green state proves the required CI jobs completed for that pipeline, not any external service state.
Why does the cleanup procedure query the exact remote branch before deletion?
It guards resource identity. Cleanup should remove only the disposable branch created by the lab, not an ambiguous or unrelated ref.
Official references and version notes
- Get started with GitLab CI/CD — current first-principles overview of pipeline configuration, jobs, stages, and runners.
- CI/CD pipelines — pipeline sources, basic/stage/DAG pipeline forms, and current pipeline behavior.
- CI/CD YAML syntax reference — authoritative keyword semantics and compatibility notes.
- Predefined CI/CD variables — availability phases and runtime identity variables such as source, SHA, pipeline, job, and runner metadata.
- Runners — current runner scheduling and execution model.
- Validate GitLab CI/CD configuration — the checkpoint's pre-commit validation and simulation surface.
- Job artifacts — current default earlier-stage artifact download behavior and artifact retention concepts.
- Specify when jobs run with rules — pipeline-source values and rule semantics used by the bounded workflow.
- Security for self-managed runners — why the no-runner path is preferable to weakening a privileged production runner.
The checkpoint configuration and current GitLab behavior described here were rechecked against primary documentation on 2026-09-11. GitLab's UI, Runner versions, hosted compute offerings, and CI/CD semantics can evolve; preserve the evidence model and revalidate concrete platform behavior when running this lab in a later release.
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.