Building Debian packages

Definiciones

  • Distribuible neutral: Archivo TAR que contienene los archivos instalables y los scripts de instalación independiente de la distribución GNU/Linux a usar.
  • WORKSHOP: Directorio de trabajo donde llevamos a cabo las actividades presentadas en este documento.

Herramientas

Las herramientas que usaremos para la creación y gestión de los paquetes Debian son:

  • dh-make
  • build-essentials
  • svn-buildpackage
  • lintian
  • linda

Primeros pasos: Creando un paquete Debian para un distribuible de terceros

Necesitamos el distribuible de de los ficheros a instalar. Dicho fichero debe de estar nambrado en sintaxis nombre_paquete-X.X.X.tgz (http://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Package). Por ejemplo:

pyhello-0.1.tgz

Es importante que dicho distribuible no incluya directorios .svn, .bzr o directorios del estilo.

Descomprimimos el directorio en un directorio del mismo nombre:

$ cd ${WORKSHOP}
$ tar -xzf pyhello-0.1.tgz pyhello-0.1

Entramos en el directorio

Definimos el nombre del mantenedor como variable de entorno ($DEBFULLNAME)

Ejecutamos dh_make como se indica:

$ cd pyhello-0.1/
pyhello-0.1$ export DEBFULLNAME=”Manolo Cabezabolo”
pyhello-0.1$ dh_make -f ../pyhello-0.1.tar.gz -e tom.poo@domain.es -c gpl

Type of package: single binary, multiple binary, library,
kernel module or cdbs? [s/m/l/k/b] s

Maintainer name : Manolo Cabezabolo
Email-Address : tom.poo@domain.es
Date : Mon, 5 Xan 2009 19:07:39 +0200
Package Name : pyhello
Version : 0.1
License : gpl
Type of Package : cdbs
Hit to confirm:

El tipo de paquete que nos interesa a nosotros, principalmente, es simple binary para la mayoría de los casos. este tipo de paquete genera un solo paquete Debian y se basa en la ejecución de algún script de instalación o algún archivo Makefile para lo propio.

De todos modos, cuando estemos en el maravilloso mundo Python, simpre pordremos usar el tipo de paquete cdbs y podremos usar las herramientas de easy-install (http://peak.telecommunity.com/DevCenter/EasyInstall)

Adaptamos los ficheros del directorio debian para que realice la instalación correcta. Esto se verá detalladamente en el siguiente apartado del documento

Generamos el paquete deb y demás:

$ cd ${WORKSHOP}/pyhello-0.1/
$ dpkg-buildpackage -rfakeroot

Esto genera todos los ficheros .dsc, .deb, …:

pyhello-0.1-1.all.deb: El paquete Debian binario (listo para instalar)
pyhello-0.1-1.diff.gz: Todos los cambios respecto al archivo orig
pyhello-0.1-1.dsc: Resumen del paquete con MD5 para el orig y el diff
pyhello-0.1-1_i386.changes: Los cambios que has hecho en esta versión
pyhello-0.1.orig.tar.gz: Lo que distribuye el autor del programa (upstream)

Limpieza de los archivos de cosntrucción del paquete:

$ cd ${WORKSHOP}/pyhello-0.1/
$ fakeroot debian/rules clean

Adaptando los ficheros del paquete Debian

Se debe de tener presente siempre la política de Debian:

Para más detalles se debe de ver http://www.debian.org/doc/debian-policy/ o la política de empaquetado de Debian.

Orden de ejecución de los scripts

  • Durante la primera instalación:
    • preinst del paquete nuevo: Se prepara el sistema para la instalación del los datos del nuevo paquete
    • postinst del paquete nuevo: Última los detalles de la instalación del paquete nuevo. Por ejemplo, permisos de usuario, enlaces simbólicos.
  • Durante una actualización:
    • prerm del paquete antiguo: Se debería de ejecutar los comandos pertinentes para dejar lista la eliminación de los datos (ficheros y paquetes del paquete)
    • preinst del paquete nuevo: Se prepara el sistema para la instalación del los datos del nuevo paquete
    • postrm del paquete antiguo: Última los detalles de la eliminación del paquete antiguo.
    • postinst del paquete nuevo: Última los detalles de la instalación del paquete nuevo. Por ejemplo, permisos de usuario, enlaces simbólicos.
  • Durante la eliminación
    • prerm del paquete antiguo: Se debería de ejecutar los comandos pertinentes para dejar lista la eliminación de los datos (ficheros y paquetes del paquete)
    • postrm del paquete antiguo: Última los detalles de la eliminación del paquete antiguo.
    • Finalmente, si se ejecuta un –purge, entonces se invoca el comando purge del script postrm.

Prerm

Este script puede ser llamado del siguiente modo:

remove upgrade
failed-upgrade
remove in-favour
deconfigure in-favour
removing

A continuación se muestra un ejemplo de este fichero para el caso de instalación de un paquete.

#!/bin/sh
# prerm script for $$$COMPONENT_NAME$$$
#
# see: dh_installdeb(1)

set -e

# summary of how this script can be called:
# *
`remove’
# *
`upgrade’
# * `failed-upgrade’
# * `remove’ `in-favour’

# * `deconfigure’ `in-favour’
#
`removing’
#
# for details, see http://www.debian.org/doc/debian-policy/ or
# the debian-policy package

COMPONENT_NAME=”$$$COMPONENT_NAME$$$”

# echo “DEBUG: PRERM script”

case “$1” in
remove|upgrade|deconfigure)

if [ -e “/etc/init.d/$COMPONENT_NAME” ]
then
echo “INFO: Stopping $COMPONENT_NAME service.”
# Stop component
/etc/init.d/$COMPONENT_NAME stop
else
echo “INFO: Not SystemV init.d file found”
fi
;;

failed-upgrade)
;;

*)
echo “prerm called with unknown argument \`$1′” >&2
exit 1
;;
esac

# dh_installdeb will replace this with shell code automatically
# generated by other debhelper scripts.

#DEBHELPER#

exit 0

Preinst

Este script puede ser llamado del siguiente modo:

install
install
upgrade
abort-upgrade

A continuación se muestra un ejemplo de este fichero para el caso de instalación de un paquete. Su uso es recomendable pero no obligado:

#!/bin/sh
# preinst script for $$$COMPONENT_NAME$$$
#
# see: dh_installdeb(1)

set -e

# summary of how this script can be called:
# * `install’
# * `install’
# * `upgrade’
# *
`abort-upgrade’
# for details, see http://www.debian.org/doc/debian-policy/ or
# the debian-policy package

COMPONENT_NAME=”$$$COMPONENT_NAME$$$”
INSTALL_PATH=”/usr/local/$COMPONENT_NAME”
DATA_PATH=”/var/lib/$COMPONENT_NAME”
ETC_PATH=”/etc/$COMPONENT_NAME”
BACKUP_PATH=”/var/backups/$COMPONENT_NAME”

DATA=”lib”
ETC=”etc”

back_up () {
echo “INFO: Backing up the old file/dir: $1″
echo ” Back-up destination: $2″
if [ -e “$2” ]; then
/bin/rm -rf “$2”
fi
if [ -e $1 ]; then
/bin/cp -ra $1 “$2”
else
echo “WARNING: Not file/dir found for back-up”
fi
}

#echo “DEBUG: PREINST script.”

case “$1” in
install|upgrade)

if [ -e “/etc/init.d/$COMPONENT_NAME” ]
then
echo “INFO: Stopping $COMPONENT_NAME service”
/etc/init.d/$COMPONENT_NAME stop

VERSION=`/bin/cat /etc/init.d/$COMPONENT_NAME | /bin/grep REL_VERSION= | /usr/bin/cut -d’=’ -f 2 | /usr/bin/cut -d'”‘ -f 2`
echo “INFO: Old version detected: $VERSION”

# Make $BACKUP_PATH if dont exist
! [ -d $BACKUP_PATH ] && mkdir $BACKUP_PATH

echo “INFO: Backing $INSTALL_PATH/releases/$VERSION/$COMPONENT_NAME.config”
echo ” in $BACKUP_PATH/$COMPONENT_NAME.config”
back_up “$INSTALL_PATH/releases/$VERSION/$COMPONENT_NAME.config” “$BACKUP_PATH/$COMPONENT_NAME.config”

back_up “$INSTALL_PATH/releases” “$BACKUP_PATH/releases”
# XXX: Hack for avoid questions of the Lambdastream installer
if [ -d $INSTALL_PATH/releases ]; then
rm -rf “$INSTALL_PATH/releases”
fi

back_up “$ETC_PATH” “$BACKUP_PATH/$ETC”
back_up “$DATA_PATH” “$BACKUP_PATH/$DATA”

else
echo “INFO: No previous installation in this system”
fi

;;

abort-upgrade)
;;

*)
echo “preinst called with unknown argument \`$1′” >&2
exit 1
;;
esac

# dh_installdeb will replace this with shell code automatically
# generated by other debhelper scripts.

#DEBHELPER#

exit 0

Postrm

Este script puede ser llamado del siguiente modo:

remove purge upgrade
failed-upgrade
abort-install
abort-install
abort-upgrade
disappear

A continuación se muestra un ejemplo de este fichero para el caso de instalación de un paquete. Su uso es recomendable pero no obligado:

#!/bin/sh
# postrm script for $$$COMPONENT_NAME$$$
#
# see: dh_installdeb(1)

set -e

# summary of how this script can be called:
# *
`remove’
# *
`purge’
# *
`upgrade’
# * `failed-upgrade’
# * `abort-install’
# * `abort-install’
# * `abort-upgrade’
# * `disappear’
#
# for details, see http://www.debian.org/doc/debian-policy/ or
# the debian-policy package

