V článku bude demonstrováno použití mock a standardního FTP/HTTP serveru pro možnost tvorby vlastního repozitáře. Toto může být vhodné pro případ, že není možné z licenčních či jiných důvodů (např. konflikt s existujícím balíkem) využít poskytované služby build service (openSUSE nebo Fedora).

Úvod

Mock je nástroj využívaný distribucích založených na RPM/YUM (Fedora, CentOS, RHEL) k sestavování RPM balíčků v chroot prostředí. Na jednom systému můžeme vytvářet balíčky pro různé distribuce a někdy i pro různé architektury (např. i686 a x68_64).

Pro podrobnější studium mohu doporučit následující odkazy:

Většinu věcí se při práci s mock je možno provádět pod běžným uživatelským účtem. Zde budeme používat tuto konvenci: pokud budou potřeba vyšší oprávnění (root), bude použit příkaz "sudo", ale je možné to dělat i
přepnutím na root pomocí "su".

Instalace

mock budeme instalovat v distribuci Fedora 17 a balíčky budeme vytvářet pro CentOS 5 a CentOS 6, resp. pro Fedoru. Nejprve nainstalujeme potřebné balíčky:

sudo rpm -ivh http://download1.rpmfusion.org/free/fedora/rpmfusion-free-release-stable.noarch.rpm \
   http://download1.rpmfusion.org/nonfree/fedora/rpmfusion-nonfree-release-stable.noarch.rpm

sudo yum install mock.noarch \
   mock-rpmfusion-free.noarch mock-rpmfusion-nonfree.noarch \
   fedora-packager.noarch

Příprava prostředí

Aby mohl celou úlohu provádět i někdo další, bude popsána pomocí proměnných. Pokud je nastavíme dle svého, další postup již můžeme využít beze změn. Provedeme tedy nastavení proměnných použitých pro instalaci i konfiguraci.

MOCKCFGDIR="/etc/mock"
MOCKDIR="/export/mock"
MOCKBASEDIR="${MOCKDIR}/base"
MOCKCACHEDIR="${MOCKDIR}/cache"
REPOBASEDIR="${MOCKDIR}/repo"

MYMOCKUSER=franta

FTPSERVER="ftp1.mojefirma.cz"
FTPSERVERSSH="22"
FTPBASEDIR="/var/ftp/pub/mojefirma"
FTPSERVERMIRRORS="ftp2.mojefirma.cz ftp3.mojefirma.cz"

Potřebujeme tedy běžného uživatele, který bude balíčky vytvářet - musí být členem skupiny mock. Vytvoření uživatele, pod kterým budeme vše ostatní provádět:

sudo useradd -m -G mock $MYMOCKUSER

nebo přidání aktuálně přihlášeného uživatele do skupiny mock:

MYMOCKUSER=$LOGNAME
sudo usermod --append --groups mock $MYMOCKUSER

Bude nutné nové přihlášení pod účtem $MYMOCKUSER a nové nastavení proměnných:

su - $MYMOCKUSER

a nastavení práv:

sudo chown -R :mock ${MOCKCFGDIR}
sudo chmod -R g+w ${MOCKCFGDIR}

Nastavení práv by mohlo jít i s pomocí ACL, ale to nemusí fungovat:

# sudo setfacl -d -m g:mock:rwx ${MOCKCFGDIR}
# sudo setfacl -R -m g:mock:rw ${MOCKCFGDIR}/*

Vytvoříme kompletní adresářovou strukturu pro repozitář pro dvě distribuce: CentOS 5/6 a Fedora 17/18 a 3 varianty: os, updates, testing:

if [ ! -d "$REPOBASEDIR" ] ; then 
   sudo mkdir -p "$REPOBASEDIR"
fi

for dist in fedora ; do
   for distnr in 17 18 ; do
      for repotype in os updates testing ; do
         sudo mkdir -p ${REPOBASEDIR}/${dist}/${distnr}/${repotype}/{i386/RPMS,x86_64/RPMS,SRPMS}
      done
   done
done

for dist in centos ; do
   for distnr in 5 6 ; do
      for repotype in os updates testing ; do
         sudo mkdir -p ${REPOBASEDIR}/${dist}/${distnr}/${repotype}/{i386/RPMS,x86_64/RPMS,SRPMS}
      done
   done
