apkg: nový nástroj pro automatizaci upstream balení

Vítejte v historicky prvním článku o novém nástroji apkg, který vznikl v Laboratořích CZ.NIC, aby vývojářům usnadnil tvorbu softwarových balíků pro různá (nejen) linuxová distra přímo ze zdrojů.

Začnu úvodem o tom, proč svět takový nástroj potřebuje a proč by vás to mohlo zajímat, pokud vyvíjíte software. V druhé půlce článku pak naleznete ukázku použití v praxi.

V textu hojně odkazuji na relevantní sekce oficiální dokumentace (v angličtině), nestyďte se tam nahlédnout v případě nejasností.

Pojmy

Píši o problematice balení poprvé v češtině, takže je na místě definice klíčových pojmů:

  • distribuce software – proces doručování softwaru k uživatelům,
  • distro – konkrétní množina softwaru k distribuci (např. Debian, Arch, Fedora, …),
  • balík (package) – archiv softwaru v distribučním formátu,
  • balič (packager) – osoba, která vytváří a spravuje balíky,
  • správce balíků (package manager) – nástroj pro správu balíků (např apt, dnf, pacman, …),
  • upstream – původní vývojáři softwaru.

Formáty balíků

Mezi stovkami existujících dister otevřeného softwaru existuje široká škála formátů balíků a také nástrojů pro jejich správu. Z mé zkušenosti jsou v divočině současně nejrozšířenější a nejpoptávanější balíky pro:

  • Debian a příbuzné: Ubuntu, Mint, MX, …,
  • Fedora a příbuzné: CentOS, Rocky, RHEL, SUSE, …,
  • Arch a příbuzné: Manjaro, Garuda, …

Sehnat balík v podporovaném formátu však ještě neznamená, že bude na systému fungovat. Závislosti balíků jsou obvykle generovány pro konkrétní vydání konkrétního distra a tedy není zaručena ani kompatibilita mezi různými verzemi stejného distra, natož mezi různými distry a jejich verzemi.

Distro balíky

Distribuce softwaru je komplexní problém a možná i proto je ve světě otevřeného software zvykem delegovat ji na specializované baliče. Každé linuxové distro má jinou politiku, procesy, zvyklosti a nástroje a z nich vyplývající zážitek pro své přispěvatele a uživatele.

Univerzální je však oddělení vývoje softwaru od jeho distribuce/balení. Zdrojové soubory pro tvorbu distro balíků žijí odděleně od zdrojů projektu. Práce vývojářů obvykle končí s vydáním nové verze a o tvorbu balíků se starají neopěvovaní hrdinové komunit jednotlivých dister.

Tento distribuovaný komunitní systém má své výhody i nevýhody. Zřejmou výhodou je efektivní dělba práce – vývojáři nemusí připravovat balíky pro stovky různých systémů přičemž každý z nich má svá specifika a temná zákoutí, ve kterých zkušený balič už umí chodit, ale neznalý vývojář je odsouzen k mnoha hodinám zbytečného utrpení. Pro uživatele je nejjednodušší získat software z oficiálních distro repozitářů a je zaručena určitá míra kompatibility s ostatními balíky v rámci distra.

Mezi nevýhody patří ztráta kontroly nad celým aspektem distribuce softwaru. Konzervativní distra jako Debian stable mají striktní pravidla, která omezují možnosti aktualizace balíků na novější verze. To může být problematické pro projekty s velkou kadencí vydání nebo pro projekty závisející na nových verzích závislostí, které ještě nejsou dostupné.

Různé politiky dister mohou vést k nekonzistencím mezi balíky stejného projektu napříč distry, například na Debianu je zvykem služby v balíku standardně povolit, na Fedoře jsou obvykle zakázané.

Nezávislí baliči obvykle nemají tak hlubokou znalost baleného softwaru jako vývojáři, což může vést k suboptimálním balíkům a chování v rozporu se záměry upstreamu.

