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:
- konfigurační soubor distro/config/apkg.toml
- cestu k šablonám balíků distro/pkg/
- nalezené šablony balíků deb a rpm a podporovaná distra
- detekované distro – debian 11
- zvolený balící styl – deb
- zvolená šalbona balíku – distro/pkg/deb
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. 📦