done

sudo chgrp -R mock ${REPOBASEDIR}
sudo chmod -R g+w ${REPOBASEDIR}

Upravíme nastavení mock v ${MOCKCFGDIR}:

cp ${MOCKCFGDIR}/site-defaults.cfg ${MOCKCFGDIR}/site-defaults.cfg-orig

sed -e "s@# config_opts\['basedir'\] = '/var/lib/mock/'@config_opts['basedir'] = '${MOCKBASEDIR}'@" \
    -e "s@# config_opts\['cache_topdir'\] = '/var/cache/mock'@config_opts['cache_topdir'] = '${MOCKCACHEDIR}'@" \
   ${MOCKCFGDIR}/site-defaults.cfg-orig > ${MOCKCFGDIR}/site-defaults.cfg

sudo mkdir -p ${MOCKBASEDIR} ${MOCKCACHEDIR}
sudo chgrp mock ${MOCKBASEDIR} ${MOCKCACHEDIR}
sudo chmod -R g+rws ${MOCKBASEDIR} ${MOCKCACHEDIR}

Je nutné importovat všechny GPG klíče do RPM:

sudo rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-*

Vytvoříme chroot stromy (možnosti lze vybrat podle konfiguračních souborů uložených v ${MOCKCFGDIR}). Varianta pro tvoru balíků založených na repozitářích Fedora:

mock -r fedora-17-i386 init
mock -r fedora-17-x86_64 init
mock -r fedora-18-i386 init
mock -r fedora-18-x86_64 init

Varianta pro tvoru balíků založených na repozitářích EPEL (včetně CentOS):

mock -r epel-5-i386 init
mock -r epel-5-x86_64 init
mock -r epel-6-i386 init
mock -r epel-6-x86_64 init

Pokud chceme námi vytvořené balíky využívat k tvorbě dalších, musíme naše repozitáře přidat také. Zkopírujeme konfiguraci z ${MOCKCFGDIR}/epel*, přidáme část s naším repozitářem a pak můžeme provést init.

mock -r mojefirma-epel-5-i386 init
mock -r mojefirma-epel-5-x86_64 init
mock -r mojefirma-epel-6-i386 init
mock -r mojefirma-epel-6-x86_64 init

Tvorba balíčku

Vytvoření zdrojového balíčku

POZOR! Kvůli novému RPM (F11 a výše, tj. CentOS 6 a výše) je potřeba src.rpm balíčky pro EPEL a CentOS vytvářet pomocí upraveného skriptu rpmbuild-md5, jinak selže rozbalování rpm v chroot buildu. Balíčků pro F11 a výše se toto upozornění netýká.

Musíme nastavit pracovní adresář:

cd ~/rpmbuild/SPECS

Sehnat zdrojové soubory pro daný SW a vytvořit .spec soubor:

vim mod_auth_pgsql.spec

Zkusit přeložit balíček na vlastní platformě:

rpmbuild-md5 -ba mod_auth_pgsql.spec

Není nutné balíček cvičně překládat, stačí upravit .spec a vytvořit pouze src.rpm, např. takto (je vhodné i s parametrem --nodeps):

rpmbuild-md5 --nodeps -bs mod_auth_pgsql.spec

Překlad balíčku pro jedinou platformu

Toto je vhodné dělat pro úvodní překlad, kde se testují potřebné závisloti apod.

Příklad pro základní distribuci s balíčky EPEL

mock rebuild -r epel-5-i386 /usr/src/redhat/SRPMS/mod_auth_pgsql-2.0.3-2.f17.src.rpm

Překlad balíčku pro všechny vyjmenované platformy

překlad nějakého balíčku pro vyjmenované distribuce

srcrpmpath=~/rpmbuild/SRPMS/mod_auth_pgsql-2.0.3-2.f17.src.rpm
for dist in fedora-17-i386 fedora-17-x86_64 ; do
   mock rebuild -r $dist $srcrpmpath &
done

Překlad nějakého balíčku pro vyjmenované distribuce (použity balíčky z repozitáře EPEL):

srcrpmpath=~/rpmbuild/SRPMS/mod_auth_pgsql-2.0.3-2.f17.src.rpm
for dist in epel-5-i386 epel-5-x86_64 epel-6-i386 epel-6-x86_64 ; do
   mock rebuild -r $dist $srcrpmpath &
