ЁЯПл The SchoolтА║ЁЯУо CI/CDтА║ЁЯУо рдзрдбрд╛ 01 тАФ CI/CD рдХрд╛: рдЧреГрд╣рдкрд╛рдард╛рдЪрд╛ рдвреАрдЧ
ЁЯЦ╝я╕П See the drawing + lab ЁЯПа Course home ЁЯМ┐ Branch on GitHub тЬПя╕П View source
ЁЯЦ╝я╕П рдЖрдХреГрддреА рдЖрдгрд┐ labThe drawing + lab рдкреВрд░реНрдг рдкрд╛рдирд╛рд╡рд░ рдЙрдШрдбрд╛ тЖЧOpen full page тЖЧ

ЁЯУо рдзрдбрд╛ 01 тАФ CI/CD рдХрд╛: рдЧреГрд╣рдкрд╛рдард╛рдЪрд╛ рдвреАрдЧ

ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: 12 рдкреИрдХреА рдзрдбрд╛ 01 ┬╖ рдкреБрдвреЗ: lesson-02-pipeline-anatomy


ЁЯУж рдпрд╛ рдмреНрд░рдБрдЪрдордзреНрдпреЗ рдХрд╛рдп рдЖрд╣реЗ

рдХреЛрд░реНрд╕рдЪрд╛ рдкрд╛рдпрд╛: pipeline рдореБрд│рд╛рдд рдХрд╛ рдЕрд╕рддреЗ, рдЖрдгрд┐ рддреЛ рдЫреЛрдЯрд╛ ре▓рдк рдЬреЛ courier рд╕рдЧрд│реНрдпрд╛ 12 рдзрдбреНрдпрд╛рдВрдордзреНрдпреЗ рд╡рд╛рд╣реВрди рдиреЗрдгрд╛рд░ рдЖрд╣реЗ. рдЦрд▒реНрдпрд╛ files:

ЁЯзТ 5 рд╡рд░реНрд╖рд╛рдВрдЪреНрдпрд╛ рдореБрд▓рд╛рд▓рд╛ рд╕рдордЬрд╛рд╡рд▓реНрдпрд╛рд╕рд╛рд░рдЦреЗ

рдЕрд╢рд╛ рд╡рд░реНрдЧрд╛рдЪреА рдХрд▓реНрдкрдирд╛ рдХрд░рд╛ рдЬрд┐рдереЗ рд╕рддреНрд░рд╛рдЪреНрдпрд╛ рд╢реЗрд╡рдЯрдЪреНрдпрд╛ рджрд┐рд╡рд╕рд╛рдкрд░реНрдпрдВрдд рдХреЛрдгреАрдЪ рдЧреГрд╣рдкрд╛рда рджреЗрдд рдирд╛рд╣реА. рддреАрд╕ рдлреЛрд▓реНрдбрд░реНрд╕ рдПрдХрджрдо рд╢рд┐рдХреНрд╖рд┐рдХреЗрдЪреНрдпрд╛ рдЯреЗрдмрд▓рд╛рд╡рд░ рдпреЗрдКрди рдкрдбрддрд╛рдд. ЁЯУЪ рдкрд╛рдиреЗ рдЙрд▓рдЯрд╕реБрд▓рдЯ рдЖрд╣реЗрдд, рджреЛрди рд╡рд┐рджреНрдпрд╛рд░реНрдереНрдпрд╛рдВрдиреА рдЖрдкрд▓реНрдпрд╛ рдХрд╛рдЧрджрд╛рдВрдирд╛ рд╡реЗрдЧрд╡реЗрдЧрд│реЗ рдХреНрд░рдорд╛рдВрдХ рджрд┐рд▓реЗ рдЖрд╣реЗрдд, рдПрдХ рдЙрддреНрддрд░ рдЕрд╢рд╛ worksheet рд╡рд░ рдЕрд╡рд▓рдВрдмреВрди рдЖрд╣реЗ рдЬреА рджреБрд╕рд░рд╛ рдХреЛрдгреАрддрд░реА рдЬреЛрдбрд╛рдпрд▓рд╛ рд╡рд┐рд╕рд░рд▓рд╛. рдПрдХрд╣реА рдкреЗрдкрд░ рддрдкрд╛рд╕рдгреНрдпрд╛рдЖрдзреАрдЪ рд╢рд┐рдХреНрд╖рд┐рдХрд╛ рд╕рдВрдкреВрд░реНрдг weekend рдлрдХреНрдд рдХреЛрдгрд╛рдЪреА рдкрд╛рдиреЗ рдПрдХрдореЗрдХрд╛рдВрд╢реА рдЬреБрд│рддрд╛рдд рд╣реЗ рд╢реЛрдзрдгреНрдпрд╛рдд рдШрд╛рд▓рд╡рддреЗ. ЁЯШй

