GitLab CI

Безперервна інтеграція (CI) — спосіб автоматично тестувати, збирати та перевіряти код щоразу, коли вносяться зміни.

GitLab надає вбудовані можливості CI/CD через файл .gitlab-ci.yml. Цей файл у корені репозиторію повідомляє GitLab, як збирати та тестувати проєкт. Він визначає етапи та скрипти, які запускаються в чистому середовищі при кожному push.

У цьому документі описано, як працює конвеєр GitLab CI/CD Lumi, зокрема роль файлу .gitlab-ci.yml, скриптів оболонки та зовнішніх інструментів, таких як Meson і Ninja.

Детальну технічну документацію процесу збірки Lumi CI див. у README-CI.md у репозиторії.

Основи GitLab CI/CD

CI керується файлом .gitlab-ci.yml. Він визначає:

  • Етапи: упорядковані групи завдань (наприклад, build-this, build-that, package-up)
  • Завдання: окремі задачі в кожному етапі
  • Скрипти: команди оболонки для кожного завдання
  • Runners: комп’ютери, на яких GitLab виконує завдання конвеєра

У Lumi етапи конвеєра:

  • dependencies
  • build lumi
  • appimage

Збірки в контейнерах

Конвеєр Lumi використовує контейнеризацію для узгоджених збірок:

  1. Створення контейнера збірки: перший етап використовує Buildah для створення образу Docker з усіма залежностями
  2. Використання контейнера: наступні етапи виконуються всередині цього контейнера в узгодженому середовищі
  3. Відтворювані збірки: ізоляція контейнера гарантує однакові результати на різних runner’ах

Цей підхід забезпечує однакову поведінку збірок на будь-якому runner GitLab і контрольоване середовище для складних процесів.

Інтегровані джерела залежностей

Образ залежностей CI Lumi збирає форкнутий стек із інтегрованих джерел у репозиторії (не зовнішніх клонів):

  • lumi-babl/ (BABL)
  • lumi-gegl/ (GEGL)
  • lumi-gtk3/ (GTK3)

Ці каталоги копіюються в контекст збірки контейнера та компілюються в префікс залежностей (зазвичай /opt/lumi-deps). Це робить CI відтворюваним і гарантує, що збірка AppImage використовує ті самі джерела, що й локальна розробка.

Роль скриптів оболонки

Завдання в .gitlab-ci.yml зазвичай викликають команди оболонки безпосередньо. Складні операції часто виносять у окремі скрипти в репозиторії.

CI Lumi використовує модульні скрипти оболонки для організації логіки збірки:

Приклад виклику скрипту:

script:
  - bash build/linux/appimage/lumi-goappimage.sh 2>&1 | tee appimage_creation.log

Переваги цього підходу:

  • Чистий YAML: файл .gitlab-ci.yml зосереджений на структурі завдань
  • Підтримуваність: складну логіку легше налагоджувати та змінювати в скриптах оболонки
  • Повторне використання: скрипти можна використовувати в різних контекстах і середовищах
  • Модульність: різні аспекти збірки можна розділити на окремі скрипти

Це зберігає конфігурацію CI чистою й водночас дозволяє складні процеси збірки.

Інтеграція із системами збірки

Lumi використовує Meson і Ninja для підготовки та збірки коду.

Наприклад:

script:
  - meson setup _build-${CI_RUNNER_TAG} -Dprefix="${LUMI_PREFIX}"
  - ninja -C _build-${CI_RUNNER_TAG}
  - ninja -C _build-${CI_RUNNER_TAG} install

Тут:

  • meson setup готує каталог збірки та генерує build.ninja
  • ninja виконує команди збірки згідно з визначенням

Структура системи збірки Meson

Система збірки Meson використовує кореневий файл meson.build у кореневому каталозі проєкту. Він визначає конфігурацію верхнього рівня та точку входу процесу збірки.

  • Кореневий meson.build зазвичай знаходиться в тому самому каталозі, що й .gitlab-ci.yml
  • Звідти він рекурсивно переходить у підкаталоги, кожен з яких може мати власний meson.build
  • Ці файли підкаталогів визначають цілі, джерела, залежності та інструкції збірки для відповідного каталогу

Змінні середовища

Ключові змінні конвеєра Lumi:

variables:
  DEBIAN_FRONTEND: "noninteractive"  # Без інтерактивних запитів
  DEB_VERSION: "trixie"              # Версія Debian для узгодженості
  CI_RUNNER_TAG: "x86_64"            # Архітектура

Змінні конкретного завдання:

build-lumi:
  variables:
    COMPILER: "clang"                                           # Вибір компілятора
    LINKER: "lld"                                               # Вибір лінкера
    LUMI_PREFIX: "${CI_PROJECT_DIR}/_install-${CI_RUNNER_TAG}"  # Шлях встановлення
    DEPS_PREFIX: "/opt/lumi-deps"                               # Префікс готових залежностей
    MESON_OPTIONS: "-Dpkgconfig.relocatable=true -Drelocatable-bundle=yes"  # Параметри збірки

Ці змінні керують поведінкою збірки та забезпечують узгодженість між етапами та runner’ами.

Приклад структури

project-root/
├── .gitlab-ci.yml
├── meson.build              <-- Кореневий файл Meson
├── src/
│   ├── meson.build          <-- Файл Meson підкаталогу
│   └── some_source.c
├── data/
│   ├── meson.build
│   └── icons/

У цій структурі:

  • кореневий meson.build налаштовує загальне середовище збірки
  • файли meson.build підкаталогів описують деталі компіляції для конкретних компонентів або модулів
  • така ієрархія робить логіку збірки модульною та зручною для підтримки

Артефакти між етапами

Артефакти — файли, створені завданнями, які потрібні на наступних етапах:

build-lumi:
  # ...конфігурація завдання...
  artifacts:
    paths:
      - "${LUMI_PREFIX}/"      # Файли встановлення
      - _build-${CI_RUNNER_TAG}/meson-logs/meson-log.txt  # Журнали збірки

Етапи конвеєра та залежності

Конвеєр Lumi складається з трьох основних етапів:

  1. Залежності: створює контейнерне середовище збірки з усіма необхідними інструментами та бібліотеками
  2. Збірка Lumi: компілює Lumi за допомогою Meson і Ninja у підготовленому середовищі
  3. AppImage: пакує зібрану програму у формат AppImage для розповсюдження

Залежності між етапами:

build-lumi:
  needs: [deps-debian]  # Чекає на контейнер залежностей

lumi-appimage:
  needs: [build-lumi] # Чекає на збірку програми

Кожен етап запускається лише після успішного завершення залежностей, що забезпечує правильний порядок збірки та доступність артефактів.

Поточні назви завдань

.gitlab-ci.yml Lumi наразі визначає такі назви завдань:

  • deps-debian
  • build-lumi
  • lumi-appimage

Підсумок

  • .gitlab-ci.yml визначає структуру та логіку конвеєра
  • завдання містять команди оболонки або зовнішні скрипти
  • інструменти на кшталт Meson і Ninja використовуються всередині завдань як частина процесу збірки

Lumi використовує GitLab CI для автоматичної збірки AppImage на платформах на базі Debian. Конвеєр збирає залежності, компілює Lumi, а потім пакує AppImage.

Для деталей на рівні вихідного коду див.:

  • .gitlab-ci.yml у корені репозиторію Lumi
  • build/linux/appimage/lumi-goappimage.sh
  • build/linux/appimage/README-CI.md

Вичерпні технічні відомості про процес збірки Lumi CI — налаштування середовища, архітектура скриптів і усунення несправностей — див. у README-CI.md.