Testing Debian Package Builds with Docker

Debian packaging is convoluted and easy to get wrong. When making changes to debian packaging, I find that I want to test new package builds, sometimes on multiple platforms, quickly on my mac.

Rather than cloning my changes to a bunch of physical hosts, I can use docker to quickly set up discardable containers that mimic the target build system.

Some .deb Basics

Build systems generate debian packages in myriad ways, but the basic build command is typically:

dpkg-buildpackage -us -uc

This does a a lot of things, but in a nutshell, it builds the debian package from scratch and deposits it in the project’s parent directory.

The resulting .deb file can then be inspected in a few ways:

In situ inspection of a .deb. This prints the files that will be installed on the host, but not the pre-and post-installation scripts that will be executed. Use dpkg-deb -c <deb>.

root@a3635d2302d2:/# dpkg-deb -c tcpdump_4.6.2-5+deb8u1_amd64.deb
drwxr-xr-x root/root         0 2015-08-02 19:33 ./
drwxr-xr-x root/root         0 2015-08-02 19:33 ./usr/
drwxr-xr-x root/root         0 2015-08-02 19:33 ./usr/share/
drwxr-xr-x root/root         0 2015-08-02 19:33 ./usr/share/doc/
drwxr-xr-x root/root         0 2015-08-02 19:33 ./usr/share/doc/tcpdump/
-rw-r--r-- root/root      4164 2014-07-02 22:12 ./usr/share/doc/tcpdump/README.md.gz
-rw-r--r-- root/root      7706 2015-08-02 19:32 ./usr/share/doc/tcpdump/changelog.Debian.gz
-rw-r--r-- root/root     17555 2014-09-03 01:25 ./usr/share/doc/tcpdump/changelog.gz
drwxr-xr-x root/root         0 2015-08-02 19:33 ./usr/share/doc/tcpdump/examples/
-rw-r--r-- root/root       567 2014-07-02 22:12 ./usr/share/doc/tcpdump/examples/stime.awk
-rw-r--r-- root/root      1598 2014-07-02 22:12 ./usr/share/doc/tcpdump/examples/send-ack.awk
-rw-r--r-- root/root      1428 2014-07-02 22:12 ./usr/share/doc/tcpdump/examples/packetdat.awk
-rw-r--r-- root/root       529 2014-07-02 22:12 ./usr/share/doc/tcpdump/examples/atime.awk
-rw-r--r-- root/root      6257 2015-05-23 16:22 ./usr/share/doc/tcpdump/copyright
drwxr-xr-x root/root         0 2015-08-02 19:33 ./usr/share/man/
drwxr-xr-x root/root         0 2015-08-02 19:33 ./usr/share/man/man8/
-rw-r--r-- root/root     21022 2015-08-02 19:33 ./usr/share/man/man8/tcpdump.8.gz
drwxr-xr-x root/root         0 2015-08-02 19:33 ./usr/sbin/
-rwxr-xr-x root/root   1056376 2015-08-02 19:33 ./usr/sbin/tcpdump

Extraction of .deb contents. This extracts the files that will be installed on the host. Again, this does not include the pre- and post-installation scripts. Use dpkg-deb -x <deb> <target>.

root@a3635d2302d2:/# dpkg-deb -x tcpdump_4.6.2-5+deb8u1_amd64.deb ~/tcpdump
root@a3635d2302d2:/# tree ~/tcpdump/
/root/tcpdump/
`-- usr
    |-- sbin
    |   `-- tcpdump
    `-- share
        |-- doc
        |   `-- tcpdump
        |       |-- README.md.gz
        |       |-- changelog.Debian.gz
        |       |-- changelog.gz
        |       |-- copyright
        |       `-- examples
        |           |-- atime.awk
        |           |-- packetdat.awk
        |           |-- send-ack.awk
        |           `-- stime.awk
        `-- man
            `-- man8
                `-- tcpdump.8.gz

8 directories, 10 files

Extraction of .deb control files, including pre- and post-installation scripts. Use dpkg-deb -e <deb> <target>.

root@a3635d2302d2:/# dpkg-deb -e python2.7_2.7.9-2+deb8u1_amd64.deb ~/controlfiles/
root@a3635d2302d2:/# tree ~/controlfiles/
/root/controlfiles/
|-- control
|-- md5sums
|-- postinst
|-- postrm
`-- prerm

0 directories, 5 files

Building a .deb with Docker

Let’s say I want to build a debian package on Debian Jessie:

# run from the project root, where debian/control is accessible
docker run --rm -v "$(pwd)":/pkg bunn/debbuild:jessie	    

This will spin up a Jessie container, install the build-deps listed in debian/control, and build the package. Afterwards, it will deposit the resulting .deb(s) in your working directory and destroy the build container.

Here’s a similar setup for Ubuntu Precise:

docker run --rm -v "$(pwd)":/pkg bunn/debbuild:precise

Speeding Things Up

Since the base debbuild image doesn’t know what build-deps you might have, you can create a new container that overlays your specific build-deps on top of these packages:

# Example Dockerfile to add systemd and golang build-deps
FROM bunn/debbuild:jessie

RUN apt-get install -y dh-systemd golang
ENV GOPATH /pkg

From here, you can docker build . and run the resulting image similarly, and it will run the same build script without needing to install the build-deps each time.

Dockerfiles

See https://github.com/mypetyak/debbuild for base dockerfiles and some layering examples.