рддреЛ weekend рдореНрд╣рдгрдЬреЗ integration hell. Software рдордзреНрдпреЗ рддреЗ рдЕрд╕реЗ рджрд┐рд╕рддреЗ: рдкрд╛рдЪ рд▓реЛрдХ рдЖрдард╡рдбреЗрднрд░ рдЖрдкрд╛рдкрд▓реНрдпрд╛ branches рд╡рд░ рдХрд╛рдо рдХрд░рддрд╛рдд, рдордЧ рд╢реБрдХреНрд░рд╡рд╛рд░реА merge рдХрд░рддрд╛рдд, рдЖрдгрд┐ рдирд┐рдХрд╛рд▓ build рд╕реБрджреНрдзрд╛ рд╣реЛрдд рдирд╛рд╣реА.

рдЖрддрд╛ рдПрдХрд╛ рд╡реЗрдЧрд│реНрдпрд╛ рд╢рд╛рд│реЗрдЪреА рдХрд▓реНрдкрдирд╛ рдХрд░рд╛. рдкреНрд░рддреНрдпреЗрдХ рдЧреГрд╣рдкрд╛рдард╛рдЪрд╛ рдХрд╛рдЧрдж рдкреВрд░реНрдг рд╣реЛрддрд╛рдЪ рддреНрдпрд╛рдЪ рджрд┐рд╡рд╢реА mailroom ЁЯУо рдордзреНрдпреЗ рдЬрд╛рддреЛ. рдПрдХ courier robot рддреЛ рдЙрдЪрд▓рддреЛ, рддрдкрд╛рд╕рдгреА рдбреЗрд╕реНрдХ тЬЕ рд╡рд░реВрди рдиреЗрддреЛ, рдЖрдгрд┐ рдХрд╛рд╣реА рдорд┐рдирд┐рдЯрд╛рдВрдд рд╢рд┐рдХреНрдХрд╛ рдкрд░рдд рдпреЗрддреЛ: тЬЕ рдХрд┐рдВрд╡рд╛ тЭМ. рдХрд╛рд╣реАрдЪ рд╕рд╛рдЪрдд рдирд╛рд╣реА, рдХрд╛рд░рдг рдкреНрд░рддреНрдпреЗрдХ рдХрд╛рдЧрдж рдЫреЛрдЯрд╛ рдЖрдгрд┐ рддрд╛рдЬрд╛ рдЕрд╕рддрд╛рдирд╛рдЪ рддрдкрд╛рд╕рд▓рд╛ рдЬрд╛рддреЛ тАФ рдЖрдгрд┐ рдПрдЦрд╛рджрд╛ fail рдЭрд╛рд▓рд╛, рддрд░ рдиреЗрдордХрд╛ рдХреЛрдгрддрд╛ рдХрд╛рдЧрдж рдЖрдгрд┐ рдХрд╛ рддреЗ рддреБрдореНрд╣рд╛рд▓рд╛ рдХрд│рддреЗ.

рддреА mailroom рдореНрд╣рдгрдЬреЗ рддреБрдордЪреА CI system. Robot рдЪрд╛ рдорд╛рд░реНрдЧ тАФ рддрдкрд╛рд╕рдгреА рдбреЗрд╕реНрдХ, рдордЧ рдлреЛрдЯреЛрдХреЙрдкреА рдорд╢реАрди ЁЯН▒, рдордЧ delivery van ЁЯЪЪ тАФ рдореНрд╣рдгрдЬреЗ pipeline. рд╣рд╛ рдХреЛрд░реНрд╕ рддреЛ рдорд╛рд░реНрдЧ рдмрд╛рдВрдзрдгреНрдпрд╛рдмрджреНрджрд▓ рдЖрд╣реЗ, рдПрдХрд╛ рд╡реЗрд│реА рдПрдХ рдбреЗрд╕реНрдХ.