Dle mého názoru také často dochází k nezdravému oddělení vývojářů od reálných zážitků uživatelů s jejich softwaru. V ideálním případě funguje komunikace mezi baliči a vývojáři směrem k lepšímu uživatelskému zážitku, ale to nelze zaručit, takže často žijí balíky svým vlastním životem a nebo naopak nežijí vůbec.

Upstream balíky

Jak jsem nastínil výše, existuje mnoho dobrých důvodů, proč nedelegovat veškeré balení na distro baliče a místo toho ho zahrnout do upstream vývojoveho procesu.

Upstream balíky mohou posloužit uživatelům na distrech, kde nejsou oficiální balíky dostupné nebo jsou ve starých verzích. Tvorba a instalace balíků na různých systémech může být zahrnuta v CI projektu, což umožní odchytit problémy před vydáním. Vývojáři mají plnou kontrolu nad upstream balíky a jejich repozitáři.

U nových projektů dává smysl začít upstream balíky a postupně je dostat do dister, což může trvat delší dobu. U starších projektů v distrech jsou zase upstream balíky způsob, jak poskytnout uživatelům nejnovější verze nezávisle na politikách a cyklech jednotlivých dister.

Na rozdíl od zdrojových kódů distro balíků, které bydlí v oddělených repozitářích, nic nebrání zahrnutí zdrojů upstream balíku přímo do zdrojového kódu projektu. Neexistuje však jednotný standard ani adekvátní nástroje, a tak to každý projekt řeší po svém.

V divočině můžeme vidět zdroje balíků různě rozmístěné v projektech, často v adresářích jako debian/ či rpm/ a obvykle doplněné kontroverzními skripty pro sestavení balíku. Zážitek neznalé osoby s tímto chaosem může být až traumatický.

apkg 📦

apkg vzniklo jako chybějící dílek skládačky upstream balení. Cílem je schopnost postavit kdykoliv balíky projektu pro konkrétní distro přímo ze zdrojů jedním příkazem.

apkg zavádí jednoduché konvence ohledně umístění zdrojů balíků v rámci projektu a poskytuje jednotné minimální rozhraní k balícím nástrojům podporovaných dister. Ve výsledku pak stačí ve zdrojích projektu zavolat apkg build a výsledkem jsou balíky projektu pro detekovaný systém.

Zvolené konvence jsou souborem dobrých zvyklostí z balící praxe spojených dohromady s cílem automatizovaného a konzistentního procesu tvorby upstream balíků. Zásadní vliv měly projekty Knot Resolver a Knot DNS, které už před nasazením apkg stavěly upstream balíky přímo ze zdrojů pro širokou škálu dister.

apkg lze plnohodnotně používat jak interaktivně z terminálu, tak v rámci CI/CD systémů, ať už jako příkaz ve skriptech nebo jako python modul. apkg CI průběžně testuje funkčnost apkg na 5 různých projektech napříč 11 distry.

Důležité: apkg negeneruje zdrojové soubory balíků ani je automaticky nemodifikuje – tato práce je stejně jako v případě distro balíků zodpovědností baličů.

Více se můžete dočíst v dokumentaci, je čas se podívat, jak to vypadá v praxi.

apkg na vlastní kůži

Pro demonstraci použiji projekt libyang, protože má málo závislostí a kompiluje se rychle, ale proces je univerzální a můžete ho aplikovat na kterýkoliv z projektů, které používají apkg.

Pokud si chcete apkg vyzkoušet, nastartuje systém založený na Debianu (.deb balíky) nebo Fedoře (.rpm balíky). Můžete použít váš vývojový systém, pokud vám nevadí balící závislosti a nebo virtuální stroj či kontejner dle libosti.

Instalace apkg

apkg je momentálně zhruba po roce vývoje v BETA fázi, kdy ho první projekty používají v produkci, ale vývoj je stále svižný, takže než dospěje, je oficiální cestou instalace PyPI:

pip3 install apkg

Více možností instalace naleznete v dokumentaci.

Příprava systému

Jednorázově nainstalujeme balící závislosti:

[debian-11]$ apkg system-setup