done

Překlad nějakého balíčku pro vyjmenované distribuce (použity balíčky z repozitářů EPEL i MOJEFIRMA):

srcrpmpath=~/rpmbuild/SRPMS/mod_auth_pgsql-2.0.3-2.f17.src.rpm
for dist in mojefirma-epel-5-i386 mojefirma-epel-5-x86_64 mojefirma-epel-6-i386 mojefirma-epel-6-x86_64 ; do
   mock rebuild -r $dist $srcrpmpath &
done

Sledování průběhu buildu

Je vhodné použít nástroj multitail:

sudo yum -y install multitail

Například:

multitail ${MOCKBASEDIR}/epel-5-*/result/*.log
multitail ${MOCKBASEDIR}/epel-6-*/result/*.log

Aktualizace repozitáře

GPG podpis

Po úspěšném sestavení balíčku je nutné ho podepsat pomocí gpg. Aby se nepodepisovaly všechny již vytvořené balíky ale pouze ty vyžadované, zadáme si masku. Zde vypisuji všechny varianty, při práci si vyberu jeden řádek metodou 'copy/paste', podobně to bude uváděno i dále:

REPONAMEPATTERN="epel"
REPONAMEPATTERN="epel-5"
REPONAMEPATTERN="epel-6"
REPONAMEPATTERN="mojefirma-epel"
REPONAMEPATTERN="mojefirma-epel-5"
REPONAMEPATTERN="mojefirma-epel-6"

Podpis všech balíčků ručně. Zde je nutné heslovou frázi na výzvu napsat:

RPMS_TO_SIGN=$(find ${MOCKBASEDIR}/${REPONAMEPATTERN}*/result/ -name '*.rpm' | tr '\n' ' ' )
rpmsign --addsign $RPMS_TO_SIGN

Pro následující skript budeme muset doinstalovat expect:

sudo yum -y install expect

Abych nemusel "heslovou frázi" při podpisování balíčků stále psát, mám ji uloženou v proměnné. Její nastavení provedu takto (mezera na začátku řádku zajistí, že se to neobjeví v bash historii, viz man bash HISTCONTROL=ignoreboth):

 PASSPHRASE="nejake fakt dlouhe heslo"

Podpis všech balíčků skriptem:

RPMS_TO_SIGN=$(find ${MOCKBASEDIR}/${REPONAMEPATTERN}*/result/ -name '*.rpm' | tr '\n' ' ' )
export LANG=cs_CZ.UTF-8
if [ -n "$PASSPHRASE" ] ; then
/usr/bin/expect -f - << EOF
spawn rpmsign --addsign $RPMS_TO_SIGN
match_max 100000
fork
expect "Vložte heslovou frázi: "
send -- "${PASSPHRASE}\r"
expect eof
EOF
else
   echo "Set PASSPHRASE variable first"
fi

Přesun do lokálního repozitáře

Výsledek musíme přesunout na jiné místo, protože to bude při dalším překladu "vyčištěno":

unset REPONAMEPATTERN
REPONAMEPATTERN="fedora"
REPONAMEPATTERN="centos"
REPONAMEPATTERN="epel"
REPONAMEPATTERN="mojefirma-epel"

BUILDTYPE="os"
BUILDTYPE="updates"
BUILDTYPE="testing"

pushd ${MOCKBASEDIR}