ЁЯЧ║я╕П рдЖрдХреГрддреА

flowchart LR
    pile["ЁЯУЪ term-end pile<br/>everything merged at once"]
    dev["ЁЯзСтАНЁЯТ╗ you<br/>git push"]
    mail["ЁЯУо mailroom<br/>the CI system"]
    check["тЬЕ checking desk<br/>node --test"]
    copy["ЁЯН▒ photocopier<br/>docker build"]
    van["ЁЯЪЪ delivery van<br/>deploy"]
    board["ЁЯУМ notice board<br/>Kubernetes Deployment"]
    dev -.->|"0 the old way"| pile
    dev -->|"1 every push"| mail
    mail -->|"2 check"| check
    check -->|"3 only if green"| copy
    copy -->|"4 lessons 07 to 10"| van
    van -->|"5 pin the new notice"| board

тЭУ рдХрд╛рдп

ЁЯза рддреАрди рд╢рдмреНрдж, рд╢реЗрдЬрд╛рд░реА рд╢реЗрдЬрд╛рд░реА

CI                      merge often, check each merge        тЖТ "it still works together"
Continuous delivery     main is releasable; a human ships    тЖТ the principal signs
Continuous deployment   every green change ships by itself   тЖТ no signature step

рд╢реЗрд╡рдЯрдЪреНрдпрд╛ рджреЛрдиреНрд╣реАрдВрд╕рд╛рдареА "CD" рд╡рд╛рдкрд░рд▓реЗ рдЬрд╛рддреЗ, рдореНрд╣рдгреВрдирдЪ рд╣рд╛ рдХреЛрд░реНрд╕ рддреНрдпрд╛рд▓рд╛ рдХреЛрдгрддреЗ рдореНрд╣рдгрд╛рдпрдЪреЗ рдЖрд╣реЗ рддреЗ рдирд╛рд╡рд╛рдиреЗ рд╕рд╛рдВрдЧрддреЛ. рдмрд╣реБрддреЗрдХ teams delivery рдХрд░рддрд╛рдд; deployment рдореНрд╣рдгрдЬреЗ рд╕рд╣реАрдЪреА рдкрд╛рдпрд░реА рдХрд╛рдвреВрди рдЯрд╛рдХрд▓реЗрд▓реА delivery тАФ рд╣реА рдПрдХ рдирд┐рд╡рдб рдЖрд╣реЗ, upgrade рдирд╛рд╣реА.

ЁЯдФ рдХрд╛

рдбрдмрд╛ рдХрд╕рд╛ рднрд░рд╛рдпрдЪрд╛ (Docker рд╢рд╛рд│рд╛) рдЖрдгрд┐ рддреЛ рдХрд╕рд╛ рдЪрд╛рд▓рд╡рд╛рдпрдЪрд╛ (Kubernetes рд╢рд╛рд│рд╛) рд╣реЗ рддреБрдореНрд╣рд╛рд▓рд╛ рдЖрдзреАрдЪ рдорд╛рд╣реАрдд рдЖрд╣реЗ. рдЬреЗ рд╣рд░рд╡рд▓реЗ рдЖрд╣реЗ рддреЗ рдореНрд╣рдгрдЬреЗ рдордзрд▓рд╛ рдкреНрд░рд╡рд╛рд╕: git push рдкрд╛рд╕реВрди рдирд╡реА image рдЪрд╛рд▓рд╡рдгрд╛рд▒реНрдпрд╛ pod рдкрд░реНрдпрдВрдд, рдкреНрд░рддреНрдпреЗрдХ рдерд╛рдВрдмреНрдпрд╛рд╡рд░ рд╢рд┐рдХреНрдХреНрдпрд╛рд╕рд╣. рд╣реЗ рд╣рд╛рддрд╛рдиреЗ рдХреЗрд▓реЗ рддрд░ рддреЗ рдХреНрд╡рдЪрд┐рдд, рд░рд╛рддреНрд░реА рдЙрд╢рд┐рд░рд╛, рдкрд╛рдпрд▒реНрдпрд╛ рд▓рдХреНрд╖рд╛рдд рдЕрд╕рд▓реЗрд▓реНрдпрд╛ рдПрдХрд╛рдЪ рд╡реНрдпрдХреНрддреАрдХрдбреВрди рд╣реЛрддреЗ. Pipeline рдиреЗ рдХреЗрд▓реЗ рддрд░ рддреЗ рдкреНрд░рддреНрдпреЗрдХ push рд╡рд░, рд╣реБрдмреЗрд╣реВрдм рддрд╕реЗрдЪ, log рд╕рд╣ рд╣реЛрддреЗ.

