Integrating Trac and Gitolite: Round One
egj trac , gitolite , sysadmin
We're now going to set up the core integration between Trac and Gitolite, including:
- Gitolite post-receive hooks to notify Trac of new commits
- Trac components to update or close tickets when commit messages like "Fixes #1" are pushed to the git server
- Trac Repository Browser for git repositories
Getting Permissions Right
The most involved bit here is ensuring that the filesystem permissions are all correct, and remain correct as files are edited and created by the software over time. Trac and Git both need some ability to read files from one another's directories. After this, the rest of the integration is pretty straightforward. Here's an overview of what permissions are needed:
- For Trac to display files and changesets in its Repository Browser, the user running the Trac process needs read access to the objects in the relevant Git repositories.
- For Gitolite's post-receive hook to notify Trac of new commits, the user running the Gitolite process needs read access to the relevant Trac instance's conf/trac.ini file; read and write access to the Trac instance's database; and read and write access to the Trac instance's logs.
The strategy we'll use is to add both the "trac" and "git" system users to a shared group, and ensure that all the files they need to share are owned by that group and group-readable.
As root:
groupadd infra usermod -a -G infra trac usermod -a -G infra git
Now we need to fix up the permissions in the Trac instance, and ensure that they remain fixed up even if Trac edits its own files (e.g. when you use the Admin Panels to edit your trac.iniconfiguration) --
su - trac
chown -R trac:infra sites
chmod g+r sites/test/conf/trac.ini
chmod -R g+w sites/test/db/ sites/test/log/
find /home/trac/sites/ -type d -exec chmod +s {} \;
We also need to fix up the permissions for both existing and new Gitolite repositories:
su - git
chown git:infra -R /home/git/repositories/
chmod -R g+rX /home/git/repositories/
find /home/git/repositories/ -type d -exec chmod +s {} \;
echo "21c21
< UMASK => 0077,
---
> UMASK => 0027,
" > /tmp/gitolite.rc.patch
patch /home/git/.gitolite.rc < /tmp/gitolite.rc.patch && rm /tmp/gitolite.rc.patch
Gitolite Post Receive Hook
As user "git", copy this script into /home/git/post-receive-trac and chmod +x it:
#! /usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2011 Grzegorz Sobański
#
# Version: 2.0
#
# Git post receive script developed for mlabs
# - adds the commits to trac
# based on post-receive-email from git-contrib
#
import re
import os
import sys
from subprocess import Popen, PIPE, call
# config
with open("/home/git/TRAC_ENV") as config:
TRAC_ENV = config.read.strip()
GIT_PATH = '/usr/bin/git'
TRAC_ADMIN = '/home/trac/web/ve/bin/trac-admin'
# if you are using gitolite or sth similar, you can get the repo name from environemt
REPO_NAME = os.getenv('GL_REPO')
# communication with git
def call_git(command, args, input=None):
return Popen([GIT_PATH, command] + args, stdin=PIPE, stdout=PIPE).communicate(input)[0]
def get_new_commits(ref_updates):
""" Gets a list uf updates from git running post-receive,
we want the list of new commits to the repo, that are part
of the push. Even if the are in more then one ref in the push.
Basically, we are running:
git rev-list new1 ^old1 new2 ^old2 ^everything_else
It returns a list of commits"""
all_refs = set(call_git('for-each-ref', ['--format=%(refname)']).splitlines())
commands = []
for old, new, ref in ref_updates:
# branch delete, skip it
if re.match('0*$', new):
continue
commands += [new]
all_refs.discard(ref)
if not re.match('0*$', old):
# update
commands += ["^%s" % old]
# else: new - do nothing more
for ref in all_refs:
commands += ["^%s" % ref]
new_commits = call_git('rev-list', ['--stdin', '--reverse'], '\n'.join(commands)).splitlines()
return new_commits
def handle_trac(commits):
if not (os.path.exists(TRAC_ENV) and os.path.isdir(TRAC_ENV)):
print "Trac path (%s) is not a directory." % TRAC_ENV
if len(commits) == 0:
return
args = [TRAC_ADMIN, TRAC_ENV, 'changeset', 'added', REPO_NAME] + commits
with open("/tmp/trac-gitolite.log", 'a') as fp:
fp.write(' '.join(args))
call(args)
# main
if __name__ == '__main__':
# gather all commits, to call trac-admin only once
lines = sys.stdin.readlines()
updates = [line.split() for line in lines]
commits = get_new_commits(updates)
# call trac-admin
handle_trac(commits)
Then, also add a file `/home/git/TRAC_ENV` with the following contents: `/home/trac/sites/test/`
That way, later on, we can easily change the Trac environment that the hook points to, without editing the hook script itself.
Then set up post-receive hooks on all repos to execute that script:
su - git echo '#!/bin/sh /home/git/post-receive-trac' > /home/git/.gitolite/hooks/common/post-receive && chmod +x /home/git/.gitolite/hooks/common/post-receive ./bin/gitolite setup --hooks-only
Now activate the Trac components to hook into that post-receive hook, and to enable a Git version-control backend:
su - trac echo "[components] tracopt.ticket.commit_updater.committicketreferencemacro = enabled tracopt.ticket.commit_updater.committicketupdater = enabled tracopt.versioncontrol.git.git_fs.csetpropertyrenderer = enabled tracopt.versioncontrol.git.git_fs.gitconnector = enabled tracopt.versioncontrol.git.git_fs.gitwebprojectsrepositoryprovider = enabled " >> sites/test/conf/trac.ini
Making Sure It Works
To test it, we'll add Gitolite's "testing.git" repo to Trac:
su - trac web/ve/bin/trac-admin sites/test repository add testing /home/git/repositories/testing.git
Through the web, create a ticket #1 in the Trac "test" environment. Then, on your local machine, clone the repo, add some code, and commit with -m "testing -- fixes #1" before pushing.
If all goes well, Trac's "Browser" tab will show your code and commits from the "testing" repo, and ticket #1 will now be closed.
Related Posts
- Installing and Integrating Trac and Gitolite — Dec. 5, 2012 by egj
- Trac and Gitolite: System Setup — Dec. 8, 2012 by egj
- Integrating Trac and Gitolite: Round One (this post) — Dec. 15, 2012 by egj
- Integrating Trac and Gitolite: Using Postgres — Dec. 22, 2012 by egj