COMPONENT_NAME=”$$$COMPONENT_NAME$$$”
BACKUP_PATH=”/var/backups/$COMPONENT_NAME”

delete_dir ()
{
if [ -d $1 ]; then
rm -rf $1
fi
}

# echo “DEBUG: POSTRM script”

case “$1” in

purge)

if [ -f /etc/init.d/$COMPONENT_NAME ]; then
update-rc.d -f $COMPONENT_NAME remove
rm /etc/init.d/$COMPONENT_NAME
fi

delete_dir /usr/local/$COMPONENT_NAME
delete_dir /etc/$COMPONENT_NAME
delete_dir /var/lib/$COMPONENT_NAME
delete_dir /var/log/$COMPONENT_NAME
delete_dir /var/run/$COMPONENT_NAME

delete_dir $BACKUP_PATH

deluser $COMPONENT_NAME
;;

remove|upgrade)

if [ -f /etc/init.d/$COMPONENT_NAME ]; then
update-rc.d -f $COMPONENT_NAME remove
fi

if [ -d /usr/local/$COMPONENT_NAME ]; then
for d in `ls /usr/local/$COMPONENT_NAME`
do
if [ $d != “releases” ]
then
echo “INFO: Removing /usr/local/$COMPONENT_NAME/$d”
rm -rf /usr/local/$COMPONENT_NAME/$d
else
echo “INFO: Skip remove action: /usr/local/$COMPONENT_NAME/$d”
fi
done
fi