I system setup for packaging
I target distro: debian 11
# sudo -E apt-get install -y devscripts
✓ system ready for packaging

Zdroje projektu

Seženeme si zdrojové kódy projektu a dostavíme se do jeho kořenového adresáře:

git clone https://github.com/CESNET/libyang
cd libyang

Všechny další apkg příkazy spouštíme v kořenovém adresáři projektu.

Průzkum terénu – apkg status

Je čas zjistit, co si o tom myslí apkg:

[debian-11]$ apkg status

project name:            libyang
project base path:       /mnt/src/libyang
project VCS:             git
project config:          distro/config/apkg.toml (exists)
package templates path:  distro/pkg (exists)
package templates:
    deb: deb pkgstyle default: ubuntu | debian | linuxmint
    rpm: rpm pkgstyle default: fedora | centos | rocky | rhel | opensuse

current distro: debian 11 / Debian GNU/Linux 11 (bullseye)
    package style: deb
    package template: distro/pkg/deb

Ve výstupu vidíme:

Když spustíme apkg status ve stejném adresáři na Fedora 34 systému, vidíme změnu v detekovném distru a z toho vyplývající jiný zvolený balící styl a šablonu:

[fedora-34]$ apkg status

...

current distro: fedora 34 / Fedora 34
    package style: rpm
    package template: distro/pkg/rpm

apkg podporuje také Arch, ale libyang pro něj neobsahuje balící šablonu (obvykle distro/pkg/arch):

[arch]$ apkg status

...

current distro: arch / Arch Linux
    package style: unsupported
    package template: unsupported

Vstupní adresář – distro

distro/ adresář je konvencí vstupní adresář apkg, do kterého nikdy nezasahuje.

$ tree distro

distro
├── config
│   └── apkg.toml
├── pkg
│   ├── deb
│   │   ├── changelog
│   │   ├── control
│   │   ├── rules
│   │   └── [a další debian balící soubory...]
│   └── rpm
│       └── libyang.spec
└── scripts
    ├── make-archive.sh
    └── upstream-version.sh

distro/config adresář obsahuje jediný konfigurační soubor
apkg.toml, který odkazuje na skripty v distro/scripts:

  • make-archive.sh pro vytvoření archivu projektu z aktualních zdrojů
  • upstream-version.sh pro zjištění aktuální upstream verze projektu

Tyto skripty jsou volitelné a mohou bydlet i mimo distro adresář.

Každý adresář v distro/pkg je konvencí šablona balíku – zdrojové soubory balíku pro určitý balící styl šablonované pomocí univerzálního jinja šablonovacího systému, který poskytuje extra flexibilitu potřebnou pro automatizaci.

Zdrojové soubory v šablonách jsou stejné jako ve zdrojích oficiálních distro balíků, jen s extra možností šablonování jako například náhrada {{ version }} aktuální verzí balíku atd.

distro/pkg/deb šablona obsahuje zdrojové soubory pro vytvoření balíku na Debianu a klonech.

distro/pkg/rpm šablona obsahuje zdrojové soubory pro vytvoření balíku na RPM systémech (Fedora a její klony).

Instalace balících závislostí

Bez dalšího otálení nainstalujeme závislosti pro sestavení balíku. Potřebujeme mít zprovozněné sudo a nebo být root:

[debian-11]$ apkg build-dep

I installing build deps
I target distro: debian 11
I build deps from template: distro/pkg/deb
I installing 4 build deps...
# sudo -E apt-get satisfy -y cmake debhelper (>= 10) libpcre2-dev (>= 10.21) pkg-config

To samé na Fedoře:

[fedora-34]$ apkg build-dep

I installing build deps
I target distro: fedora 34
I build deps from template: distro/pkg/rpm
I installing 4 build deps...
# sudo dnf install -y cmake gcc make pkgconfig(libpcre2-8) >= 10.21

Tento krok lze vykonat také v rámci příkazů build a install pomocí přepínače -b/--build-dep pro maximální pohodlí.

Závislosti lze také pouze vypsat bez instalace přepínačem -l/--list a zpracovat dle uvážení:

[debian-11]$ apkg build-dep --list

I listing build deps
I target distro: debian 11
I build deps from template: distro/pkg/deb
cmake
debhelper (>= 10)
libpcre2-dev (>= 10.21)
pkg-config

Sestavení balíků 📦

Konečně je čas sestavit balíky pomocí hlavního příkazu apkg build!

[debian-11] apkg build

I building packages
I target distro: debian 11
I creating dev source package
I creating dev archive
I running make_archive_script: distro/scripts/make-archive.sh
✓ made archive: pkg/archives/dev/libyang-2.0.97.tar.gz
I package style: deb
I package template: distro/pkg/deb
I building deb source package: libyang-2.0.97
✓ made source package: pkg/srcpkgs/debian-11/libyang2-2.0.97-1/libyang2_2.0.97-1.dsc
I unpacking source package for direct host build
I starting direct host build using dpkg-buildpackage
✓ built 6 packages in: pkg/pkgs/debian-11/libyang2_2.0.97-1

Stejný příkaz ve stejném adresáři na Fedoře:

[feodra-34] apkg build

I building packages
I target distro: fedora 34
I creating dev source package
I creating dev archive
✓ reuse cached archive: pkg/archives/dev/libyang-2.0.97.tar.gz
I package style: rpm
I package template: distro/pkg/rpm
I building .src.rpm using rpmbuild
✓ made source package: pkg/srcpkgs/fedora-34/libyang-2.0.97-1/libyang-2.0.97-1.fc34.src.rpm
I starting direct host .rpm build using rpmbuild
✓ built 6 packages in: pkg/pkgs/fedora-34/libyang-2.0.97-1.fc34

Zde si kromě změn kvůli jinému distru můžete všimnout odchylky v podobě:

✓ reuse cached archive: pkg/archives/dev/libyang-2.0.97.tar.gz

apkg zjistilo, že se stav projektu nezměnil od minula a není tedy třeba znova vyvářet zdrojový archiv, místo toho přepoužilo ten vytvořený v rámci předchozího buildu na Debianu.

Když spustíme stejný příkaz znova beze změn v projektu, apkg prostě přepoužije předchozí výstupy:

[feodra-34] apkg build

✓ reuse cached archive: pkg/archives/dev/libyang-2.0.97.tar.gz
✓ reuse cached source package: pkg/srcpkgs/fedora-34/libyang-2.0.97-1/libyang-2.0.97-1.fc34.src.rpm
✓ reuse 6 cached packages

Cache lze vypnout přepínačem --no-cache.

Výstupní adresář – pkg

Adresář pkg/ je výstupní adresář apkg, který obsahuje veškerý výstup – archivy, zdrojové balíky, balíky a všechno mezi. Tento adresář můžete kdykoliv bez obav smazat.

$ tree pkg

pkg
├── archives
│   └── dev
│       └── libyang-2.0.97.tar.gz
├── build
│   ├── pkgs
│   │   ├── debian-11
│   │   │   └── libyang2_2.0.97-1
│   │   └── fedora-34
│   │       └── libyang-2.0.97-1.fc34
│   └── srcpkgs
│       ├── debian-11
│       │   └── libyang2-2.0.97-1
│       └── fedora-34
│           └── libyang-2.0.97-1
├── pkgs
│   ├── debian-11
│   │   └── libyang2_2.0.97-1
│   │       └── libyang*.deb
│   └── fedora-34
│       └── libyang-2.0.97-1.fc34
│           └── libyang*.rpm
└── srcpkgs
    ├── debian-11
    │   └── libyang2-2.0.97-1
    │       ├── libyang2_2.0.97-1.debian.tar.xz
    │       ├── libyang2_2.0.97-1.dsc
    │       └── libyang2_2.0.97.orig.tar.gz
    └── fedora-34
        └── libyang-2.0.97-1
            └── libyang-2.0.97-1.fc34.src.rpm

Více o struktuře výstupního adresáře se můžete dočíst v dokumentaci.

Instalace balíků

