Fusk med FPM

(This article was originally published by Datormagazin. Republished with permission)

Fusk med FPM

Om du någon gång pakekterat ett program i enlighet med ett operativsystems pakethanterare, vet du att det kan vara ganska arbetsamt.

För att göra det ännu mer komplicerat finns det i Linux-världen ett antal olika varianter. Det två viktigaste är dock RPM, RPM package manager, en på klassiskt hacker-manér rekursiv akronym, samt Deb, Debians pakethanteringssystem.

Det finns många anledningar varför man vill paketera programvaror i enlighet med operativsystemets egna inbyggda mekanismer. både rpm och deb möjligör att man skapar kanaler för programvarudistribution på ett ganska enkelt sätt. Paketen kan sen installeras, uppdateras, och verifieras med distributionernas egna kommandon. Samma principiella förfarande används även på proprietära system som Microsoft Windows, även om teknikerna är annorlunda i praktiken.

Speciellt när man arbetar inom fälten Devops och kontinuerliga leveranser är det direkt nödvändigt att känna till hur de här systemen fungerar.

En färdig rpm eller deb är egenligen inte så mycket mer än ett arkiv i stil med en zipfil, med lite tillhörande metadata och skript. Man kan därför tycka att det borde vara relativt enkelt att skapa paket av den här typen. Det faktum att det finns en hel bok på området att tillverka rpm:er, Maximum Rpm, ger en fingervisning om att det kanske inte riktigt är så enkelt.

Rpm kräver exempelvis byggbeskrivningar i ett eget macroformat, .spec-filer som är baserade på m4 macron. formatet är flexibelt och användbart till det som det är avsett för, att bygga och paketera oförändrade källkodsarkiv, samt eventuellt patcha dem och anpassa för distribution.

När man själv paketerar nånting, kanske man har helt andra krav. Till exempel kanske vill använda ert företags interna byggserver för att bygga och paketera. Man kanske fortfarande vill utnyttja operativsystemets paketdistributionskanaler.

Man kanske bara vill göra ett quick and dirty paket och inte bygga upp en hel infrastruktur för att paketera och distribuera.

Fpm är ett verktyg som kan bistå vid sånna här tillfällen.

För distributioner är det viktigt att verktygen framtvingar konformism, vilket oftast är bra. I våra tänkta användningsfall är vi dock mer intresserade av att vår egna interna paket fungerar som vi själva önskar, och är smidiga att ta fram.

Det är inte tillrådligt att försöka bidra med ett paket byggt med fpm till tex Fedora eller Debian, eftersom de som arbetar med distributionerna antagligen kommer att börja tugga fradga. Men som sagt, i sin egen infrastruktur är man själv kung och vill kunna ha möjligheten att göra det som är bäst i sitt eget sammanhang.

En annan aspekt är att distributioner ofta har en policy angående vilka versioner av länkbibliotek och andra beroenden som används av applikationer. Det är i allmänhet bra, men kanske mindre bra när du vill kompilera och distribuera en applikation som kräver nyare bibliotek.

Författaren till fpm hävdar att han kan gå från källkod till paket färdigt för distribution på en minut, så vi börjar därför med att testa om det stämmer.

Först måste fpm installeras. Fpm är baserat på Ruby, och beroende på din distribution kan det behövas olika handgrepp. På Fedora måste man först installera paketet rubygems som ger tillgång till kommandot gem. gem används i sin tur för att installera fpm.

1yum install rubygems
2yum install ruby
3yum install ruby-devel
4gem install fpm

Denna process installerar ett shellscript som wrappar rubyprogrammet. När man har gjort detta kan man bygga ett paket med shellkommandot fpm. För att inte krångla till det så här i början, gör vi ett enkelt hello world program, som vi sedan skall paketera. Var lugn, det blir mer krångligt sen.

Programmet ser ut som följer:

1#!/bin/sh
2echo 'hej värld!'

Vi vill att kommandot skall installeras i /usr/local/bin katalogen i detta fall, så vi skapar en sådan katalogstruktur.

Nu gör vi scriptet exekvrbart, sedan paketerar vi:

1chmod a+x usr/local/bin/hej
2fpm -s dir -t rpm -n hej-vaerld -v 1 -C installdir usr

Detta kommando skapar en rpm med namnet hej-vaerld. Version 1.

För att testa paketet kan vi först lista innehållet, sedan installera det.

1rpm -qivp hej-vaerld.rpm
2rpm -ivh hej-vaerld.rpm

Shellscriptet skall nu ligga snyggt och prydligt i /usr/local/bin.

Vän av ordning invänder nu att /usr/local/bin inte är den katalog man enligt Red Hats paketenringsnormer bör använda för installationer. Det är riktigt, men illustrerar även en poäng med fpm. Det finns inget principiellt som hindrar oss att vare sig bryta eller följa regler.

Ofta har man företagsinterna regler om vart paket skall installeras, som tex i /opt. Fpm löser det oavsett.

Om vi istället vill paketera för debian-deriverade distributioner, går det också bra.