;;

failed-upgrade|abort-install|abort-upgrade|disappear)
echo “Select (failed-upgrade|abort-install|abort-upgrade|disappear) in postrm script” >&2
;;

*)
echo “postrm called with unknown argument \`$1′” >&2
exit 1
;;
esac

# dh_installdeb will replace this with shell code automatically
# generated by other debhelper scripts.

#DEBHELPER#

exit 0

Postinst

Este script puede ser llamado del siguiente modo:

configure abort-upgrade
abort-remove in-favour
abort-remove
abort-deconfigure in-favour removing

A continuación se muestra un ejemplo de este fichero para el caso de instalación de un paquete. Su uso es recomendable pero no obligado:

#!/bin/sh
# postinst script for $$$COMPONENT_NAME$$$
#
# see: dh_installdeb(1)

set -e

# summary of how this script can be called:
# *
`configure’
# *
`abort-upgrade’
# * `abort-remove’ `in-favour’
#
# *
`abort-remove’
# * `abort-deconfigure’ `in-favour’
# `removing’
#
# for details, see http://www.debian.org/doc/debian-policy/ or
# the debian-policy package

COMPONENT_NAME=”$$$COMPONENT_NAME$$$”
INSTALL_PATH=”/usr/local/$COMPONENT_NAME”
DATA_PATH=”/var/lib”
ETC_PATH=”/etc”
BACKUP_PATH=”/var/backups/$COMPONENT_NAME”
DEB_BKP_CONFIG_FILE=”$BACKUP_PATH/$COMPONENT_NAME.config”