apkg umožňuje výsledné balíky nainstalovat, což se hodí zejména v rámci testování:

[debian-11]$ apkg install

I installing packages
✓ reuse cached archive: pkg/archives/dev/libyang-2.0.97.tar.gz
✓ reuse cached source package: pkg/srcpkgs/debian-11/libyang2-2.0.97-1/libyang2_2.0.97-1.dsc
✓ reuse 6 cached packages
# sudo -E apt-get install -y [seznam balíků k instalaci]
✓ installed 6 packages

apkg opět přepoužilo už postavené balíky. Kdyby nebyly dostupné, postavilo by je pomocí apkg build.

Hierarchie příkazů

Tím se dostáváme k hierarchii apkg příkazů (převzato z dokumentace):

                        apkg packaging workflow

 +------------------------------+    +------------------------------------+
 |                              |    |                                    |
 |     $ apkg make-archive      |    |     $ apkg get-archive [-v 1.2.3]  |
 |                              |    |                                    |
 |   in: current project state  | OR |   in: archive hosted online        |
 |                              |    |                                    |
 |  out: pkg/archives/dev/*.xz  |    |  out: pkg/archives/upstream/*.xz   |
 |                              |    |                                    |
 +---------------+--------------+    +----------------+-------------------+
                 |                                    |
                 |                                    |
                 |                                    |
                 v                                    v
      +----------+------------------------------------+-------------+
      |                                                             |
      |     $ apkg srcpkg                                           |
      |                                                             |
      |   in: distro/pkg/$TEMPLATE/  (package template)             |
      |       pkg/archives/*/*.xz    (archive)                      |
      |                                                             |
      |  out: pkg/srcpkgs/$DISTRO/$SRCPKG         (source package)  |
      |       pkg/build/srcpkgs/$DISTRO/$SRCPKG/  (build dir)       |
      |                                                             |
      +------------------------------+------------------------------+
                                     |
                                     |
                                     |
                                     v
      +------------------------------+------------------------------+
      |                                                             |
      |     $ apkg build                                            |
      |                                                             |
      |   in: pkg/srcpkgs/$DISTRO/$SRCPKG  (source package)         |
      |                                                             |
      |  out: pkg/pkgs/$DISTRO/$PKG        (package)                |
      |       pkg/build/pkgs/$DISTRO/$PKG  (build dir)              |
      |                                                             |
      +-------------------------------------------------------------+

Každý apkg příkaz, který generuje soubory, vypisuje na standardní výstup jeden výstupní soubor na řádek. Všechny příkazy, které očekávají vstupní soubory, podporují argument -F/--file-list pro specifikaci ze souboru včetně - pro standardní vstup.

Ve výsledku lze apkg příkazy zapojit za sebe. Když chybí vstupní soubory, apkg se pokusí chytře použít adekvátní příkaz pro jejich vytvoření.

Standardní chování apkg install lze explicitně rozepsat na jednotlivé příkazy:

apkg make-archive \                    # vytvoř archiv projektu
    | apkg srcpkg --archive -F - \     # vytvoř zdrojový balík
    | apkg build --srcpkg -F - \       # postav balíky
    | apkg install --custom-pkgs -F -  # nainstaluj balíky

Závěr

apkg umí ještě další užitečné triky jako je stavba balíků z upstream archivů, ale pro dnešek se spokojíme s balíky přímo ze zdroje.

Pokud něco po cestě selhalo, zkuste přidat -L verbose a nebo -L debug hned za apkg pro podrobnější (a barevnější) výstup, např.

apkg -L debug build --build-dep

apkg existuje, aby zlepšilo život vývojářům, baličům, i uživatelům a zpětná vazba je klíčová pro jeho vývoj. Neváhejte otevřít Issue.

Nechť vaše balíky přináší uživatelům radost. 📦

Autor:

Zanechte komentář

Všechny údaje jsou povinné. E-mail nebude zobrazen.

Tato stránka používá Akismet k omezení spamu. Podívejte se, jak vaše data z komentářů zpracováváme..