Poesía Binaria

Cómo empaquetar programas para Ubuntu o Debian y servirlos desde mi PPA de Launchpad

Hace unos meses vi una serie de posts de El Atareao donde hablaba del empaquetado de un programa para Ubuntu y subirlo a un PPA. Esto nos va a permitir distribuir nuestros programas para que puedan ser instalados fácilmente. Porque, para nosotros programadores, es muy fácil decir que descomprimamos un archivo y cada uno se lo compile en su ordenador. Pero, en realidad, poca gente se lo va a compilar y poca gente lo va a utilizar. Incluso otros programadores, antes de meterse a ver el código de tu programa, preferirían instalárselo en su ordenador y ver cómo funciona. Por otro lado, no todo el mundo tiene instaladas todas las utilidades para poder compilarlo, es más, en servidores, se recomienda, por seguridad, no tener entornos de compilación instalados.

Mención obligatoria

Antes de nada, recomiendo mirar los posts de El Atareao donde habla de estos temas:

En este post, repetiré algunos de los pasos que se mencionan en esos posts, aunque también haré algunas cosas de forma diferente para adaptar el proceso a mis necesidades.

Creando una clave para firmar paquetes

Por seguridad, todos los paquetes deberán ir firmados. Para ello, crearemos claves que garantizarán que el paquete es nuestro. Realizan una correspondencia entre el paquete y el creador. Además, ya que utilizaremos launchpad.net como plataforma para nuestros paquetes, en launchpad se verificará esta identidad, por lo que nadie podrá «hacerse pasar por nosotros» y publicar un paquete en nuestro nombre que no sea nuestro.

Para ello, en nuestro ordenador ejecutamos:

gpg --gen-key
gpg (GnuPG) 1.4.20; Copyright (C) 2015 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Seleccione el tipo de clave deseado:
(1) RSA y RSA (por defecto)
(2) DSA y ElGamal (por defecto)
(3) DSA (sólo firmar)
(4) RSA (sólo firmar)
¿Su elección?
1
las claves RSA pueden tener entre 1024 y 4096 bits de longitud.
¿De qué tamaño quiere la clave? (2048)
2048
El tamaño requerido es de 2048 bits
Especifique el período de validez de la clave.
0 = la clave nunca caduca
= la clave caduca en n días
w = la clave caduca en n semanas
m = la clave caduca en n meses
y = la clave caduca en n años
¿Validez de la clave (0)?
0
La clave nunca caduca
¿Es correcto? (s/n)
s
Necesita un identificador de usuario para identificar su clave. El programa
construye el identificador a partir del Nombre Real, Comentario y Dirección
de Correo electrónico de esta forma:
«Heinrich Heine (Der Dichter) »
Nombre y apellidos:
Gaspar Fernández
Dirección de correo electrónico:
micorreo@midominio.com
Comentario:
Sigueme en Twitter @gaspar_fm
Ha seleccionado este ID de usuario:
«Gaspar Fernández (Sigueme en Twitter @gaspar_fm) »
¿Cambia (N)ombre, (C)omentario, (D)irección o (V)ale/(S)alir?
v

A continuación, el sistema nos preguntará una contraseña dos veces, ya sea en modo texto o en modo gráfico. Tras eso, se generarán las claves RSA necesarias y se añadirá la clave a nuestro repositorio de claves PGP (aunque los programas de GNU para gestionar estas claves se llaman gpg)

Tras eso, podemos ver el listado de claves con la siguiente instrucción

gpg -K
sec   2048R/0333D788 2018-03-18
uid                  Gaspar Fernández (Sigueme en Twitter @gaspar_fm)
ssb   2048R/D59AA557 2018-03-18

Donde 033D788 es la ID de nuestra clave, y que debemos utilizar más adelante. También debemos enviar la clave al servidor de claves de Ubuntu. Esto lo podemos hacer así:

gpg --keyserver keyserver.ubuntu.com --send-keys 0333D788
gpg: enviando clave 0333D788 a hkp servidor keyserver.ubuntu.com

Ahora debemos subid la clave a Launchpad, para ello, dentro de nuestro panel de usuario, editamos OpenPGP keys:

Luego nos pedirá el fingerprint de la clave, que podemos obtener con:

gpg --fingerprint

Pegando el fingerprint de nuestra clave en la web obtendremos una confirmacion de este tipo:
.
Una vez hecho esto recibiremos un e-mail cifrado que debemos abrir con la URL para verificar nuestra clave.

