ЁЯУо рдзрдбрд╛ 01 тАФ CI/CD рдХрд╛: рдЧреГрд╣рдкрд╛рдард╛рдЪрд╛ рдвреАрдЧ
ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: 12 рдкреИрдХреА рдзрдбрд╛ 01 ┬╖ рдкреБрдвреЗ: lesson-02-pipeline-anatomy
ЁЯУж рдпрд╛ рдмреНрд░рдБрдЪрдордзреНрдпреЗ рдХрд╛рдп рдЖрд╣реЗ
рдХреЛрд░реНрд╕рдЪрд╛ рдкрд╛рдпрд╛: pipeline рдореБрд│рд╛рдд рдХрд╛ рдЕрд╕рддреЗ, рдЖрдгрд┐ рддреЛ рдЫреЛрдЯрд╛ ре▓рдк рдЬреЛ courier рд╕рдЧрд│реНрдпрд╛ 12 рдзрдбреНрдпрд╛рдВрдордзреНрдпреЗ рд╡рд╛рд╣реВрди рдиреЗрдгрд╛рд░ рдЖрд╣реЗ. рдЦрд▒реНрдпрд╛ files:
- app/server.js тАФ demo ре▓рдк (рд╢реВрдиреНрдп dependencies; port 3000 рд╡рд░ рдЖрдкрд▓реЗ hostname рдЖрдгрд┐ version рд╕рд╛рдВрдЧрддреЛ,
/healthzрдореНрд╣рдгрддреЛok) - app/server.test.js тАФ Node рдЪреНрдпрд╛ built-in test runner рдиреЗ рд▓рд┐рд╣рд┐рд▓реЗрд▓реНрдпрд╛ рддреАрди рддрдкрд╛рд╕рдгреНрдпрд╛; рддрдкрд╛рд╕рдгреА рдбреЗрд╕реНрдХ рдкреНрд░рддреНрдпреЗрдХ push рд╡рд░ рдиреЗрдордХреНрдпрд╛ рдпрд╛рдЪ рдЪрд╛рд▓рд╡рддреЗ
ЁЯзТ 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
тЭУ рдХрд╛рдп
- Continuous integration (CI) = рдЫреЛрдЯреЗ рдмрджрд▓ рд╕рд╛рдорд╛рдпрд┐рдХ main line рдордзреНрдпреЗ рд╡рд╛рд░рдВрд╡рд╛рд░ merge рдХрд░рдгреНрдпрд╛рдЪреА рд╕рд╡рдп, рдЖрдгрд┐ рдкреНрд░рддреНрдпреЗрдХ рдмрджрд▓рд╛рд╡рд░ рдЖрдкреЛрдЖрдк build-рдЖрдгрд┐-test run. рдореБрджреНрджрд╛ рд╕рд╡рдпреАрдЪрд╛ рдЖрд╣реЗ; tool рдореНрд╣рдгрдЬреЗ рдлрдХреНрдд mailroom.
- Continuous delivery = main line рдиреЗрд╣рдореА release рдХрд░рдгреНрдпрд╛рдпреЛрдЧреНрдп рдареЗрд╡рд▓реА рдЬрд╛рддреЗ; production рд▓рд╛ рдкрд╛рдард╡рдгреЗ рд╣рд╛ рдПрдХрд╛ рдорд╛рдгрд╕рд╛рдиреЗ рдмрдЯрдг рджрд╛рдмреВрди рдШреЗрддрд▓реЗрд▓рд╛ рдирд┐рд░реНрдгрдп рдЕрд╕рддреЛ (рдкреНрд░рд╛рдЪрд╛рд░реНрдпрд╛рдВрдЪреА рд╕рд╣реА ЁЯЫС, рдзрдбрд╛ 08).
- Continuous deployment = pipeline рдкрд╛рд╕ рд╣реЛрдгрд╛рд░рд╛ рдкреНрд░рддреНрдпреЗрдХ рдмрджрд▓ рдЖрдкреЛрдЖрдк production рд▓рд╛ рдЬрд╛рддреЛ. рдмрдЯрдг рдирд╛рд╣реА. Pipeline рд╣реАрдЪ рд╕рд╣реА рдЖрд╣реЗ.
- Pipeline = рдмрджрд▓ рдЬреНрдпрд╛ рдХреНрд░рдорд╛рдиреЗ, рдЖрдкреЛрдЖрдк рдЬрд╛рддреЛ рддреЛ рдорд╛рд░реНрдЧ: рддрдкрд╛рд╕рд╛ тЖТ copy рдХрд░рд╛ тЖТ рдкреЛрд╣реЛрдЪрд╡рд╛. рдкреНрд░рддреНрдпреЗрдХ рдерд╛рдВрдмрд╛ рдПрдХ job рдЖрд╣реЗ; рд╕рдВрдкреВрд░реНрдг рдорд╛рд░реНрдЧ рдореНрд╣рдгрдЬреЗ courier рдЪреА рдлреЗрд░реА.
- Integration hell = merge рдкреБрдвреЗ рдврдХрд▓рд▓реЗ рдХреА рдЬреЗ рд╣реЛрддреЗ рддреЗ: conflicts, рдХреЛрдгрд╛рдЪреНрдпрд╛рдЪ рдорд╛рд▓рдХреАрдЪрд╛ рдирд╕рд▓реЗрд▓рд╛ build, рдЖрдгрд┐ рдЙрддреНрдЦрдирдирд╛рдд рд╡рд╛рдпрд╛ рдЧреЗрд▓реЗрд▓рд╛ weekend.
ЁЯза рддреАрди рд╢рдмреНрдж, рд╢реЗрдЬрд╛рд░реА рд╢реЗрдЬрд╛рд░реА
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 ..
тЪая╕П рдиреЗрд╣рдореАрдЪреНрдпрд╛ рдЪреБрдХрд╛
- "рдЖрдордЪреНрдпрд╛рдХрдбреЗ Jenkins server рдЖрд╣реЗ" рдпрд╛рд▓рд╛ continuous integration рдореНрд╣рдгрдгреЗ тАФ CI рдореНрд╣рдгрдЬреЗ рдЫреЛрдЯреЗ рдЖрдгрд┐ рд╡рд╛рд░рдВрд╡рд╛рд░ merge, рдкреНрд░рддреНрдпреЗрдХ merge рд╡рд░ рддрдкрд╛рд╕рдгреАрд╕рд╣; server рдореНрд╣рдгрдЬреЗ рдлрдХреНрдд mailroom
- delivery рдЖрдгрд┐ deployment рдпрд╛рдВрдирд╛ рд╕рдорд╛рдирд╛рд░реНрдереА рд╕рдордЬрдгреЗ тАФ рдПрдХ рдорд╛рдгрд╕рд╛рдЪреЗ рдмрдЯрдг рдареЗрд╡рддреЗ, рджреБрд╕рд░реЗ рддреЗ рдХрд╛рдвреВрди рдЯрд╛рдХрддреЗ, рдЖрдгрд┐ рд░рд╛рддреНрд░реА 2 рд╡рд╛рдЬрддрд╛ рдХреЛрдгрд╛рд▓рд╛ page рдпреЗрдгрд╛рд░ рддреЗ рддреНрдпрд╛рд╡рд░реВрди рдард░рддреЗ
- рдПрдЦрд╛рджреА branch рдЖрдард╡рдбреЗрднрд░ рдЬрд┐рд╡рдВрдд рдареЗрд╡реВрди рд╢реЗрд╡рдЯреА "integrate" рдХрд░рдгреЗ тАФ рдкреНрд░рддреНрдпреЗрдХ branch рдЪрд╛ run рдХрд┐рддреАрд╣реА рд╣рд┐рд░рд╡рд╛ рдЕрд╕рд▓рд╛ рддрд░реА рддреЛ рд╕рддреНрд░рд╛рдЪреНрдпрд╛ рд╢реЗрд╡рдЯрдЪрд╛ рдвреАрдЧрдЪ рдЖрд╣реЗ
тПня╕П рдкреБрдвреЗ
Courier рдЖрдкрд▓реА рдкрд╣рд┐рд▓реА рдлреЗрд░реА рдХрд░рдгреНрдпрд╛рдЖрдзреА, рддреНрдпрд╛рдЪреЗ рднрд╛рдЧ рд╢рд┐рдХрд╛: рддреНрдпрд╛рд▓рд╛ рд╕реБрд░реВ рдХрд░рдгрд╛рд░реА рдШрдВрдЯрд╛, рддреЛ рдЬреНрдпрд╛ рдбреЗрд╕реНрдХрдирд╛ рднреЗрдЯ рджреЗрддреЛ рддреЗ, рдЖрдгрд┐ рддреНрдпрд╛рд╡рд░ рдмрд╕рдгрд╛рд░реЗ рдХрд╛рд░рдХреВрди. ЁЯзй
git checkout lesson-02-pipeline-anatomy