Automate Trac instance deployment with Buildout

Recently, I started to contribute to pbp.recipe.trac, a Buildout recipe aimed to simplify the management and configuration of Trac instances.

I’ve taken interest in this piece of code the day I realized the Trac instance we used at work was still running on the old 0.10.x series. Even if we spend the majority of our time there, nobody has taken care of our little Trac: it was not updated for 3 years. If you add to this a sudden need for multi-repository support (as our team is adopting other internal projects), you have enough incentives to upgrade our Trac and automate its maintenance.

So here is how I migrated our legacy Trac 0.10 instance to a brand new 0.12 thanks to Buildout and pbp.recipe.trac.

First, let’s install all system dependencies using your distribution package management tool. My target server is running an RHEL 5.4, so I’ll invoke Yum:

$ sudo yum install subversion subversion-python sqlite-devel cyrus-sasl-lib cyrus-sasl-md5 mercurial

On Debian/Ubuntu, equivalent packages should be installed with apt-get:

$ sudo apt-get install subversion python-subversion libsqlite-dev cyrus-sasl-lib cyrus-sasl-md5 mercurial

Now we create an empty structure that will host our Trac instance:

$ mkdir ~/trac-home
$ cd ~/trac-home
$ touch ./buildout.cfg

It’s time to edit the file at the core of the process: buildout.cfg. Here is my version:

[buildout]
extensions = buildout.bootstrap
parts = my-trac
deploy-server = trac.example.net

[my-trac]
recipe = pbp.recipe.trac
project-name = My Trac instance
project-description = This is my stand-alone Trac instance hosting my devlopment activities.
project-url = http://${buildout:deploy-server}:8000/my-trac
repos = my-repo-1 | svn | ${buildout:directory}/repos/my-repo-1 | svn://${buildout:deploy-server}:3690/my-repo-1
        my-repo-2 | svn | ${buildout:directory}/repos/my-repo-2 | svn://${buildout:deploy-server}:3690/my-repo-2
        my-repo-3 | svn | ${buildout:directory}/repos/my-repo-3 | svn://${buildout:deploy-server}:3690/my-repo-3