Här kan det vara på sin plats att backa ett steg, och ifrågasätta vad vi egentligen åstadkommit hittils. hade det inte varit enklare att helt enkelt kopiera in filen direkt? i detta enkla fall, är svaret självklart ja. värdet av paketeringen infinner sig först när man har program man vill distribiuera ut på flera maskiner, och gärna även kunna uppdatera smidigt.

Att illustrera detta är aningen mer arbete, men är ändå ganska enkelt.

Vi skall nu göra en yum-kanal för vårt paket.

vi lägger paketet i en katalog som exporteras via en webserver. Eftersom det här är en övning, gör vi det kopiera vår rpm till katalog som exporteras via en webserver. Sedan skapar vi de nödvändga databasfilerna för yum.

1createrepo --database /var/www/html/yum

Därefter måste klientmaskinerna prenumerera på kanalen, genom att installera följande fil i /etc/yum.repos.d/local.repo:

1[local]
2name=local
3baseurl=http://localhost/yum
4enabled=1
5gpgcheck=0
6priority=1
7skip_if_unavailable = 1
8keepcache = 0

På klientmaskinen,som för övningsändamål gärna även den kan vara localhost, kan vi nu göra:

1yum install hej-vaerld

programmet installeras.

vill vi nu göra en updatering, stegar vi bara upp versionen på paketet, bygger om kanalen med createrepo, och gör sedan yum upgrade på klientmaskinerna.

Om man har en byggserver som till exempel Jenkins, kan man använda byggnummerfunktionen i byggservern för att stega upp versionen på paketet automatiskt. Man kan även koppla versionsnummret till sitt versionshanteringsystem, som till exempel antal incheckningar sen källkodsarkivets början.

På det här sättet kan man alltså relativt enkelt skapa en lågbudget yumkanal för sin applikation.

Att generera deb paket för debian är lika enkelt

1fpm -s dir -n hej -t deb usr

Om man jobbar på en distribution och vill arbeta med paket för en annan distribution är det bekvämt att installera paketverktyg för den andra distributionen. på Fedora kan man installera både apt-get och dpkg. När vi gjort det kan vi analysera den nyligen skapade deb-filen.

1dpkg -c hej_1.0_amd64.deb

Och finner att listningen är som vi förväntar oss.

fpm kan utgå från ett antal olika typer av källor, och paketera ett antal olika typer av pakettyper. Eftersom fpm inte ingår i Fedoras paketkanaler, kan man använda fpm för att paketera sig själv. Exemplet illustrerar att fpm kan använda ett namngivet rubygem som källa:

1fpm -s gem -t rpm fpm

Fpm hämtar i detta fall automatiskt hem det angivna rubygemet och paketerar det.

Vi kan lista innehållet, och konstatera att fpm om inget annat anges, väljer att installera i användarens hemkatalog. Man kan styra vilken katalog som skall användas för installation med --prefix flaggan om man inte är nöjd med skönsvärdet, tex --prefix /usr/share/gems/gems.

1  rpm -qilp
2rubygem-fpm-1.1.0-1.noarch.rpm

Om du nån gång behövt paketera ett rubyprogram för ditribution inser att det här är mycket användbart. Vissa typer av programvaror är välldigt snabbrörliga, och ruby är ett bra exempel.

Det finns ett antal olika typer av intressanta pakettyper man kan skapa. För en utförlig lista av möjliga flaggor, använd fpm --help. Alla typer är dock dessvärre inte problemfria. pakettypen osxpkg som skapar paket för macosx, kräver som ett exempel programmet pkgbuild vilket bara finns tillgängligt för macosx. Man kan alltså inte utan vidare paketera för macosx på fedora.

Nu skall vi paketera ett mer komplext programg, för att vidare illustrera fpm:s användning.

Programmet är en gren av Emacs, Xwidget Emacs. Det speciella med den är att den implementerar en web-vy i editorn med hjälp av webkit. Det är en egenskap som inte finns i standardvarianten av Emacs. Eftersom Xwidget Emacs mer eller mindre är en betaversion uppdateras den relativt ofta. Vidare finns den inte paketerad i standardkanaler. Ett fall för fpm!

1  bzr co bzr branch http://bzr.savannah.gnu.org/r/emacs/xwidget
2./autogen.sh
3./configure --with-xwidgets  --with-x-toolkit=gtk3
4make -j4 bootstrap
5mkdir -p installdir
6make install DESTDIR=installdir
7fpm -s dir -t rpm -n xwidget-emacs -v $BUILD_NUMBER -C installdir usr

vi vill också kunna beskriva vilka beroenden appliktionen har. En av de stora fördelarna med att distribuera paket via kanaler är ju att beroenden installeras automatiskt vid behov. För detta kan man använda -d flaggan, exempelvis i detta fall -d gtk3.

Om man vidare kombinerar detta med jenkinsreceptet får man alltså en färsk xwidget emacs via sitt privata yumrepo när man önskar. byggservern kan bygga om paketet så fort programmets källkodsarkiv uppdateras, och även uppdatera distributionskanalen.

fpm illustrerar att regler är bra, men är också till för att brytas. du bestämmer i din egen infrastruktur!

faktaruta: för mer exempel och källkod se http://www.verona.se/projects/hello-fpm.html och även http://www.verona.se/projects/xwidget-emacs.html