Packaging Django Projects for Debian Using Autotools

Why Package?

I use Django to develop web sites. I develop and test the code on my machine, uploading it from time to time to the test area on the server for the customer to review, then upload the approved version to the main site. There is a number of ways to do that. The simplest one I used at the beginning was to upload the files to the server over ssh using mc. This method is time-consuming and error-prone since I had to take care of the following issues:

I deploy my sites on Debian GNU/Linux. Its packaging tools solve all of the above problems during package installation and upgrades. A package is a file containing a number of (usually somehow related) files to be installed on a system in one step. With the introduction of packaging, my work flow didn't change: it's still develop — review — deploy. After testing the site at home, I create the packages (using dpkg-buildpackage), transfer them to the server, and install them there (using dpkg).

Packaging does add another level of complexity, which one needs to understand and maintain. This can also be an advantage: For instance, I can quickly test a package on my home machine before uploading it to the review area; without packaging, it would not be a zero-cost operation, and I just wouldn't do that. Or, I can check out the project in another directory, try to build the package, and see whether I forgot to add any files to the version control (this can also be done automatically). This does sometimes happen in spite of heavy git-status usage (which is kind of mandatory for git index management, so I use it much more often (and read its output much more carefully :-) ) than I used to do with similar tools of other SCM systems).

In Debian, the main package building logic is contained in the debian/rules file. It is usually a makefile with one or more rules how to build the package (see Debian New Maintainers' Guide for more information). A minimal debian/rules file for a Django-based project could look like this:

#!/usr/bin/make -f

clean:
	# Delete debian/mysite
	# Delete debian/mysite-test

build:
	# Create .mo files

install: build
	# Copy .py files and templates to debian/mysite
	# Copy .py files and templates to debian/mysite-test

dpkg-buildpackage -uc -us -rfakeroot performs the following steps:

  1. fakeroot debian/rules clean

  2. Build the source package (a gzipped tar file with a description file).

  3. debian/rules build. We need this rule only because dpkg-buildpackage executes it.

  4. fakeroot debian/rules install

Why Autotools?

For some time after packaging ("debianizing") my Django projects, I performed all file selection and file moving operations in debian/rules. It worked well till I got bored reinventing the different deployment locations wheel: I've started implementing scripts that picked and installed files in two directories, updating the paths accordingly. This, and file generation, are the things that automake does very well, so I started using it.

automake's main file is Makefile.am; it specifies the targets to build and install in its own macro language. The software author runs automake, which generates the Makefile.in file, a makefile with some variables named like @var@. At the build time, the Debian package maintainer runs autoconf's configure script, which substitutes the variables with the values supplied or detected by configure (see autotools tutorial for more information).

In my projects, I'm the software author and the package maintainer. I've set up the things once and touch them quite rarely since then. dpkg-buildpackage runs configure, make, and make install for me every time I need to build a package.

On my home machine, I also run ./configure --datadir=`pwd` DEBUG=True DB_PASSWD=xyzzy whenever I check out a project into a new location (e.g., when I work on a feature and am not ready for a commit and want to commit an unrelated, logically complete change). Here, autoconf puts the password in settings.py, and automake generates a makefile that can be used to build .mo files, clean them, or install the Python files into a specified DESTDIR.

It would be possible to use a custom combination of cat and sed instead of autoconf. I use it since it is required by automake anyway, and I think that configure.ac and its template files will be more readable for an experienced developer as complexity grows. A disadvantage is that, e.g., Apache configuration files are not completely DRY and have a few repeating blocks. Perhaps they could be replaced with some nested macros; I haven't looked at that.

I need to change the autotools files whenever I add a new file to build or install, or when I add a new substitution variable.

Example

I've debianized an example from the Django tutorial. You can create Debian packages or run configure, make, and make install DESTDIR=/tmp/mysite with it.

The binary packages won't work out of the box. They include apache2 configuration for Debian; you'll need to a2ensite it and fix or remove the certificate and authentication bits. You'll also have to CREATE USER mysite PASSWORD 'xyzzy'; CREATE DATABASE mysite; GRANT ALL ON DATABASE mysite TO mysite; in su - postgres -c psql template1, assuming you're running PostgreSQL. It is possible to do that automatically after the package installation; I don't do that in my packages since I don't have to deploy them on many systems, and package upgrades often require schema changes anyway, and the effort to automate that is usually not worth it.

A tip if you use this approach: I run make-messages only after fakeroot debian/rules clean since otherwise the former scans also the Python sources in the debian/mysite and debian/mysite-test directories, which leads to spurious changes in the .po files.

Feel free to write me if you have corrections, comments or suggestions.