рдореНрд╣рдгреВрдирдЪ рд╣рд╛ рдХреЛрд░реНрд╕ Ops track рдордзреНрдпреЗ 3я╕ПтГг рдХреНрд░рдорд╛рдВрдХрд╛рд╡рд░ рдЖрд╣реЗ. рддреЛ delivery van рдиреЗ рдлрд▓рдХрд╛рд╡рд░ рд╕реВрдЪрдирд╛ рд▓рд╛рд╡рдгреНрдпрд╛рдиреЗ рд╕рдВрдкрддреЛ тАФ push model тАФ рдЖрдгрд┐ van рдирд┐рдШреВрди рдЧреЗрд▓реНрдпрд╛рд╡рд░ рддреА рдХрд╛рдп рдХрд░реВ рд╢рдХрдд рдирд╛рд╣реА рдпрд╛рдЪреНрдпрд╛ рдкреНрд░рд╛рдорд╛рдгрд┐рдХ рдпрд╛рджреАрдиреЗ (drift, CI рдордзреНрдпреЗ рд░рд╛рд╣рдгрд╛рд░реА cluster credentials). рддрд┐рдереВрдирдЪ ArgoCD рд╢рд╛рд│реЗрдЪрд╛ рдзрдбрд╛ 03 рдкреБрдвреЗ рд╕реБрд░реВ рд╣реЛрддреЛ.

ЁЯФз рдХрд╕реЗ (рдпрд╛ repo рдордзреНрдпреЗ)

рдпрд╛ рдзрдбреНрдпрд╛рдд рдЕрдЬреВрди рдХреЛрдгрддреАрд╣реА pipeline рдирд╛рд╣реА тАФ рдлрдХреНрдд рдЧреГрд╣рдкрд╛рда рдЖрдгрд┐ рдЙрддреНрддрд░рдкрддреНрд░рд┐рдХрд╛. app/server.js рдордзрд▓реНрдпрд╛ ре▓рдкрдордзреНрдпреЗ рдПрдХ route рдЖрд╣реЗ рдЬреНрдпрд╛рдЪреА рд╕рдВрдкреВрд░реНрдг track рд▓рд╛ рдХрд╛рд│рдЬреА рдЖрд╣реЗ, рдХрд╛рд░рдг readiness probe (Kubernetes рд╢рд╛рд│рд╛ рдзрдбрд╛ 07) рдЖрдгрд┐ smoke test (рдЗрдерд▓рд╛ рдзрдбрд╛ 07) рджреЛрдШреЗрд╣реА рддреНрдпрд╛рд▓рд╛рдЪ рд╡рд┐рдЪрд╛рд░рддрд╛рдд:

  if (req.url === '/healthz') {                 // "are you ready?" тАФ the readiness probe asks this
    res.writeHead(200, { 'content-type': 'text/plain' });
    return res.end('ok\n');
  }

рддрдкрд╛рд╕рдгреА рдбреЗрд╕реНрдХрдЪреА рдЙрддреНрддрд░рдкрддреНрд░рд┐рдХрд╛ рдореНрд╣рдгрдЬреЗ app/server.test.js. рддреА server port 0 рд╡рд░ рд╕реБрд░реВ рдХрд░рддреЗ (рдХреЛрдгрддрд╛рд╣реА рдореЛрдХрд│рд╛ port, рдореНрд╣рдгрдЬреЗ рддреБрдореНрд╣реА рдЪрд╛рд▓реВ рдареЗрд╡рд▓реЗрд▓реНрдпрд╛ copy рд╢реА tests рдЪреА рдЯрдХреНрдХрд░ рд╣реЛрдд рдирд╛рд╣реА) рдЖрдгрд┐ рдиреЗрдордХрд╛ body рддрдкрд╛рд╕рддреЗ:

test('GET /healthz answers ok тАФ what the readiness probe asks', () =>
  withServer(async (base) => {
    const res = await fetch(`${base}/healthz`);
    assert.equal(res.status, 200);
    assert.equal(await res.text(), 'ok\n');
  }));

