Setting-up Trac
by Sebastien Mirolo on Sun, 2 Oct 2011I needed to setup a forum for developers on a recent project. That included a source control repository (git), a wiki, a blog, a buildbot and an issue tracking system. To provide the last components I decided to setup trac and a few trac plug-ins.
Install
I realized later that initialization of a trac environment often sets valid defaults for all plug-ins are already on the local system. Some plug-ins are available, as trac, through the package manager (aptitude search 'trac-.*') while others can be installed through python setup tools (easy_install) or finally a source archive. I have installed the following plug-ins for
- Authentication
- Source repository
- Project tracking
- Custom design
# Base servers $ apt-get install trac nginx
# Authentication $ apt-get install trac-accountmanager $ find /usr/lib -name '*acct_mgr*' /usr/lib/pymodules/python2.7/acct_mgr $ easy_install http://trac-hacks.org/svn/noanonymousplugin/0.11 ... Installed /usr/local/lib/python2.7/dist-packages/TracNoAnonymous-2.4-py2.7.egg $ apt-get install pwauth $ curl -O TracPwAuth-1.0.tar.gz $ tar zxvf TracPwAuth-1.0.tar.gz $ cd TracPwAuth-1.0 && python ./setup.py install ... Installed /usr/local/lib/python2.7/dist-packages/TracPwAuth-1.0-py2.7.egg
# Source repository $ apt-get install trac-git $ easy_install http://trac-hacks.org/svn/bittenforgitplugin/0.11/0.6b2 ... Installing bitten-slave script to /usr/local/bin Installed /usr/local/lib/python2.7/dist-packages/Bitten-0.6b2-py2.7.egg $ wget http://git.mortis.eu/git/codetags.git/snapshot/0.3.tar.gz $ tar zxvf 0.3.tar.gz $ cd codetags-0.3-fb76322 && python ./setup.py install ... Installed /usr/local/lib/python2.7/dist-packages/codetags-0.3-py2.7.egg $ easy_install http://trac-hacks.org/svn/revtreeplugin/0.11/ ... Installed /usr/local/lib/python2.7/dist-packages/TracRevtreePlugin-0.6.3dev_r5601-py2.7.egg $ easy_install http://trac-hacks.org/svn/reposearchplugin/0.11/ ... Installing update-index script to /usr/local/bin Installed /usr/local/lib/python2.7/dist-packages/tracreposearch-0.2-py2.7.egg
# tracking metrics $ easy_install http://trac-hacks.org/svn/tagsplugin/tags/0.6 $ easy_install --always-unzip http://trac-hacks.org/svn/fullblogplugin/0.11 $ git clone https://github.com/mrjbq7/tracstats.git $ cd tracstats && python ./setup.py install ... Installed /usr/local/lib/python2.7/dist-packages/TracStats-0.4-py2.7.egg $ easy_install http://trac-hacks.org/svn/icalviewplugin/0.11/ ... Installed /usr/local/lib/python2.7/dist-packages/icalview-0.4-py2.7.egg $ easy_install http://trac-hacks.org/svn/timingandestimationplugin/branches/trac0.11 ... Installed /usr/local/lib/python2.7/dist-packages/timingandestimationplugin-0.9.8-py2.7.egg $ easy_install http://trac-hacks.org/svn/scrumburndownplugin/trunk/ ... Installed /usr/local/lib/python2.7/dist-packages/TracBurndown-1.9.2-py2.7.egg $ easy_install http://trac-hacks.org/svn/tasklistplugin/trunk/ ...
# Custom design $ easy_install http://trac-hacks.org/svn/themeengineplugin/0.11/ ... Installed /usr/local/lib/python2.7/dist-packages/TracThemeEngine-2.0.1-py2.7.egg $ easy_install http://trac-hacks.org/svn/randomincludeplugin/0.11/ ... Installed /usr/local/lib/python2.7/dist-packages/TracRandomInclude-0.1-py2.7.egg
Some of these commands installed trac 0.12 in /usr/local/bin/trac-admin and /usr/local/bin/tracd. That created a lot of incompatibilities and problems later on so I deleted them in order to stick with the Ubuntu 11.04 packaged trac 0.11 version.
Create repository
$ vi testme.py $ sudo -u www-data vi testme.py $ less testme.py $ sudo -u www-data git add testme.py $ sudo -u www-data git commit -m 'FIXME codetag'
It is now time to create the trac environment.
$ man trac-admin $ trac-admin help $ mkdir /var/www/trac $ trac-admin /var/www/trac initenv testproj sqlite:db/trac.db git /var/www/reps/testproj/.git
And turn on logging in order to debug configuration issues along the way.
$ diff -u trac.ini.prev trac.ini [logging] log_level = DEBUG - log_type = none + log_type = file
Authentication
Since the information on the trac site is confidential, I decided to setup it behind https. A lot of the documentation dealing with trac and authentication rely on web server authentication. That is unfortunate because that pops up an authentication dialog box. Most sites that require authentication today land on a login page and I wanted the same functionality for the trac site. Fortunately there is the wonderful AccountManager plug-in to do that. I still had to prevent unauthenticated access to the trac site and the NoAnonymous plug-in enabled that requirement.
Last, I needed to choose a password store to check username/password against. I picked TracPwAuth to authenticate against the unix /etc/passwd file and avoid password stores proliferation.
Trac has a built-in http server that makes it straightforwad to start serving trac pages after the daemon is running.
$ tracd -d --port 8000 /var/www/trac $ tracd -s --port 8000 /var/www/trac $ curl -O http://localhost:8000/trac
Unfortunately trac does not have a built-in https server so we will need to rely on a more complete web server in front of it. I picked nginx. I also decided to setup nginx/trac through a fastcgi interface instead of a proxy forward from nginx to trac, a choice I might well revert in the future.
I disabled the default site in nginx, created a trac site configuration and enabled it.
$ cat /etc/nginx/sites-available/trac server { listen 443; server_name domainname; ssl on; ssl_certificate /etc/ssl/certs/domainname.pem; ssl_certificate_key /etc/ssl/private/domainname.key; ssl_session_timeout 5m; ssl_protocols SSLv3 TLSv1; ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv3:+EXP; ssl_prefer_server_ciphers on; if ($uri ~ ^/(.*)) { set $path_info /$1; } #if ($request_uri ~ /login) { # break; #} #if ($remote_user ~ ^$) { # set $path_info /login; # rewrite ^ /login redirect; #} # You can copy this whole location to ``location [/some/prefix]/login`` # and remove the auth entries below if you want Trac to enforce # authorization where appropriate instead of needing to authenticate # for accessing the whole site. # (Or ``location /some/prefix``.) location / { #auth_basic "trac realm"; #auth_basic_user_file /home/trac/htpasswd; # socket address fastcgi_pass unix:/var/www/tracenv/run/instance.sock; # python - wsgi specific fastcgi_param HTTPS on; ## WSGI REQUIRED VARIABLES # WSGI application name - trac instance prefix. # (Or ``fastcgi_param SCRIPT_NAME /some/prefix``.) fastcgi_param SCRIPT_NAME ""; fastcgi_param PATH_INFO $path_info; ## WSGI NEEDED VARIABLES - trac warns about them fastcgi_param REQUEST_METHOD $request_method; fastcgi_param SERVER_NAME $server_name; fastcgi_param SERVER_PORT $server_port; fastcgi_param SERVER_PROTOCOL $server_protocol; fastcgi_param QUERY_STRING $query_string; # for authentication to work fastcgi_param AUTH_USER $remote_user; fastcgi_param REMOTE_USER $remote_user; # for ip to work fastcgi_param REMOTE_ADDR $remote_addr; # For attchments to work fastcgi_param CONTENT_TYPE $content_type; fastcgi_param CONTENT_LENGTH $content_length; } } $ cd /etc/nginx/sites-enabled $ rm default $ ln -s ../sites-available/trac
There is a script in /usr/lib/python2.7/dist-packages/trac/admin/templates/ called deploy_trac.fcgi. It contains a few template variables that need to be instantiated and looks quite different from the ones written up on the official trac wiki. I finally decided to copy/paste the one from the wiki into a local /var/www/trac/trac.fcgi file.
#!/usr/bin/env python import os sockaddr = '/var/www/trac/run/instance.sock' os.environ['TRAC_ENV'] = '/var/www/trac' try: from trac.web.main import dispatch_request import trac.web._fcgi fcgiserv = trac.web._fcgi.WSGIServer(dispatch_request, bindAddress = sockaddr, umask = 7) fcgiserv.run() except SystemExit: raise except Exception, e: print 'Content-Type: text/plain\r\n\r\n', print 'Oops...' print print 'Trac detected an internal error:' print print e print import traceback import StringIO tb = StringIO.StringIO() traceback.print_exc(file=tb) print tb.getvalue()
both nginx and trac.fcgi need access to the socket file. Nginx is running as the www-data user. It is possible to run trac as a different user by setting the following permissions.
$ mkdir -p /var/www/trac/run $ chgrp www-data run $ chmod g+ws run $ chgrp www-data trac.fcgi $ chmod g+s trac.fcgi
Later though using two different users will prevent authentication. If /usr/sbin/pwauth returns error code "50" (STATUS_INT_USER) when run as the trac user, it means pwauth was compiled without that user defined in SERVER_UIDS (config.h). At this point, I prefer to use apt-get to install pwauth instead of recompiling it from source, so I run nginx and trac as the www-data user, updating files permissions to reflect that.
$ sudo chown -R www-data:www-data /var/www/trac
$ diff -u trac.ini.prev trac.ini [account-manager] +password_store = PwAuthStore [components] +trac.web.auth.LoginModule = disabled +acct_mgr.web_ui.LoginModule = enabled +acct_mgr.web_ui.RegistrationModule = disabled +pwauth.* = enabled +noanonymous.* = enabled
Note: If you prefer the popup approach and handle all authentication through nginx, checkout the httpAuth plugin.
Nginx as a proxy to trac http server
The fastcgi approach worked fine but I still decided to use the http proxy setup after all because it requires less configuration steps. I also found an init.d script for tracd which proved valuable to start and stop tracd as a service. Here is the nginx configuration for the site:
upstream proxy_trac { server 127.0.0.1:8000; } server { listen 80; server_name domainname; location / { rewrite ^/(.*)$ https://domainname/$1 redirect; } } server { listen 443; server_name domainname; ssl on; ssl_certificate /etc/ssl/certs/domainname.pem; ssl_certificate_key /etc/ssl/private/domainname.key; ssl_session_timeout 5m; ssl_protocols SSLv3 TLSv1; ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv3:+EXP; ssl_prefer_server_ciphers on; # it makes sense to serve static resources through Nginx location /chrome/ { alias /var/www/htdocs/; } location / { proxy_pass http://proxy_trac; proxy_redirect default; proxy_set_header Host $host; } }
The site stopped to respond to request anymore as soon as I enabled the noanonymous plug-in. As it runs out something is creating an http redirect that required me to open port 80 in the firewall and add the appropriate redirects in the nginx config file.
PwAuth means trac contributors have a unix account on the server machine. As a result if you want to modify a password you will need to run passwd from a unix shell on the machine, in turn that might require to give all contributors ssh login to the machine.
What I really wanted in a single authentication directory for users, the ability for them to change their passwords but do not grant shell access to all users. I thought setting-up an LDAP password store would be great. They are a lot of LDAP plugins, each of them somewhat forked from each other. None-of-them seem fully implemented. It just became such a nightmare that I decided to switch to redmine on the production site.
Browsing the git repository
The issue when you use the local package manager to install plug-ins is that you have no idea where files are copied. That makes things a little tricky when you need to enable components based on pathnames. To finally write the correct line to enable git source browser, I had to rely on inspecting the .deb package as such.
$ aptitude download trac-git $ dpkg -c trac-git_0.0.20100513-2ubuntu1_all.deb ... /usr/lib/python2.7/dist-packages/tracext/git/__init__.py
$ diff -u trac.ini.prev trac.ini +[code-tags] +scan_files = *.html, *.js, *.py +scan_folders = /* +tags = XXX, TODO, FIXME, BUG [components] +tracext.git.* = enabled +codetags.* = enabled +revtree.* = enabled +tracreposearch.* = enabled +[repo-search] +include =*.html:*.js:*.py +exclude = *.pyc:*.png:*.jpg:*.gif:*/README [revtree] +#contexts = changeset, browser [trac] # See http://trac.edgewall.org/wiki/TracIni -base_url = +base_url = http://domainname/trac -mainnav = wiki,timeline,roadmap,browser,tickets,newticket,search +mainnav = wiki,timeline,roadmap,browser,revtree,tickets,newticket,search repository_dir = /var/www/reps/testproj/.git
The database needs to be upgraded (codetags)
tracking metrics
The Agile trac plugin looked interesting but requires a patched trac. The patch to the ubuntu installed version is pretty huge. Browsing through the Agile trac website I did not notice enough compelling arguments to apply such an intrusive patch so I skipped this plug-in for now.
$ find /usr/lib -name '*trac*' ... /usr/lib/python2.7/dist-packages/trac ... $ svn co http://svn.agile-trac.org/BRANCH/AGILE-TRAC/SOURCE/0.11/REL/patch/trac/ trac $ diff -ru /usr/lib/python2.7/dist-packages/trac trac
The ScrumBurndown plugin requires TimingAndEstimation plug-in so I installed and configured this one as well.
$ diff -u trac.ini.prev trac.ini [components] +tractags.* = enabled +tracfullblog.* = enabled +tracstats.* = enabled +timingandestimationplugin.* = enabled +burndown.* = enabled +tasklist.* = enabled +[icalendar] +dtstart = my_custom_dtstart_field +duration = my_custom_duration_field +short_date_format = %d/%m/%Y;%Y-%m-%d +date_time_format = %d/%m/%Y %H:%M;%Y-%m-%d %H:%M +[ticket-custom] +my_custom_dtstart_field = text +my_custom_dtstart_field.label = Planned Date +my_custom_duration_field = text +my_custom_duration_field.label = Duration +action_item = text +action_item.label = Action Item [trac] -mainnav = wiki,timeline,roadmap,browser,revtree,tickets,newticket,search +mainnav = wiki,blog,timeline,roadmap,browser,revtree,tickets,newticket,search
Permissions need to be set appropriately for buttons to show up in the menubar.
$ trac-admin /var/www/trac permission list $ trac-admin /var/www/trac permission add anonymous STATS_VIEW
Custom design
I tried to install GoogleCodeTheme but the repository is empty. Downloading the archive creates a zip file that seems invalid. Finally I gave up on it and tried the GameDev theme instead.
$ easy_install http://trac-hacks.org/svn/gamedevtheme/0.11/ ... Installed /usr/local/lib/python2.7/dist-packages/TracGamedevTheme-2.0-py2.7.egg
$ diff -u trac.ini.prev trac.ini [components] +themeengine.* = enabled +gamedevtheme.* = enabled [theme] -theme = default +theme = Gamedev
Conclusion
Plug-in architectures are great but it seems trac went a little overboard as most of the expected functionality comes in the form of plug-ins more or less complex to configure.
In the end I decided to settle on using redmine because most of what I am looking for comes pre-packaged in-the-box (except changing LDAP password). How I set up redmine is the subject of a further post.