Modul 9 — CI/CD

46. Pipeline jako kód a opakovaně použitelné šablony

Jak psát CI/CD pipeline jako verzovaný kód a jak z opakujících se částí vytvořit sdílené, znovupoužitelné šablony napříč projekty.

Odhadovaná délka studia: 50 minut · Stav: nehotovo

Technologie: CI/CD

Úvod a kontext

Předchozí lekce ukázaly, jak sestavit jednu konkrétní CI/CD pipeline —
sestavení, testy, nasazení. Ve chvíli, kdy má tým nebo firma desítky
podobných projektů (např. mikroslužby se stejnou strukturou), se stejné
kroky pipeline začnou kopírovat z projektu do projektu. To je stejný
problém, jaký v aplikačním kódu řeší funkce a knihovny — a CI/CD nástroje
mají svou obdobu: opakovaně použitelné šablony pipeline. Tato lekce
ukazuje princip „pipeline jako kód“ v širším smyslu a konkrétní
mechanismy sdílení v GitLab CI a GitHub Actions.

Teorie

Pipeline jako kód

„Pipeline jako kód“ znamená, že definice CI/CD procesu (které kroky,
v jakém pořadí, s jakými podmínkami) žije jako verzovaný textový soubor
v repozitáři vedle aplikačního kódu, ne jako klikací konfigurace
v UI nějakého serveru. Výhody jsou stejné jako u Infrastructure as Code
(viz další modul): historie změn v Gitu, code review na změny pipeline,
možnost pipeline testovat a opakovaně použít, a žádná ruční konfigurace,
která by se dala v UI omylem rozbít bez záznamu.

Proč šablonizovat

Bez šablon se typicky opakují stejné bloky — instalace závislostí,
přihlášení do registry, nasazovací kroky — v .gitlab-ci.yml nebo
workflow souboru každého repozitáře zvlášť. Když se pak změní např.
verze nástroje pro bezpečnostní scan, je nutné to opravit ve všech
repozitářích ručně. Šablona centralizuje tuto logiku na jedno místo,
odkud si ji jednotlivé projekty jen „vypůjčí“ a případně parametrizují.

Mechanismy sdílení v GitLab CI

GitLab CI nabízí klíčové slovo include, kterým lze načíst definice
z jiného souboru — lokálního, z jiného projektu, nebo dokonce
vzdálené URL:

# .gitlab-ci.yml v konkrétním projektu
include:
  - project: 'platform/ci-templates'
    ref: main
    file: '/templates/docker-build.yml'
  - project: 'platform/ci-templates'
    ref: main
    file: '/templates/deploy-kubernetes.yml'

build:
  extends: .docker-build-template
  variables:
    IMAGE_NAME: moje-appka

deploy-staging:
  extends: .k8s-deploy-template
  variables:
    ENVIRONMENT: staging
    NAMESPACE: moje-appka-staging

Klíčové slovo extends umožňuje, aby konkrétní job „zdědil“ definici
šablonového jobu (jehož jméno tradičně začíná tečkou, což ho zároveň
skryje jako samostatný spustitelný job) a přepsal jen to, co je pro daný
projekt specifické — typicky proměnné.

Samotná šablona v centrálním repozitáři platform/ci-templates:

# templates/docker-build.yml
.docker-build-template:
  stage: build
  image: docker:24
  services:
    - docker:24-dind
  script:
    - docker build -t "$CI_REGISTRY_IMAGE/$IMAGE_NAME:$CI_COMMIT_SHORT_SHA" .
    - docker push "$CI_REGISTRY_IMAGE/$IMAGE_NAME:$CI_COMMIT_SHORT_SHA"

Mechanismus sdílení v GitHub Actions

GitHub Actions má pro tento účel reusable workflows — workflow
soubor, který lze zavolat z jiného workflow pomocí workflow_call,
a composite actions pro sdílení menších, opakujících se sekvencí
kroků.

Sdílený, znovupoužitelný workflow (uložený typicky v samostatném
repozitáři org/ci-templates):

# .github/workflows/docker-build.yml v repozitáři org/ci-templates
name: Reusable Docker build
on:
  workflow_call:
    inputs:
      image-name:
        required: true
        type: string
    secrets:
      registry-token:
        required: true

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Log in to registry
        run: echo "${{ secrets.registry-token }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
      - name: Build and push
        run: |
          docker build -t ghcr.io/${{ github.repository }}/${{ inputs.image-name }}:${{ github.sha }} .
          docker push ghcr.io/${{ github.repository }}/${{ inputs.image-name }}:${{ github.sha }}

Volání z konkrétního projektu:

# .github/workflows/ci.yml v konkrétním projektu
name: CI
on: [push]

jobs:
  build:
    uses: org/ci-templates/.github/workflows/docker-build.yml@main
    with:
      image-name: moje-appka
    secrets:
      registry-token: ${{ secrets.GHCR_TOKEN }}

Klíčové slovo uses s odkazem na cizí workflow soubor (org/repo/.github/ workflows/soubor.yml@ref) je ekvivalentem include/extends v GitLab
CI — parametrizace probíhá přes inputs a secrets deklarované v hlavičce
workflow_call.

Verzování šablon

Šablony samotné je vhodné verzovat — buď přes Git tag/ref (@main je
pohodlné, ale nebezpečné, protože se změna v šabloně projeví okamžitě
ve všech projektech bez varování; produkčně bezpečnější je pevná verze,
např. @v2.3.0), nebo přes samostatný release proces šablonového
repozitáře s changelogem, aby týmy mohly upgradovat řízeně.

Praktický příklad

Ukázka menší, ale časté šablony — composite action v GitHub Actions,
která sdílí instalaci závislostí a spuštění testů napříč více repozitáři
stejné technologie (Node.js):

# .github/actions/node-test/action.yml v repozitáři org/ci-templates
name: 'Node test'
description: 'Instaluje závislosti a spustí testy pro Node.js projekt'
inputs:
  node-version:
    description: 'Verze Node.js'
    required: false
    default: '20'
runs:
  using: 'composite'
  steps:
    - uses: actions/setup-node@v4
      with:
        node-version: ${{ inputs.node-version }}
    - run: npm ci
      shell: bash
    - run: npm test
      shell: bash

Použití v konkrétním projektu:

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: org/ci-templates/.github/actions/node-test@v1
        with:
          node-version: '22'

Shrnutí

„Pipeline jako kód“ znamená verzovanou, recenzovanou definici CI/CD
procesu vedle aplikačního kódu. Jakmile se stejné kroky opakují napříč
projekty, vyplatí se je extrahovat do sdílené šablony — v GitLab CI
pomocí include/extends, v GitHub Actions pomocí reusable workflows
(workflow_call) nebo composite actions. Šablony samotné je vhodné
verzovat pevnou referencí, aby změna v šabloně neovlivnila všechny
závislé projekty nekontrolovaně a najednou.

Kontrolní otázky

  1. V čem je princip „pipeline jako kód“ podobný principu Infrastructure
    as Code?
  2. Jaký je rozdíl mezi include/extends v GitLab CI a workflow_call
    v GitHub Actions z pohledu výsledného efektu?
  3. Proč je nebezpečné odkazovat na sdílenou šablonu přes @main místo
    pevné verze v produkčně důležité pipeline?
  4. Mini cvičení: navrhněte, jaké dva až tři kroky z pipeline vašeho
    (fiktivního) projektu byste extrahovali do sdílené šablony a proč
    právě ty.

Lekce na sebe nejsou zamčené — libovolnou lekci můžete otevřít i označit jako hotovou v jakémkoliv pořadí.