# echo “DEBUG: POSTINST script”

case “$1” in
configure)
echo “INFO: Running LambdaStream binary installer (.bin)”
/bin/sh /usr/share/$$$COMPONENT_NAME$$$/$$$COMPONENT_NAME$$$-$$$INSTALLER_VERSION$$$_installer.bin

VERSION=`/bin/cat /etc/init.d/$$$COMPONENT_NAME$$$ | /bin/grep REL_VERSION= | /usr/bin/cut -d’=’ -f 2 | /usr/bin/cut -d'”‘ -f 2`
echo “INFO: LambdaStream component Version: $VERSION”

if [ -e “$DEB_BKP_CONFIG_FILE” ]; then
echo “INFO: Restoring the previous configuration file”
# Installer config file preserved
/bin/mv “$INSTALL_PATH/releases/$VERSION/$COMPONENT_NAME.config” \
“$INSTALL_PATH/releases/$VERSION/$COMPONENT_NAME.config.deb.orig”

# Old configuration file restored
/bin/mv “$DEB_BKP_CONFIG_FILE” \
“$INSTALL_PATH/releases/$VERSION/$COMPONENT_NAME.config”
else
echo “INFO: Previous configuration file not found”
fi

echo “INFO: Starting $$$COMPONENT_NAME$$$ server”
/etc/init.d/$COMPONENT_NAME start
;;

abort-upgrade|abort-remove|abort-deconfigure)
;;