Un programa en C

Como el principal uso que le quiero dar a la herramienta es la instalación de programas compilados, voy a empezar creando un programa en C para empezar desde un punto de vista práctico. Algo así como un hola mundo. De todas formas, veremos que no varía mucho la creación del repositorio para un programa sencillo que para un programa más complicado. Por otro lado, debo destacar la facilidad que nos da launchpad.net para crear nuestros repositorios y compilar los programas automáticamente para varias plataformas y versiones. Esto evita que tengamos que tener varias máquinas virtuales con varias versiones del sistema operativo en 32, 64bit y herramientas para poder generar código para ARM para compilar nuestros programas.

El programa será holadist.c:

1
2
3
4
5
6
7
8
9
#include <stdio.h>

#define VERSION "0.1.1"

int main(int argc, char* argv[])
{
        printf ("Hola Distribuible. Versión %s\n", VERSION);
        return 0;
}

Lo podremos compilar haciendo:

gcc -o holadist holadist.c

Como vemos, todo el tema de versión está dentro del mismo programa, aunque podríamos sacarlo fuera en un archivo .h para hacer más fáciles las modificaciones. Bueno, ahora vamos a crear un archivo Makefile para construir nuestro programa. El fichero es un poco general y lo he sacado de una plantilla que utilizo para varios proyectos pequeños. Si tienes un proyecto más grande, seguramente utilices autotools, Cmake, Scons u otra herramienta:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
### Makefile ---

## Author: Gaspar Fernández <blakeyed@totaki.com>
## Keywords:
## X-URL:

CC=gcc
CFLAGS=-O4
LIBS=

SOURCES=holadist.c
INCLUDES=
OBJECTS=$(SOURCES:.c=.o)

EXECUTABLE=holadist

all: $(SOURCES) $(EXECUTABLE)

$(EXECUTABLE): $(OBJECTS)
    $(CC) $(CFLAGS) $(INCLUDES) $(OBJECTS) $(LIBS) -o $@

clean:
    rm -rf $(OBJECTS)
    rm -rf $(EXECUTABLE)

### Makefile ends here

En este punto si hacemos:

make

Se creará el ejecutable.

En nuestro programa, será muy importante que tengamos en cuenta la versión que estamos empaquetando. Será útil cuando creemos actualizaciones del programa, para que los sistemas sepan que tienen la última y para solucionar muchos temas de dependencias. En nuestro caso, la versión es 0.1.1. Por lo tanto, nuestro programa, lo guardaremos en un directorio llamado holadist-0.1.1 (nombre_del_programa-versión). Y si no se te ocurre número para la versión siempre puedes utilizar la fecha en la que lanzaste la última actualización en formato YYYYMMDD o, lo que es lo mismo, los cuatro dígitos del año, seguidos de dos dígitos del mes y por último los dos dígitos del día. El formato es importante, ya que este número será mayor cuanto más en el futuro esté la fecha, así las herramientas de Debian no se harán un lío buscando versiones.

Configurar paquete

Para comenzar, como las herramientas serán en línea de comandos, vamos a establecer el valor de dos variables importantes, DEBEMAIL y DEBFULLNAME que corresponderán con nuestro correo electrónico y nuestro nombre, como desarrolladores o mantenedores del paquete. Podemos hacer lo siguiente:

export DEBEMAIL=»micorreo@midominio.com
export DEBFULLNAME=»Gaspar Fernandez»

Estas dos líneas también podemos ponerlas en nuestro archivo ~/.bashrc y se cargarán siempre que accedamos al sistema. Ahora, dentro del directorio holadist-0.1.1 ejecutamos loo siguiente:
dh_make -c mit --createorig
Type of package: (single, indep, library, python)
[s/i/l/p]?
s
Email-Address       : micorreo@midominio.com
License             : mit
Package Name        : holadist
Maintainer Name     : Gaspar Fernandez
Version             : 0.1.1
Package Type        : single
Date                : Sun, 18 Mar 2018 19:28:05 +0100
Are the details correct? [Y/n/q]
Y
Done. Please edit the files in the debian/ subdirectory now.

En el anterior comando, el parámetro -c mit indica la licencia con la que distribuimos nuestro programa, en este caso, licencia MIT y –createorig indica que se creará automáticamente un archivo comprimido con los archivos del paquete.
Esto ha creado un directorio llamado debian con muchos archivos dentro:

En principio, podemos eliminar todos los archivos que terminen en .ex, son ejemplos de acciones que podemos crear en la instalación de nuestro programa, como arrancarlo al inicio, tener página de manual, ejecutar algo tras la instalación, añadir una entrada en cron, etc:
rm debian/*.ex

Ahora vamos a centrarnos en los archivos que nos quedan, los editaremos y retocaremos para adaptarlos a nuestro proyecto.

changelog:

1
2
3
4
5
holadist (0.1.1-1) xenial; urgency=medium

  * Hola Mundo listo para distribuir

 -- Gaspar Fernandez <micorreo@midominio.com>  Sun, 18 Mar 2018 19:57:34 +0100

Nota: donde pongo xenial debemos colocar la distribución contra la que queremos construir los ejecutables. En mi caso uso Xenial al ser LTS. Pero podemos construir para zesty o con cualquier distribución de Ubuntu. Es posible que deseemos que nuestro paquete funcione también en distribuciones derivadas de Ubuntu o incluso Debian. Tenemos que tener en cuenta el software y las bibliotecas instaladas en cada sistema (yo recurro a máquinas virtuales para probar esto). Con derivados de Ubuntu puede ser fácil porque suelen utilizar las mismas versiones de las bibliotecas que Ubuntu, aunque con Debian podemos tener algún problema, eso lo debemos resolver experimentalmente, o leyendo la documentación de bibliotecas incluidas en la distribución.

control:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Source: holadist
Section: misc  
Priority: extra  
Maintainer: Gaspar Fernandez <micorreo@midominio.com>
Build-Depends: debhelper (>=9)
Standards-Version: 3.9.6
Homepage: https://poesiabinaria.net
#Vcs-Git: git://anonscm.debian.org/collab-maint/holadist.git
#Vcs-Browser: https://anonscm.debian.org/cgit/collab-maint/holadist.git

Package: holadist
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}
Description: Un paquete de ejemplo
 No es más que un pequeño ejemplo en C

copyright (se adjunta la licencia que hemos seleccionado, en mi caso, la MIT, aunque debemos rellenar algunas cosas que faltan):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: holadist
Source: <https://poesiabinaria.net>

Files: *
Copyright: 2018 Gaspar Fernández <micorreo@midominio.com>

License: MIT

Files: debian/*
Copyright: 2018 Gaspar Fernandez <micorreo@midominio.com>
License: MIT

License: MIT
 Permission is hereby granted, free of charge, to any person obtaining a
 copy of this software and associated documentation files (the "Software"),
 to deal in the Software without restriction, including without limitation
 the rights to use, copy, modify, merge, publish, distribute, sublicense,
 and/or sell copies of the Software, and to permit persons to whom the
 Software is furnished to do so, subject to the following conditions:
 .
 The above copyright notice and this permission notice shall be included
 in all copies or substantial portions of the Software.
 .
 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

# Please also look if there are files or directories which have a
# different copyright/license attached and list them here.
# Please avoid picking licenses with terms that are more restrictive than the
# packaged work, as it may make Debian's contributions unacceptable upstream.

Creando el paquete

En el mismo directorio en el que estamos, haremos:

debuild -S -sa -K033D788

Recordemos que 033D788 es la ID de nuestra clave PGP. Además, con -S creamos un paquete de código fuente.

Al ejecutar esta línea, en una parte del proceso nos preguntará la contraseña de nuestra firma (la que elegimos cuando creamos la clave PGP).

Como vemos, si listamos los archivos, tenemos varios archivos pertenecientes a nuestro proyecto:

-rw-r–r– 1 gaspy gaspy 2364 mar 18 21:26 holadist_0.1.1-1.debian.tar.xz
-rw-r–r– 1 gaspy gaspy 1339 mar 18 21:26 holadist_0.1.1-1.dsc
-rw-r–r– 1 gaspy gaspy 1726 mar 18 21:26 holadist_0.1.1-1_source.changes
-rw-r–r– 1 gaspy gaspy 2039 mar 18 21:26 holadist_0.1.1-1_source.build

Debemos tener presentes estos archivos de cara a subir el paquete a launchpad.net

Enviando nuestra clave a Launchpad

Esto sólo lo tendremos que hacer una vez
Finalmente, para subir nuestro paquete a launchpad, lo haremos con dput, de la siguiente manera:

dput ppa:gasparfm/sysadmins holadist_0.1.1-1_source.changes
Checking signature on .changes
gpg: Firmado el dom 18 mar 2018 21:26:36 CET usando clave RSA ID 0444C699
gpg: Firma correcta de «Gaspar Fernández (https://poesiabinaria.net) »
Good signature on /home/gaspy/Proyectos/git/build/holadist_0.1.1-1_source.changes.
Checking signature on .dsc
gpg: Firmado el dom 18 mar 2018 21:26:33 CET usando clave RSA ID 0444C699
gpg: Firma correcta de «Gaspar Fernández (https://poesiabinaria.net) »
Good signature on /home/gaspy/Proyectos/git/build/holadist_0.1.1-1.dsc.
Uploading to ppa (via ftp to ppa.launchpad.net):
Uploading holadist_0.1.1-1.dsc: done.
Uploading holadist_0.1.1.orig.tar.xz: done.
Uploading holadist_0.1.1-1.debian.tar.xz: done.
Uploading holadist_0.1.1-1_source.changes: done.
Successfully uploaded packages.

En cuestión de segundos deberíamos recibir un correo electrónico diciendo si el archivo a subir ha sido aceptado o no. Cuando se acepta la subida, empezará a compilar. Este proceso puede tardar varios minutos si se trata de un código sencillo. El programa de ejemplo ha tardado unos 10 minutos en compilarse. Tras eso, se marcan como pendientes de publicación, un proceso que puede tardar otros 10 minutos. Si no ha sido aceptado, siempre podemos corregir los errores que hayan sucedido (los recibimos por correo). Si hay errores al compilar, recibiremos un correo indicando que hay errores. Los errores los podemos ver en nuestro panel de launchpad, dentro del paquete y a su vez, dentro de la arquitectura para la que ha sido compilado (puede que haya fallado para compilar en 64bit, pero no en 32bit).

Si este proceso ha fallado, siempre podemos repetir el empaquetado con debuild y enviar con dput. Podemos utilizar dput -f si no cambiamos la versión de nuestro paquete para que nos permita subirlo de nuevo. Si hay errores al compilar, debemos primero eliminar el paquete en launchpad.net. Normalmente la compilación debemos hacerla primero en local, y asegurarnos de que todo funciona, aunque cuando llega a launchpad siempre puede fallar algún tema de dependencias o si estamos compilando para una versión de Ubuntu diferente a la nuestra.

Otro modo de hacer la instalación

Hasta ahora hemos hecho la instalación desde el fichero Makefile. Pero si no estamos utilizando Makefile para construir nuestro programa, queremos separar la instalación de archivos, o nuestro programa no requiere un Makefile, pueden ser scripts, iconos o cualquier cosa que queramos instalar. Debian, y todas las distribuciones con dpkg/apt controlan muy bien la instalación, y no nos tendremos que preocupar de implementar nada para las desinstalaciones, ya se encarga el sistema de todo.
La instalación podemos hacerla también desde el fichero debian/install.

El formato del archivo debian/install es muy sencillo. Simplemente tendremos que poner el archivo con la ruta relativa del archivo que queremos copiar a la ruta del sistema donde vamos a copiarlo, ya sea de forma absoluta o también relativa (El sistema nos pondrá el / delante). El ejemplo de debian/install en nuestro proyecto será el siguiente:

1
holadist usr/bin

Siento holadist el fichero de nuestro programa compilado y /usr/bin el directorio donde vamos a copiar dicho archivo.

Agilizando el proceso para actualizar versiones

Si cada vez que lanzamos una nueva versión tenemos que crear un nuevo paquete y personalizar los archivos del directorio debian sería un trabajo muy duro. En su lugar, vamos a ver el proceso que seguiríamos para hacer una actualización de nuestros paquetes.

Todo esto se puede automatizar, y podemos crear scripts que hagan nuestra vida, y la de nuestros programas mucho más fácil.

¿Empaquetas tu software para Debian o Ubuntu?

Ya no tenemos excusa para distribuir nuestros programas, al menos para Ubuntu. Aunque aún quedan algunos puntos por cubrir, podemos empezar con esto y empezar a distribuirlos. Es más, muy pronto, empezaré a soltar algunos de mis programas y utilidades de esta forma 🙂

Referencias

Además de visitar el blog El Atareao y los posts mencionados anteriormente. He sacado bastante información de otros sitios:

Foto principal: unsplash-logoKira auf der Heide

También podría interesarte....