Publishing SVN Backups as Trac Attachments

Fredrik Lundh | January 2008

We’re using customer-specific Trac and Subversion instances for some of our customers, which is working pretty well. Recently, one customer wanted to be able to download complete Subversion archives of their code from the site, at will. Since the customer was already downloading code releases via the Trac wiki, we decided to make the archives available through the Trac wiki as well. Here’s how we did it.

Trac stores attachments in a subdirectory, attachments/wiki in the Trac data directory. For example, on our server, attachments for the SvnBackup page in the customer.trac wiki would be stored in this directory:


Creating Archive Tarballs #

So, the first step is to create regular backups, and store them in that directory. Well, we’re already creating nightly backups using the script, so the first step is to tell the script to put those backups in the right directory:

$ mkdir webapps/customer.trac/attachments/wiki/SvnBackup

$ bin/ --archive-type=gz --num-backups=10 \
  webapps/customer.svn/ \

(the command should be written on a single line)

The above command tells the script to create a hotcopy of the webapps/customer.svn repository, run it through tar and gzip, and store the resulting tarball in the SvnBackup attachment directory (which has to exist). Up to ten tarballs are kept around.

Attaching the Tarballs to a Wiki Page #

Next, we need to tell Trac about the attachments. Just putting some files in the directory isn’t enough; we also need to update Trac’s database. Trac stores a list of attachments in the attachment table in a local sqlite3 database; to update the list, we just need to clean out any old attachments for the page, and add entries for the existing files.

This script ( does the trick:

# usage: <trac dir> <page>
#   e.g: webapps/customer.trac SvnBackup

import glob, sys
import sqlite3 as dbapi

from os.path import *

TRAC_ROOT, PAGE = sys.argv[1:3]

db = dbapi.connect(join(TRAC_ROOT, "db/trac.db"))

db.execute("delete from attachment where type = 'wiki' and id = ?", [PAGE])

for file in glob.glob(join(TRAC_ROOT, "attachments/wiki", PAGE, "*")):
    print os.path.basename(file)
        "insert into attachment"
        " (type, id, filename, size, time, description, author, ipnr)"
        " values ('wiki', ?, ?, ?, ?, '', 'system', '')",
        [PAGE, basename(file), getsize(file), getmtime(file)]


Running this script adds the files to the given page:

$ python webapps/customer.trac SvnBackup

This results in an attachment section that looks something like:

Attachments #

Wrapping Up #

The final step is to add the and commands to your standard backup cronjob, and tell the customer about it. My cron entry looks like this:

15 0 * * * /home/company/ >/home/company/backup.log 2>&1

and the script simply contains the and commands shown earlier (and lots of other stuff as well, but that’s outside the scope of this article).


A Django site. rendered by a django application. hosted by webfaction.