*)
echo “postinst called with unknown argument \`$1′” >&2
exit 1
;;
esac

# dh_installdeb will replace this with shell code automatically
# generated by other debhelper scripts.

#DEBHELPER#

exit 0

Verificación de la correcta construcción

Una vez creado un paquete se debe de comprobar que todo es correcto, es decir:

  • Se han metido las tareas de cron pertinentes
  • Se han metido las tareas propias de rotación de logs
  • Se han metido los scripts de arranque pertinentes
  • El paquete se instala correctamente
  • El servicio arranca normalmente
  • Crea los directorios en /etc
  • Crea los directorios en /var/backups
  • Crea los directorios en /var/lib
  • Crea los directorios en /var/logs
  • Crea los directorios en /var/run
  • Se han metido las tareas de cron pertinentes
  • Se han metido las tareas propias de rotación de logs
  • Se han metido los scripts de arranque pertinentes
  • El paquete se actualiza correctamente
  • El servicio arranca normalmente con la nueva versión del software
  • NO elimina ninguna configuración necesaria del paquete
  • NO elimina directorios creados en /etc
  • Actualiza directorios creados en /usr
  • NO elimina directorios creados en /var/backups
  • NO elimina directorios creados en /var/lib
  • NO elimina directorios creados en /var/logs
  • NO elimina directorios creados en /var/run
  • El paquete hace una copias de respaldo en /var/backups/<>:
  • Para datos persistentes
  • Para configuraciones de componente
  • El paquete se desinstala correctamente:
  • NO elimina ninguna configuración necesaria del paquete
  • NO elimina directorios creados en /etc
  • Elimina directorios creados en /usr
  • NO elimina directorios creados en /var/backups
  • NO elimina directorios creados en /var/lib
  • NO elimina directorios creados en /var/logs
  • NO elimina directorios creados en /var/run
  • El paquete se instala correctamente con una desinstalación previa sin purgado de datos:
  • El servicio arranca normalmente
  • Actualiza los directorios en /etc
  • Actualiza los directorios en /var/backups
  • Actualiza los directorios en /var/lib
  • Actualiza los directorios en /var/logs
  • Actualiza los directorios en /var/run
  • El paquete se desinstala totalmente cuando se ejecuta la opción de purge:
  • Elimina directorios creados en /etc
  • Elimina directorios creados en /usr
  • Elimina directorios creados en /var/backups
  • Elimina directorios creados en /var/lib
  • Elimina directorios creados en /var/logs
  • Elimina directorios creados en /var/run

A mayores, se pueden comprobar:

  • Se han metido páginas de documentación
  • Que tiene los ficheros necesarios y ninguno más (con mc o lesspipe)
  • Que cumple la policy de Debian (con lintian y linda)

Gestión de errores en el paquete

Como mantenedor del paquete serás responsable de solucionar los problemas que seguro tendrá el paquete. Estos problemas pueden ser errores de empaquetado, de aplicación de la política o también errores en la codificación del propio programa (que deberías notificar al autor).

En estos casos, debes solucionar el problema y crear una nueva revisión del paquete, para eso ejecuta:

pyhello-0.1$ dch -i

Después edita el debian/changelog y escribe una breve descripción de cómo has solucionado el error. Si se tratase de un paquete oficial, deberiamos utilizar el DBTS (Debian Bug Tracking System, en el que cada bug notificado tiene un número asociado. Cuando resuelve el problema debes citar ese número con algo como Closes: #5432. Todo esto, también está legislado (http://sdn.vlsm.org/share/Debian-Doc/debian-policy/ch-source.html#s-dpkgchangelog) en Debian.

Nueva versión del upstream

La otra tarea importante que realiza un mantenedor es empaquetar las nuevas versiones que produce el autor (lo que se llama upstream). En este caso se procede de modo similar a cuando se creó el paquete:

Se copia el .tgz de rigor en el directorio padre del directorio de los fuentes y se ejecuta:

pyhello-0.1$ uupdate -u pyhello-0.2.tar.gz

Como en el caso anterior, deberías editar el debian/changelog y explicar el cambio, normalmente algo del tipo New uptream release o similar.

Parcheos de fuentes originales

Se recomienda la lectura de las páginas de manual de estas herramientas:

  • dbs
  • dpatch

Gestión del paquete Deb dentro de un repositorio SVN

Inicialización del repositorio de Subversion

Inicialización del repositorio de forma manual
Ejecutar las siguientes instrucciones:

REPO_SERVER=file:///server/repo
REPO_LOCAL=/local/svn
DEBIAN_DIR=~/debian/packaging/filepp//debian
PKG=filepp

svn co ${REPO_SERVER} ${REPO_LOCAL}
mkdir ${REPO_LOCAL}/${PKG}/
cd ${REPO_LOCAL}/${PKG}/
mkdir trunk tags tarballs
cd trunk
cp -a ${DEBIAN_DIR} .
svn add ../../${PKG}
svn propset mergeWithUpstream 1 debian
svn commit

En el directorio base de los repositorios debe de quedar la siguiente estructura:

build-area
tags
X.X-X
debian
tarballs
paquete_X.X.orig.tar.gz
trunk
debian

Sólo se añanden al repositorio SVN los directorios tags y trunk. No los directorio build-area y tarballs.

Inicialización del repositorio usando SVNbuildpackage

svn-buildpackage tiene dos modos de uso:

Manteniendo en el repo también los ficheros del upstream (el software que estás empaquetando). Es la opción por defecto y es lo recomendable cuando es un paquete Debian nativo, es decir, el mantenedor es el autor del paquete y sólo se distribuye en Debian.
Manteniendo sólo los ficheros de gestión del paquete. Se puede activar con la opción -o de svn-inject o con una propiedad subversion que se llama mergeWithStream. Esto es lo habitual y es lo que hemos hecho en el paso anterior. En este modo es necesario poner los ficheros .tgz del upstream en un directorio llamado tarballs.
En nuestro caso nos interesa no controlar los ficheros instalables por nuestro paquete, por lo que ejecutamos la siguiente línea:

$ svn-inject -o -c 2 ~/local/pyhello_0.1-1.dsc \
https://repository.lambdastream.com/svn/debian-packages/lambda-components/

La opción -c 2 autoconfirma los cambios.

El resultado es que se autogenera toda la estructura de directorios de control del paquete:

psaavedra@ideafix (office) ~/local/svn/debian-packages $ svn up
A externals/pyhello
A externals/pyhello/trunk
A externals/pyhello/trunk/debian
A externals/pyhello/trunk/debian/init.d.lsb.ex
A externals/pyhello/trunk/debian/menu.ex
A externals/pyhello/trunk/debian/control
A externals/pyhello/trunk/debian/emacsen-remove.ex
A externals/pyhello/trunk/debian/watch.ex
A externals/pyhello/trunk/debian/manpage.xml.ex
A externals/pyhello/trunk/debian/manpage.1.ex
A externals/pyhello/trunk/debian/compat
A externals/pyhello/trunk/debian/manpage.sgml.ex
A externals/pyhello/trunk/debian/emacsen-startup.ex
A externals/pyhello/trunk/debian/changelog
A externals/pyhello/trunk/debian/docs
A externals/pyhello/trunk/debian/emacsen-install.ex
A externals/pyhello/trunk/debian/rules
A externals/pyhello/trunk/debian/cron.d.ex
A externals/pyhello/trunk/debian/postinst.ex
A externals/pyhello/trunk/debian/postrm.ex
A externals/pyhello/trunk/debian/preinst.ex
A externals/pyhello/trunk/debian/init.d.ex
A externals/pyhello/trunk/debian/prerm.ex
A externals/pyhello/trunk/debian/dirs
A externals/pyhello/trunk/debian/pyhello.doc-base.EX
A externals/pyhello/trunk/debian/copyright
A externals/pyhello/trunk/debian/pyhello.default.ex
A externals/pyhello/trunk/debian/README.Debian
A externals/pyhello/tags
Actualizado a la revisión 8.

Con esto, se espera ahora que exista un directorio llamado tarballs dentro del directorio pyhello del paquete. Dentro de este directorio deben de ponerse los distribuibles neutrales del paquete

La siguiente instrucción genera el distribuible correspondiente. Dicha instrucción debe de ser invocada desde el propio directorio tag/trunk/ del paquete:

svn-buildpackage -us -uc -rfakeroot

Cerrar un bug

Si arreglas algún problema en el paquete (sin que haya salido una nueva versión del upstream) tendrás que regenerar el paquete para su distribución. Este nuevo paquete debe tener un número de versión posterior, en concreto suele cambiar el número que va después del guión. Para crear una nueva minor-release después de arreglar los problemas, ejecuta:

~/prj/pyhello/trunk$ svn-buildpackage –svn-tag-only

Esto ha creado una nueva entrada en el debian/changelog, que debes editar convenientemente. Después, puedes compilar la nueva versión del paquete del mismo modo que has hecho antes, que ahora queda en ~/prj/pyhello/build-area/pyhello_0.1-2_i386.deb

Y cuando todo sea correcto, sube los cambios al repo con:

~/prj/pyhello$ svn ci

Ahora se tiene un nuevo directorio en pyhello/tags/0.1-1 que representa la versión anterior del paquete.

Este proceso es el equivalente al dch -i cuando no se usa svn-buildpackage.

Nueva versión del upstream

Cuando el autor produce una nueva versión de su programa, como mantenedores del paquete debemos empaquetarla. Para ello, descargar el .tar.gz de la web o repositorio del autor y ejecuta el siguiente comando:

~/prj/pyhello/trunk$ svn-upgrade /tmp/pyhello-0.2.tar.gz

Después, realiza los mismos pasos que en la sección anterior para cerrar un bug.

Este proceso es el equivalente al uupdate -u en la forma manual.

Gestión de repositorios Debian con RepRepro

reprepro -b /var/www/debian-packages/debian/ list lambda
reprepro -b /var/www/debian-packages/debian/ list dapper gnome-hearts
reprepro -b /var/www/debian-packages/debian/ includedeb dapper ~/gnome-hearts_0.1.2-1_amd64.deb
reprepro -b /var/www/debian-packages/dummy/ remove lambda adduser

Usando dput para la subida de paquetes

psaavedra@ideafix (office) ~ $ cat /home/psaavedra/.dput.cf
[dp]
fqdn = archive.dp.com
incoming = /var/lib/debarchiver/incoming/
login = psaavedra
method = scp

URLs de referencia

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s