рджреЛрдиреНрд╣реА files рдлрдХреНрдд node:http, node:test рдЖрдгрд┐ node:assert рд╡рд╛рдкрд░рддрд╛рдд тАФ рд╢реВрдиреНрдп dependencies. рд╣реЗ рдЬрд╛рдгреВрдирдмреБрдЬреВрди рдЖрд╣реЗ: npm install рдирд╛рд╣реА, lockfile рдирд╛рд╣реА, download рдХрд░рд╛рдпрд▓рд╛ рдХрд╛рд╣реАрдЪ рдирд╛рд╣реА. рдкреБрдврдЪреНрдпрд╛ рдзрдбреНрдпрд╛рдВрдордзреНрдпреЗ run рд╕рд╛рд╡рдХрд╛рд╢ рдХрд┐рдВрд╡рд╛ рд▓рд╛рд▓ рдЭрд╛рд▓рд╛, рддрд░ рддреЗ pipeline рдЪреЗ рдХрд╛рдо рдЖрд╣реЗ, рдПрдЦрд╛рджреНрдпрд╛ package рдЪреЗ рдирд╛рд╣реА. app/package.json рдиреБрд╕рд╛рд░, рд╕рдВрдкреВрд░реНрдг test command рдЖрд╣реЗ "test": "node --test".

ЁЯзк рдХрд░реВрди рдкрд╛рд╣рд╛

# 0) you need Node 20 or newer (the tests use the built-in runner and fetch)
node --version

# 1) be the checking desk yourself
cd app
node --test                 # тЬФ 3 tests, 3 pass тАФ this exact command runs in CI from lesson 03 on

# 2) break the homework on purpose: make /healthz answer "okay" instead of "ok"
sed -i.bak "s/end('ok/end('okay/" server.js
node --test                 # тЬЦ GET /healthz answers ok тАж expected 'ok\n', actual 'okay\n'
echo "exit code: $?"        # non-zero тАФ this is what turns a run red in lesson 03

# 3) fix it and confirm the stamp is green again
git checkout -- server.js && rm server.js.bak
node --test
cd ..

тЪая╕П рдиреЗрд╣рдореАрдЪреНрдпрд╛ рдЪреБрдХрд╛

тПня╕П рдкреБрдвреЗ

Courier рдЖрдкрд▓реА рдкрд╣рд┐рд▓реА рдлреЗрд░реА рдХрд░рдгреНрдпрд╛рдЖрдзреА, рддреНрдпрд╛рдЪреЗ рднрд╛рдЧ рд╢рд┐рдХрд╛: рддреНрдпрд╛рд▓рд╛ рд╕реБрд░реВ рдХрд░рдгрд╛рд░реА рдШрдВрдЯрд╛, рддреЛ рдЬреНрдпрд╛ рдбреЗрд╕реНрдХрдирд╛ рднреЗрдЯ рджреЗрддреЛ рддреЗ, рдЖрдгрд┐ рддреНрдпрд╛рд╡рд░ рдмрд╕рдгрд╛рд░реЗ рдХрд╛рд░рдХреВрди. ЁЯзй

git checkout lesson-02-pipeline-anatomy

ЁЯУо Lesson 01 тАФ Why CI/CD: the homework pile-up

ЁЯУН You are here: Lesson 01 of 12 ┬╖ Next: lesson-02-pipeline-anatomy


ЁЯУж What's in this branch

The foundation of the course: why a pipeline exists at all, plus the tiny app the courier will carry through all 12 lessons. Real files:

ЁЯзТ Explain like I'm 5

Imagine a class where nobody hands in homework until the last day of term. Thirty folders land on the teacher's desk at once. ЁЯУЪ Pages are out of order, two students numbered their sheets differently, one answer depends on a worksheet someone else forgot to include. The teacher spends the whole weekend just working out whose pages fit together тАФ before grading a single one. ЁЯШй

That weekend is integration hell. In software it looks like this: five people work on their own branches for weeks, then merge on Friday, and the result does not even build.

Now picture a different school. Every homework sheet goes into the mailroom ЁЯУо the day it is finished. A courier robot picks it up, runs it past the checking desk тЬЕ, and a stamp comes back within minutes: тЬЕ or тЭМ. Nothing piles up, because every sheet is checked while it is still small and fresh тАФ and when one fails, you know exactly which sheet and why.

