Lesson 1 of 7
Pipeline anatomy
Turn every commit into a tested artifact that ships itself — the build, test, and deploy stages, why they run as gates, and how caching and build-once-promote keep the pipeline fast and honest.
① Watch
Speed
📖 Read this walkthrough — every command, and why
Pipeline anatomy — stages that gate on their exit code
Mental model: a CI/CD pipeline is a set of automated stages that a commit triggers, so no
one ships code by clicking through steps by hand. The stages run strictly in order —
lint -> build -> test -> package -> deploy — and the whole engine hinges on one idea:
each stage GATES on its exit code. Exit 0 means pass, and only then does the next stage
start. A stage that exits non-zero stops the pipeline right there, before deploy is ever
reached. That is why broken code physically cannot ship: the guardrail is the exit code.
Vocabulary (say these precisely):
stage — a phase of the pipeline, like build or test.
step — an individual command inside a stage.
job — a unit of work a runner executes.
runner — the machine (a.k.a. agent) that actually runs the commands.
1 · The staged pipeline, passing (real output)
./pipeline.sh # lint -> build -> test -> package
== lint ==
lint OK
== build ==
build OK -> build.out
== test ==
test OK (artifact present)
== package ==
packaged app.tgz
PIPELINE PASSED (exit 0)
final exit code: 0
Read it top to bottom: each "== name ==" header is a stage; each stage passed (exit 0),
so the next one ran. The final exit code of 0 is the whole pipeline reporting success.
2 · The gate in action — a failing stage stops it before deploy (real output)
# the test stage fails, so it exits non-zero
running tests...
TESTS FAILED -> gate blocks; deploy is NOT reached
pipeline exit code: 1 (non-zero = release blocked)
The test stage exited non-zero, the pipeline halted, and deploy never ran. Overall exit
code 1 = release blocked. This behavior — stop on the first failure, never run what comes
after — is called fail-fast.
3 · What each part does
exit 0 stage passed; the next stage is allowed to start.
exit non-zero stage failed; the pipeline stops here and reports failure.
fail-fast because a failing stage blocks everything after it, bad code can't reach deploy.
the header "== lint ==" / "== build ==" etc. marks the boundary between stages.
4 · CI vs CD — the two halves
CI (continuous integration) — the integrate-and-test half: lint, build, and test on
EVERY change, so regressions are caught as code lands.
CD (continuous delivery/deployment) — the delivery half: package the artifact and deploy it.
Continuous DELIVERY stops at a manual release gate; continuous DEPLOYMENT ships every
passing change automatically.
In the run above: lint -> build -> test is CI; package -> deploy is CD.
Notes:
- The pipeline is triggered by a commit — you don't run stages by hand.
- Order matters: lint -> build -> test -> package -> deploy, each gating the next.
- "Pass" is defined by the exit code, not by log text. Exit 0 = pass, anything else = fail.
- A job runs on a runner (agent); steps are the commands inside a stage's job.
Gotchas:
- A stage that "prints success" but exits non-zero still fails the gate — trust the exit
code, not the words on screen.
- Don't put deploy before test. If any earlier stage can be skipped past a failure, the
guardrail is gone and broken code ships.
- A non-zero exit anywhere before deploy blocks deploy entirely — that is the point, not a bug.
Verify:
./pipeline.sh; echo "final exit: $?"
# PIPELINE PASSED (exit 0) ... final exit: 0
Force a failing stage (e.g. break a test) and re-run: the pipeline stops at that stage,
deploy is not reached, and the final exit code is 1.