default-repo = my-repo-1
force-instance-upgrade = True
force-repos-resync = True
wiki-doc-upgrade = True
stats-plugin = enabled
permissions = anonymous | STATS_VIEW
header-logo = ${buildout:directory}/my_trac_logo.png
smtp-enabled = true
smtp-server = localhost
smtp-port = 25
smtp-from = trac@example.net
smtp-replyto = no-reply@example.net
smtp-always-cc = kevin@example.net bob@example.net
additional-menu-items = Buildbot | http://${buildout:deploy-server}:9080/console
trac-ini-additional = attachment   | max_size               | 26214400
                      browser      | downloadable_paths     | /*/trunk, /*/branches/*, /*/tags/*
                      notification | always_notify_owner    | true
                      notification | always_notify_reporter | true
                      timeline     | ticket_show_details    | true
                      wiki         | ignore_missing_pages   | true
                      svn          | branches               | /*/trunk, /*/branches/*
                      svn          | tags                   | /*/tags/*

I now encourage you to use my buildout.cfg above as a template and customize it to your needs. Please read pbp.recipe.trac documentation carefully to set the recipe options to values you like.

Before going further, we need a bootstrap.py script. This script will take care of all stuff required by a bare Python interpreter to handle a Buildout project from scratch. Let’s download the latest version:

$ wget http://svn.zope.org/repos/main/zc.buildout/trunk/bootstrap/bootstrap.py

Now we can initialize our Buildout environment. The --distribute option here is necessary to get something more modern than the abandoned setuptools:

$ python ./bootstrap.py --distribute

And then we can ask Buildout to construct our the instance:

$ ./bin/buildout

Now that we have an empty Trac 0.12 instance, we will migrate there our legacy Subversion repositories:

$ svnadmin create ./repos/my-repo-1
$ svnadmin create ./repos/my-repo-2
$ svnadmin create ./repos/my-repo-3
$ ssh -C root@legacy.example.net "svnadmin dump /software/svn/repo1" | svnadmin load ./repos/my-repo-1
$ ssh -C root@legacy.example.net "svnadmin dump /software/svn/repo2" | svnadmin load ./repos/my-repo-2
$ svnadmin load ./repos/my-repo-3 < ~/svn_repo3_20100612.dmp

Note that in this case my first two subversion repositories are still running on my legacy server, and I already have a local dump of the third.

Let’s copy the data from our legacy Trac instance. By studying the differences between a default Trac instance and the legacy one I was working on, I came to the conclusion that I only needed to move attachments and the main database. Of course this is my personal case and your’s may be a little bit different:

$ scp -rC root@legacy.example.net:/software/trac/project/attachments ./parts/my-trac/
$ scp -rC root@legacy.example.net:/software/trac/project/db/trac.db  ./parts/my-trac/db/

We need to call Buildout a second time to update our the project with all the data we’ve just migrated:

$ ./bin/buildout

Now we’ll activate and configure SASL-based authentication in all Subversion repositories:

$ sed -i 's/# use-sasl = true/use-sasl = true/' ./repos/my-repo-1/conf/svnserve.conf
$ sed -i 's/# use-sasl = true/use-sasl = true/' ./repos/my-repo-2/conf/svnserve.conf
$ sed -i 's/# use-sasl = true/use-sasl = true/' ./repos/my-repo-3/conf/svnserve.conf
$ sed -i 's/# realm = My First Repository/realm = svn/' ./repos/my-repo-1/conf/svnserve.conf
$ sed -i 's/# realm = My First Repository/realm = svn/' ./repos/my-repo-2/conf/svnserve.conf
$ sed -i 's/# realm = My First Repository/realm = svn/' ./repos/my-repo-3/conf/svnserve.conf

Create a password database with our users:

$ saslpasswd2 -f sasl.db -u svn kevin
$ saslpasswd2 -f sasl.db -u svn bob
$ ...

Setup SASL authentication on the system (please change the sasl.conf location below according your file structure):

$ touch ./sasl.conf
$ sudo ln -s /home/kevin/trac-home/sasl.conf /etc/sasl2/svn.conf

And put the following content in the sasl.conf file we just created above (don’t forget to update the sasl.db location):

pwcheck_method: auxprop
auxprop_plugin: sasldb
sasldb_path: /home/kevin/trac-home/sasl.db
mech_list: ANONYMOUS CRAM-MD5 DIGEST-MD5

It’s time to create and populate the password file used by Trac, with all the users we created 3 steps above:

$ touch ./htdigest
$ htdigest ./htdigest trac kevin
$ htdigest ./htdigest trac bob
$ ...

And now we can start the Subversion server in the background:

$ svnserve --daemon --listen-port 3690 --root ./repos/

Last step, we launch Trac’s standalone webserver:

$ ./bin/tracd --port 8000 --single-env --auth="*,htdigest,trac" ./parts/my-trac

You can now reach Trac from your browser, on the following URL:


http://trac.example.net:8000/my-trac

A final test consist in getting some code from Subversion:

$ svn co svn://trac.example.net:3690/my-repo-1

From now on, and that’s where the fun begins, each time a new Trac version is released on PyPi, I just have to:

  1. stop both Trac and Subversion standalone servers,
  2. run ./bin/buildout, and
  3. restart both Subversion and Trac servers.

That’s enough to upgrade my instance.

Now you can clearly see how it’s important to invest time in automation to save on maintenance costs and prevent code rotting… :)

Plone 3.2 (and Python 2.4) on Mac OS X Leopard

In this post I’ll show you how I installed Python 2.4 on Mac OS X Leopard and how, starting from a bare Python environnement, we can build a stand-alone Zope 2.10 instance with Plone 3.2 thanks to zc.buildout.

If your goal is to play with or evaluate Plone (or if you don’t know what zc.buildout is), then this article will lead you to some troubles and pain. The Plone community maintain a collection of out-of-the-box and ready-to-use installers for all major platforms. So before going further, I strongly advise you to use the official Plone installer for Mac OS X. This is much simpler and faster than the process described below.

Now that all Plone newcomers are redirected to the right place, I can start to talk about how to install Python 2.4 on Mac OS X. Why the 2.4 release ? That’s simple: Mac OS X 10.5 (Leopard) ships with Python 2.5, but Plone 3 requires Python 2.4.

To get Python 2.4 on your machine, you can install it from its source code. But this is too much work for me. There should be a way to do it easier and faster… And there is.

Browsing the net, I found the repository of the “fat python” project, were you can find a universal binary installer for Panther. I’ve just installed it on my brand new Mac OS X 10.5.7 and it seems to works perfectly:
python-2.4-shell-in-mac-os-x-leopard

Now that the most annoying part (to me) is done, we can install Plone via zc.buildout.

Before going further, you need to have a machine that is able to compile code, which mean Apple’s developer tools must be installed locally. These softwares are available for free on the second DVD that ships with every Mac OS X copy.

First we create our project directory, then we download, from its SVN repository, the bootstrap code of buildout:

$ mkdir -p ~/plone-vanilla
$ cd ~/plone-vanilla
$ curl http://svn.zope.org/*checkout*/zc.buildout/trunk/bootstrap/bootstrap.py --output ./bootstrap.py

Then we create our buildout config file and edit it:

$ touch ./buildout.cfg
$ vi ./buildout.cfg

The buildout.cfg file should contain the following directives, which tell buildout to install Plone 3.2.3, Zope 2.10.8 and all their dependencies:

[buildout]
find-links = http://dist.plone.org

http://download.zope.org/ppix

http://download.zope.org/distribution

http://effbot.org/downloads

http://dist.plone.org/release/3.2.3

extends    = http://dist.plone.org/release/3.2.3/versions.cfg
versions   = versions
parts      = zope-server
             zope-instance
eggs       = PIL
             Plone

[zope-server]
recipe               = plone.recipe.zope2install
url                  = http://www.zope.org/Products/Zope/2.10.8/Zope-2.10.8-final.tgz
fake-zope-eggs       = true
additional-fake-eggs = ZConfig
                       pytz

[zope-instance]
recipe           = plone.recipe.zope2instance
zope2-location   = ${zope-server:location}
user             = admin:admin
debug-mode       = on
verbose-security = on
eggs             = ${buildout:eggs}

Now let’s build our Plone and Zope environnement:

$ python2.4 ./bootstrap.py
$ ./bin/buildout

At the end, if your build process didn’t fail, you’ll be able to start your Zope server:

$ ./bin/zope-instance
program: /Users/kevin/plone-vanilla/parts/zope-instance/bin/runzope
daemon manager not running
zopectl> start
. daemon process started, pid=17585
zopectl> logtail
------
2009-07-20T20:42:26 INFO ZServer HTTP server started at Mon Jul 20 20:42:26 2009
	Hostname: 0.0.0.0
	Port: 8080
------
2009-07-20T20:42:35 INFO Marshall libxml2-python not available. Unable to register libxml2 based marshallers.
------
2009-07-20T20:42:59 INFO Zope Ready to handle requests

Then you can fire up Safari, go to http://localhost:8080/manage (default Zope config), and login as the admin user (password: admin):
safari-zope-login

Create a Plone site:
plone-site-creation

Check that your using the right version of Plone in the control panel:
plone-323-control-panel