That mailroom is your CI system. The robot's route тАФ checking desk, then the photocopier ЁЯН▒, then the delivery van ЁЯЪЪ тАФ is the pipeline. This course is about building that route, one desk at a time.

ЁЯЧ║я╕П Diagram

flowchart LR
    pile["ЁЯУЪ term-end pile<br/>everything merged at once"]
    dev["ЁЯзСтАНЁЯТ╗ you<br/>git push"]
    mail["ЁЯУо mailroom<br/>the CI system"]
    check["тЬЕ checking desk<br/>node --test"]
    copy["ЁЯН▒ photocopier<br/>docker build"]
    van["ЁЯЪЪ delivery van<br/>deploy"]
    board["ЁЯУМ notice board<br/>Kubernetes Deployment"]
    dev -.->|"0 the old way"| pile
    dev -->|"1 every push"| mail
    mail -->|"2 check"| check
    check -->|"3 only if green"| copy
    copy -->|"4 lessons 07 to 10"| van
    van -->|"5 pin the new notice"| board

тЭУ What

ЁЯза The three words, side by side

CI                      merge often, check each merge        тЖТ "it still works together"
Continuous delivery     main is releasable; a human ships    тЖТ the principal signs
Continuous deployment   every green change ships by itself   тЖТ no signature step

"CD" is used for both of the last two, which is why this course names the one it means. Most teams practice delivery; deployment is delivery with the signature step removed тАФ a choice, not an upgrade.

ЁЯдФ Why

You already know how to pack the lunchbox (Docker school) and how to run it (Kubernetes school). What is missing is the trip in between: from git push to a pod running the new image, with a stamp at every stop. Do that by hand and it happens rarely, late at night, by the one person who remembers the steps. Do it with a pipeline and it happens on every push, identically, with a log.

That is why this course sits at position 3я╕ПтГг of the Ops track. It ends with the delivery van pinning a notice on the board тАФ the push model тАФ and with an honest list of what the van cannot do after it drives away (drift, cluster credentials living in CI). That list is where the ArgoCD school's lesson 03 picks up.

ЁЯФз How (in this repo)

There is no pipeline in this lesson yet тАФ only the homework and the answer key. The app in app/server.js has one route the whole track cares about, because the readiness probe (Kubernetes school lesson 07) and the smoke test (lesson 07 here) both ask it:

  if (req.url === '/healthz') {                 // "are you ready?" тАФ the readiness probe asks this
    res.writeHead(200, { 'content-type': 'text/plain' });
    return res.end('ok\n');
  }

The checking desk's answer key is app/server.test.js. It starts the server on port 0 (any free port, so tests do not collide with a copy you left running) and asserts the exact body:

test('GET /healthz answers ok тАФ what the readiness probe asks', () =>
  withServer(async (base) => {
    const res = await fetch(`${base}/healthz`);
    assert.equal(res.status, 200);
    assert.equal(await res.text(), 'ok\n');
  }));

Both files use only node:http, node:test and node:assert тАФ zero dependencies. That is deliberate: no npm install, no lockfile, nothing to download. When a run is slow or red in later lessons, it is the pipeline's doing, not a package's. From app/package.json, the whole test command is "test": "node --test".

ЁЯзк Try it

# 0) you need Node 20 or newer (the tests use the built-in runner and fetch)
node --version

# 1) be the checking desk yourself
cd app
node --test                 # тЬФ 3 tests, 3 pass тАФ this exact command runs in CI from lesson 03 on

# 2) break the homework on purpose: make /healthz answer "okay" instead of "ok"
sed -i.bak "s/end('ok/end('okay/" server.js
node --test                 # тЬЦ GET /healthz answers ok тАж expected 'ok\n', actual 'okay\n'
echo "exit code: $?"        # non-zero тАФ this is what turns a run red in lesson 03

# 3) fix it and confirm the stamp is green again
git checkout -- server.js && rm server.js.bak
node --test
cd ..

тЪая╕П Common mistakes

тПня╕П Next

Before the courier makes its first round, learn its parts: the bell that starts it, the desks it visits, and the clerks who staff them. ЁЯзй

git checkout lesson-02-pipeline-anatomy
тЖР Course homeall lessonsNext тЖТpipeline anatomy

This page is the lesson's README from the lesson-01-why-cicd branch, shown here so the whole School stays on one site. Code files open on GitHub at the same branch.