for mockchroot in $(/bin/ls -d ${REPONAMEPATTERN}*) ; do
   if [ "${mockchroot%%-*}" == "fedora" ] ; then 
      repodist=fedora
   else
      repodist=centos
   fi
   distnr="$(echo $mockchroot | sed -e 's/.*-\([^-]\+\)-\([^-]\+\)$/\1/')"
   arch="$(echo $mockchroot | sed -e 's/.*-\([^-]\+\)-\([^-]\+\)$/\2/')"

   mv ${MOCKBASEDIR}/$(basename ${mockchroot})/result/*.src.rpm ${REPOBASEDIR}/${repodist}/${distnr}/${BUILDTYPE}/SRPMS
   mv ${MOCKBASEDIR}/$(basename ${mockchroot})/result/*.rpm ${REPOBASEDIR}/${repodist}/${distnr}/${BUILDTYPE}/${arch}/RPMS
done

popd

Tvorba repozitáře a přesun na FTP

Aktualizace údajů o balíčcích v repozitáři a přenos na FTP:

CentOS

(createrepo je nutné spouštět na CentOS/RHEL, jinak by to klienti nebyli schopni načíst.)

unset DRYRUN

DRYRUN="-n"

if [ -n "${REPOBASEDIR}" -a -n "${FTPSERVER}" -a -n "${FTPBASEDIR}" ] ; then
   rsync -av --delete -e "ssh -l root -p ${FTPSERVERSSH}" ${REPOBASEDIR}/centos/ ${FTPSERVER}:/${FTPBASEDIR}/centos/ \
   --exclude headers/ --exclude repodata/ --exclude /kickstarts/ \
   ${DRYRUN}
else
   echo "Nastav promenne: REPOBASEDIR, FTPSERVER a FTPBASEDIR"
fi

Tvorba souborů pro repozitáře (pouze právě kopírovaných):

ssh -l root -p ${FTPSERVERSSH} ${FTPSERVER} "
for distnr in 5 6 ; do
   for type in $BUILDTYPE ; do
      for arch in i386 x86_64 SRPMS ; do
         workingdir=${FTPBASEDIR}/centos/\${distnr}/\${type}/\${arch}
         if [ -d \${workingdir} ] ; then
            createrepo \${workingdir}
         fi
      done
   done
done
"

Tvorba souborů pro repozitáře (všech):

ssh -l root -p ${FTPSERVERSSH} ${FTPSERVER} "
for distnr in 5 6 ; do
   for type in os updates testing ; do
      for arch in i386 x86_64 SRPMS ; do
         workingdir=${FTPBASEDIR}/centos/\${distnr}/\${type}/\${arch}
         if [ -d \${workingdir} ] ; then
            createrepo \${workingdir}
         fi
      done
   done
done
"

Poslat na zrcadlená FTP:

ssh -l root -p ${FTPSERVERSSH} ${FTPSERVER} "for FTPSERVERMIRROR in ${FTPSERVERMIRRORS} ; do /usr/bin/rsync -av -e 'ssh -p ${FTPSERVERSSH}' --delete ${FTPBASEDIR}/ \${FTPSERVERMIRROR}:${FTPBASEDIR}/ ; done"

Fedora

Tvorba souborů pro repozitáře (pouze právě kopírovaných):

for distnr in 17 18 ; do
   for type in $BUILDTYPE ; do
      for arch in i386 x86_64 SRPMS ; do
         workingdir=${REPOBASEDIR}/fedora/${distnr}/${type}/${arch}
         if [ -d ${workingdir} ] ; then
            createrepo ${workingdir}
         fi
      done
   done
done

Tvorba souborů pro repozitáře (všech):

for distnr in 17 18 ; do
   for type in os updates testing ; do
      for arch in i386 x86_64 SRPMS ; do
         workingdir=${REPOBASEDIR}/fedora/${distnr}/${type}/${arch}
         if [ -d ${workingdir} ] ; then
            createrepo ${workingdir}
         fi
      done
   done
done

Poslat na zrcadlené FTP:

unset DRYRUN
DRYRUN="-n"

if [ -n "${REPOBASEDIR}" -a -n "${FTPSERVER}" -a -n "${FTPBASEDIR}" ] ; then
   rsync -av --delete -e "ssh -l root -p ${FTPSERVERSSH}" ${REPOBASEDIR}/fedora/ ${FTPSERVER}:/${FTPBASEDIR}/fedora/ \
   ${DRYRUN}
else
   echo "Nastav promenne: REPOBASEDIR, FTPSERVER a FTPBASEDIR"
fi

Další užitečné příkazy

Vypsání všech balíčků v repozitářích:

for dist in centos ; do
   for distnr in 5 6 ; do
      for buildtype in os updates testing ; do
         echo -e "\nREPO ${REPOBASEDIR}/${dist}/${distnr}/${buildtype}/SRPMS: "
         ls ${REPOBASEDIR}/${dist}/${distnr}/${buildtype}/SRPMS
         for arch in i386 x86_64 ; do
            echo -e "\nREPO ${REPOBASEDIR}/${dist}/${distnr}/${buildtype}/${arch}/RPMS: "
            ls ${REPOBASEDIR}/${dist}/${distnr}/${buildtype}/${arch}/RPMS 
         done
      done
   done
done

Podepsání všech balíčků v repozitářích:

find ${REPOBASEDIR}/centos/ -name '*.rpm' -print0 | xargs -0 rpm --resign

Ukázka práce s GPG podpisy

Vypsání seznamu importovaných GPG klíčů:

rpm -qa gpg-pubkey*

Vypsání podrobnějšího přehledu o importovaných GPG klíčích:

for gpg in $(rpm -qa gpg-pubkey*) ; do rpm -qi $gpg ; done | grep '^Version \|^Summary '

Vypsání podrobností o všech importovaných GPG klíčích:

for gpg in $(rpm -qa gpg-pubkey*) ; do rpm -qi $gpg ; done | less

Ladění při překladu problematického balíčku

Pokud se překlad nedaří, je nutné ho nějak ladit. K tomu lze s výhodou využít mock s volbou --shell.

Vytvoření src.rpm (varianta rpmbuild-md5 je nutná pro EPEL5):

cd ~/rpmbuild/SPECS
rpmbuild-md5 -bs cherokee.spec

Ruční překlad pomocí prostředí mock (Fedora)

Smazání prostředí mock:

mock --root=fedora-17-x86_64 --clean

Inicializace prostředí mock:

mock --root=fedora-17-x86_64 --init

Instalace závislostí balíku:

mock --root=fedora-17-x86_64 --installdeps /home/$MYMOCKUSER/rpmbuild/SRPMS/cherokee-0.99.43-1.fc17.src.rpm

Dodatečná instalace vhodných balíků:

mock --root=fedora-17-x86_64 --install vim-enhanced mc

Kopírování src.rpm:

mock --root=fedora-17-x86_64 --copyin /home/$MYMOCKUSER/rpmbuild/SRPMS/cherokee-0.99.43-1.fc17.src.rpm /builddir/build/SRPMS/

Skok do shellu:

mock --root=fedora-17-x86_64 --shell
rpm -ivh /builddir/build/SRPMS/cherokee-0.99.43-1.fc17.src.rpm
rpmbuild --nodeps -ba /builddir/build/SPECS/cherokee.spec

Editace .spec:

vim /builddir/build/SPECS/cherokee.spec

Příklad ručního překladu pomocí prostředí mock (CentOS/EPEL)

Vše najednou:

rpmbuild-md5 -bs gdal-1.7.1.spec

mock --root=mojefirma-epel-5-x86_64 --clean
mock --root=mojefirma-epel-5-x86_64 --init
mock --root=mojefirma-epel-5-x86_64 --installdeps /home/$MYMOCKUSER/rpmbuild/SRPMS/gdal-1.7.1-1.fc17.src.rpm
mock --root=mojefirma-epel-5-x86_64 --install vim mc
mock --root=mojefirma-epel-5-x86_64 --copyin /home/$MYMOCKUSER/rpmbuild/SRPMS/gdal-1.7.1-1.fc17.src.rpm /builddir/build/SRPMS/
mock --root=mojefirma-epel-5-x86_64 --shell
rpm -ivh /builddir/build/SRPMS/gdal-1.7.1-1.fc17.src.rpm
rpmbuild -ba --nodeps /builddir/build/SPECS/gdal-1.7.1.spec

mock --root=mojefirma-epel-5-i386 --clean
mock --root=mojefirma-epel-5-i386 --init
mock --root=mojefirma-epel-5-i386 --installdeps /home/$MYMOCKUSER/rpmbuild/SRPMS/gdal-1.7.1-1.fc17.src.rpm
mock --root=mojefirma-epel-5-i386 --install vim-common vim-enhanced mc
mock --root=mojefirma-epel-5-i386 --copyin /home/$MYMOCKUSER/rpmbuild/SRPMS/gdal-1.7.1-1.fc17.src.rpm /builddir/build/SRPMS/
mock --root=mojefirma-epel-5-i386 --shell
rpm -ivh /builddir/build/SRPMS/gdal-1.7.1-1.fc17.src.rpm
rpmbuild -ba --nodeps /builddir/build/SPECS/gdal-1.7.1.spec