For those who haven’t heard of it yet, TRAC is an open source combination of a ticket and a wiki system mixed with version control integration written in Python. It’s great for organising work especially in small to mid-size development teams but due to the large number of plug-ins and its incredible plug-in API, one can even customise it to cover other aspects of a business. I had meant to write a blog post about it for a while, mostly as a cheat sheet for myself or as a reference to point others to. Since I had to move my personal TRAC to a new machine it is the perfect occasion to write it down for once. So here goes …
At the time of writing, the latest stable TRAC release is 1.2 with Trac 1.3 being in development. When using a different version, your milage my vary when it comes to plug-in compatibility.
A TRAC Environment
The general concept of TRAC is that one server can host multiple separate TRAC environments, for example one per project or product line. Each environment has its own Wiki and tickets as well as various configuration options. Plug-ins can be installed globally or on a per-environment level which keeps everything nice and tidy and makes it easier to move to a new server, etc.
Tickets can be organised within milestones or versions, which are two completely orthogonal things in a standard TRAC installation (more on that later). Each ticket also has a severity, a component and additional fields can be added and are sometimes required for specific plug-ins to work. The workflow of tickets (new, approved, assigned, closed, verified, …) can be also be customised but in general default works quite well.
The Wiki should feel comfortable to anyone that has ever worked with a markup system. What’s great is that one can use markup to link to milestones, tickets, commits and other things within the environment. The wiki-syntax can also heavily be extended by plug-ins.
As you may have noticed, TRAC is very flexible and a lot depends on how you want to use it. There will be some advise on that later on. For now, the goal shall be to setup a TRAC environment and get it up and running.
Getting the base TRAC installation is easy. Open up Terminal and run
sudo easy_install Trac
This will put (among other things) two binaries into your /use/local/bin folder:
- trac-admin: The admin console tool to create, administrate and upgrade TRAC environments (more on that later)
- tracd: The standalone trac server. TRAC is usually hosted as a module inside Apache but can be run as a standalone processes as well.
Creating the Environment
A new environment is created by using
trac-admin /path/to/myproject initenv
On macOS, I usually use /Users/<username>/Documents/Trac/<projectname> but it can be pretty much anywhere. Enter the project name and just confirm that database question. We will have to adapt some things in conf/trac.ini but that can come later.
What you also want to do is add a user for yourself that can serve as TRAC admin:
trac-admin /path/to/projenv permission add <username> TRAC_ADMIN
TRAC is a server-based application and it is accessed via the web browser, no client needed. So the first choice is how to serve it, as
- an Apache module – or –
- a standalone application
While the second one is less secure/robust according to the documentation, I’ll pick that route because I usually used it in closed environments within a single company and therefore no risks of hacking attacks and the likes.
You don’t necessarily need to have a separate server, it’s just as well to run it on your own machine if no one else needs to access it. For sake of simplicity, I’ll assume you have a Mac and want to have it running on your own machine to try it out. Moving things to a server later on is pretty easy.
Using tracd can have some weird effects (especially when running as a service on Windows and you need to shut it down a lot because you want to mess with plug-ins) which is why I would advise to use a slight variation of the second route and directly use python to start TRAC’s standalone.py script. It is part of the TRAC source and you can download the 1.2 version from the repository. I usually store it in /Users/<username>/Documents/Trac so I have my environments and everything else TRAC-related in one place.
Create a new file called trac.sh and add
#/bin/bash python /Users/<username>/Documents/Trac/standalone.py --port 8000 --auth="*,/Users/<username>/Documents/Trac/users.htdigest,Trac" /Users/<username>/Documents/Trac/<projectname>
Make the file executable by using
chown a+x trac.sh
This launches TRAC on port 8000 with the specified environment. If you want to host multiple environments, just add their path as well. The user.htdigest file is for storing passwords and will come to that later.
Open a browser on http://127.0.0.1:8000/<projectname> and you’ll see your TRAC environment.
There are various methods that can be used for user authentication. The easiest is to us an htdigest file. First, add a new user
trac-admin /Users/<username>/Documents/Trac/<projectname> add <newuser>
Then call with the same username
htdigest -c /Users/<username>/Documents/Trac/users.htdigest Trac <newuser>
This will create an entry in the digest file which stores a hashed version of the users’s password.
Plug-Ins and Optional Stuff
Before we get to configuration, let’s install a couple of helpful things. The TRAC community usually posts their plug-ins on TRAC hacks where you can find many, many more. Be aware that the quality of plug-ins can vary greatly. For example, the RevTree plug-in mentioned below at one point had a bug in it that called random.seed() and thus messed up the way session tokens were generated which caused log-in mixups. Took me a couple of days to track that one down…
Note that it sometimes may require a restart of the TRAC server or an upgrade of the database (using trac-admin) to get a plug-in running.
Most of my projects are still in SVN (but GIT works equally well with TRAC). If we want to connect TRAC with an SVN repository, we need to install the Python SVN bindings. The easiest way on macOS to do this is to install Homebrew.
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
Then we can use brew to install the SVN bindings for us as shown in this Stackoverflow post:
$ brew install subversion --with-python $ mkdir -p ~/Library/Python/2.7/lib/python/site-packages $ echo $(brew --cellar)/subversion/1.9.5_1/lib/svn-python > ~/Library/Python/2.7/lib/python/site-packages/svn.pth
(note: the version number in the path, i.e. “1.9.5_1” is dependent on the latest version of the SVN bindings when you install it. In the original post it was 1.8.5, at the time of writing 1.9.5_1. Use “cd $(brew –cellar)” to go to the brew folder and check out what version you installed).
Extended Version Plug-In
Extended Version is a plug-in that let’s you group milestones within versions. This is the way most companies I have seen think in: There are milestones (or feature), some are mandatory for a certain version, others might not be associated with a version until they are out of an early development/prototyping phase.
Note that the plug-in currently needs some work to run with TRAC 1.2. But this is a great opportunity to show how basic plug-in development works! Download the source from the plug-in website on Trac Hacks. In the trunk, apply the latest patch that I attached to the ticket. Go into the directory where setup.py lies and run
python setup.py bdist_egg
which will compile and bundle everything into a Python egg. In your TRAC, go to the Admin Tab, Plug-Ins and there you can upload and install the egg into TRAC. You should also check out the document of a plug-in on its website. Most come with some form of configuration options that you can set in trac.ini to change their behaviour.
This plug-in produces some of the nicest and most helpful branch-visualisations I have ever seen. Problem is that the official trac-hack repo is not maintained and it currently doesn’t work with TRAC 1.2. There is a fork here that almost works (there is a missing comma in setup.py after “templates/*.html” which results in a KeyError exception) but it changed the database schema and I wasn’t able to have it produce results with my existing TRAC environment.
I have started fixing the original version on trac-hacks instead and will post results here when done.
Trac Menu Plug-In
Another goody is the Trac Menu Plug-In which lets you re-organize the TRAC menu bar hierarchical. Again, this unfortunately does not seem to work with TRAC 1.2 right now and I’ll try to patch it soon.
The following are plug-ins which I do not consider essential but which are of great use:
- TOC Macro Plug-In: Adds the [[TOC]] macro to the wiki markup which generates a table of contents for the page it is inserted in. This has ended up in most wiki pages I have written.
- NoteBox Plug-In: Adds a simple macro to add note/warning/error boxes inside a wiki page. Very helpful when writing larger documentation.
- Full Blog Plug-In: I’m a big proponent of internal blogs to spread news throughout a company or team. It’s just better to have things achieved then sending mails to all team members.
- Subcomponent Plug-In: We use this very successfully in an old company of mine. Allows you to hierarchically order components (e.g. UI/Windows, UI/Mac, …)
- Tags Plug-In: Adds a tag-cloud and tagging capability to the environment.
- Downloads Plug-In: Adds a Download section to your TRAC which can be tagged or referenced from within the Wiki
- Autocomplete User Plug-In: Autocompletes user names, quite handy when assigning tickets to users.
- Clone Ticke Plug-In: I personally didn’t have problems filling out tickets manually, but ex-colleagues of mine were a big fan of this one to save time.
- DoxyGen Plug-In: Basically adds a menu entry that links to your existing DoxyGen documentation and adds doxygen to the wiki markup.
- Gantt Calendar Plug-In: I used this very successfully for project planning and vacation management (add tickets for team member vacation). If I remember correctly, the official version had some problem and there was an unofficial version from a Japanese guy that you’ll probably find with some googling.
- Private Wiki Plug-In: This lets you restrict access to certain sub-wiki parts. Great if you want to put things in the TRAC that that 2-week intern should see.
- Screenshots Plug-In: Adds a gallery.
Now comes the important part: configuring TRAC for how you want to use it. Most of this can be done via the Admin-panel inside TRAC.
- In Basic Settings: Set the default handler to WikiModule. This just made the most sense to me, especially if you write a nice intro page with links to the most important parts.
- In Permissions: Remove all rights given to anonymous. As trac admin, you automatically have all rights. But you can create groups and assign them individual rights like “the source should only be visible to members of the developer group”. You have to figure out which groups work for your company but in general it’s a good idea to start with the least amount of rights and then add on demand.
- In Components: Add components for relevant areas of your code. These can be attributed to tickets and make searches easier. So it is usually a good idea to not have too many components but enough that you can for example see what hot spots you have if you look at all open tickets grouped by component. For example, the animation subsystem might be one component, the user interface another, and so on.
- In Priorities: I found it helpful to redefine priority names such that there is no question into what group a ticket belongs. I mean, what really is “normal” and what is “major?”. What I used successfully is:
- “blocks development”: This actively blocks someone and must be solved as soon as possible
- “essential for milestones”: All of these need to be solved or otherwise the milestone cannot be considered complete.
- “normal”: This is stuff that is relevant and one would usually expect to be done for a milestone. But if time gets short, they could be dropped to a later release without the milestone feeling incomplete.
- “optional”: This is purely optional. It might be a nice idea to do if there is still time but nobody will miss them if not.
- “to be discussed”: This proved very helpful when set as the default. If someone creates a new ticket, it should be this priority until the owner of the milestone or project owner has evaluated the priority. By having this artificial priority, it allowed us to create tickets for everything, every bug or idea no matter how unimportant it seemed. The milestone owner would at least consider it and the file correctly.
- In Ticket Types: Most people will intuitively create too many types but it usually boils down to what level of separation actually presents value. You could argue that every ticket is a task after all:
- “bug”: This is a bug, something that should not be. It implies that it can either be solved without major changes or is a fundamental flaw that should immediately be considered.
- “enhancement”: This is something that enhanced existing functionality as opposed to adding new stuff.
- “task”: This is the default work item, it’s just part of feature development.
- “testing”: Something needs to be verified or a unit test written, but it does not require any coding per se.
- In Versions: What works great is to use a 3 digit versioning schema (e.g. 1.0.3). This way you can separate between major (once a year or so, breaks or introduces a lot of things) and minor (adds new features but keeps compatibility for the most) or bugfix (no new feature, just bugs) releases.
- In Repository: add your SVN (or other) repository.
One of the most interesting aspects of TRAC is the way it interacts with a version control system. You can configure it such that you can close tickets with an SVN commit message or link bug tickets and the commits that fixed them.
To do this, you need to setup your repository inside TRAC. However, TRAC also needs to know when the repository changes. This is done using post-commit hooks which have to be added to your SVN repository:
Go to your SVN repository and the subfolder “hooks”. Create a copy of post-commit.tmpl and name it “post-commit”. Inside it, remove all the content and simply add:
#!/bin/sh /usr/local/bin/trac-admin /Users/<username>/Documents/Trac/<projectname> changeset added "$1" "$2"
This way every time a commit is done, TRAC is called and it can cache the required information about the commit in its database. You also need to activate to plug-ins that already come with TRAC:
If you unfold the latter entry in the plug-ins list, you’ll see a summary of the command syntax. The most important are “refs #5” to add the commit message as a comment to ticket 5 and “closes #6” to do the same to ticket 6 but also close it.
When you initially activate the binding, it makes sense to once tell TRAC to sync with the repository.
trac-admin /Users/<username>/Documents/Trac/<projectname> repository resync "*"
This will force TRAC to pull the commits into its own cache.
Using the Wiki
Here are some tips for using the Wiki:
- When editing a page, there is a small check box that says side-by-side editing. Activate it to get a live preview of your text. Using this helps with user acceptance, especially for those that are not used to write markup.
- Check out the macro help page (follow the formatting help link on the edit page). What is great about trac is that this help page is dynamically created and each plug-in you have installed will automatically add its documentation to this page.
- Images are integrated by attaching them to a page and then using the [[Image(…)]] macro.
- The wiki content is version controlled inside the TRAC database, attachments are not!
- Use sub-wikies (e.g. a name and slash before the actual wiki page name) to organise your content. Add links to the TRAC menu that directly lead there (e.g. one for “user manual”, one for “dev docu”, …)
For completeness sake, here is the configuration file I’ll currently use. Some things might need adjustment for your paths or depend on the plug-ins you have installed. But it should serve as a reference. If tried to mark the interesting bits.
# -*- coding: utf-8 -*- [attachment] max_size = -1 #disables size limitation for attachments max_zip_size = -1 # disables size limitation for attachments render_unsafe_content = disabled [browser] color_scale = enabled downloadable_paths = /trunk,/branches/*,/tags/* hide_properties = svk:merge intermediate_color = intermediate_point = newest_color = (255, 136, 136) oldest_color = (136, 136, 255) oneliner_properties = trac:summary render_unsafe_content = disabled wiki_properties = trac:description [changeset] easy_install http = //trac-hacks.org/svn/extendedversionplugin/trunk max_diff_bytes = 10000000 max_diff_files = 0 wiki_format_messages = enabled [components] extendedversion.* = enabled extendedversion.db.extendedversionssetup = enabled extendedversion.milestone.milestoneversion = enabled extendedversion.roadmap.releasesmodule = enabled extendedversion.version.visibleversion = enabled mergeinfoenhancer.enhancer.mergeinfoenhancermodule = enabled notebox.macro.noteboxmacro = enabled revtree.* = enabled revtree.api.revtreesystem = enabled revtree.enhancer.simpleenhancermodule = enabled revtree.optimizer.defaultrevtreeoptimizer = enabled revtree.web_ui.revtreemodule = enabled tracmenus.web_ui.menumanagermodule = disabled tracopt.ticket.commit_updater.committicketreferencemacro = enabled tracopt.ticket.commit_updater.committicketupdater = enabled tracopt.versioncontrol.svn.* = enabled tractoc.* = enabled [extended_version] milestone_stats_provider = DefaultTicketGroupStatsProvider navigation_item = roadmap roadmap_navigation = enabled show_milestone_description = disabled show_milestones_in_overview = enabled show_tickets_in_overview = enabled version_stats_provider = DefaultTicketGroupStatsProvider [gitweb-repositories] projects_base = projects_list = projects_url = [header_logo] alt = height = 87 link = src = width = -380 [inherit] htdocs_dir = plugins_dir = templates_dir = [logging] log_file = trac.log log_level = DEBUG log_type = file [mainnav] # This is the configuration for the menu plug-in browser.order = 4 browser.parent = sourcegrp maingrp = enabled maingrp.href = /wiki maingrp.label = Main maingrp.order = 10 maingrp.parent = top newticket.order = 6 newticket.parent = ticketgrp query = enabled query.href = /query query.label = Custom Query query.order = 2 query.parent = ticketgrp revtree.parent = sourcegrp roadmap.order = 3 search = disabled search.order = 7 sourcegrp = enabled sourcegrp.href = /browser sourcegrp.label = Source Code sourcegrp.order = 40 sourcegrp.parent = top ticketgrp = enabled ticketgrp.href = /report/1 ticketgrp.label = Tickets ticketgrp.order = 30 ticketgrp.parent = top tickets.label = View Reports tickets.order = 5 tickets.parent = ticketgrp timeline.order = 2 timeline.parent = maingrp versions.order = 20 wiki.order = 1 wiki.parent = maingrp [metanav] about.order = 5 help.order = 4 login.order = 1 logout.order = 2 prefs.order = 3 [milestone] stats_provider = DefaultTicketGroupStatsProvider [mimeviewer] max_preview_size = 262144 mime_map = text/x-dylan:dylan,text/x-idl:ice,text/x-ada:ads:adb mime_map_patterns = text/plain:README|INSTALL|COPYING.* tab_width = 8 treat_as_binary = application/octet-stream,application/pdf,application/postscript,application/msword,application/rtf [notification] admit_domains = ambiguous_char_width = single batch_subject_template = $prefix Batch modify: $tickets_descr email_sender = SmtpEmailSender ignore_domains = mime_encoding = none sendmail_path = sendmail smtp_always_bcc = smtp_always_cc = smtp_default_domain = smtp_enabled = disabled smtp_from = trac@localhost smtp_from_author = disabled smtp_from_name = smtp_password = smtp_port = 25 smtp_replyto = trac@localhost smtp_server = localhost smtp_subject_prefix = __default__ smtp_user = ticket_subject_template = $prefix #$ticket.id: $summary use_public_cc = disabled use_short_addr = disabled use_tls = disabled [notification-subscriber] always_notify_cc = CarbonCopySubscriber always_notify_previous_updater = TicketPreviousUpdatersSubscriber always_notify_updater = TicketUpdaterSubscriber [project] admin = admin_trac_url = . descr = Modeler footer = Visit the Trac open source project at<br /><a href="http://trac.edgewall.org/">http://trac.edgewall.org/</a> icon = common/trac.ico name = Modeler url = [query] default_anonymous_query = status!=closed&cc~=$USER default_query = status!=closed&owner=$USER items_per_page = 100 ticketlink_query = ?status=!closed [report] items_per_page = 100 items_per_page_rss = 0 [repositories] .dir = /Users/myusername/.SVN/Modeler # The path to the SVN repository [revisionlog] default_log_limit = 100 graph_colors = ['#cc0','#0c0','#0cc','#00c','#c0c','#c00'] [revtree] abstime = enabled branch_re = ^(?P<branch>branches/[^/]+|trunk|tags)(?:/(?P<path>.*))?$ contexts = revbase = 1 scale = 1.0 style = compact trunks = trunk [roadmap] stats_provider = DefaultTicketGroupStatsProvider [search] min_query_length = 3 [svn] authz_file = authz_module_name = [ticket] default_cc = default_component = application default_description = default_keywords = default_milestone = default_owner = < default > default_priority = to be discussed default_resolution = fixed default_severity = default_summary = default_type = task default_version = max_comment_size = 262144 max_description_size = 262144 preserve_newlines = default restrict_owner = disabled workflow = ConfigurableTicketWorkflow [ticket-workflow] accept = new,assigned,accepted,reopened -> accepted accept.operations = set_owner_to_self accept.permissions = TICKET_MODIFY create = <none> -> new create.default = 1 create_and_assign = <none> -> assigned create_and_assign.label = assign create_and_assign.operations = may_set_owner create_and_assign.permissions = TICKET_MODIFY leave = * -> * leave.default = 1 leave.operations = leave_status reassign = new,assigned,accepted,reopened -> assigned reassign.operations = set_owner reassign.permissions = TICKET_MODIFY reopen = closed -> reopened reopen.operations = del_resolution reopen.permissions = TICKET_CREATE resolve = new,assigned,accepted,reopened -> closed resolve.operations = set_resolution resolve.permissions = TICKET_MODIFY [timeline] abbreviated_messages = enabled changeset_collapse_events = disabled changeset_long_messages = disabled changeset_show_files = 0 default_daysback = 30 max_daysback = 90 newticket_formatter = oneliner ticket_show_details = disabled [trac] auth_cookie_lifetime = 0 auth_cookie_path = auto_preview_timeout = 2.0 auto_reload = disabled backup_dir = db base_url = http://127.0.0.1:8000 check_auth_ip = disabled database = sqlite:db/trac.db debug_sql = disabled default_charset = utf-8 default_dateinfo_format = relative genshi_cache_size = 128 htdocs_location = ignore_auth_case = disabled jquery_location = jquery_ui_location = jquery_ui_theme_location = mysqldump_path = mysqldump never_obfuscate_mailto = disabled permission_policies = ReadonlyWikiPolicy,DefaultPermissionPolicy,LegacyAttachmentPolicy permission_store = DefaultPermissionStore pg_dump_path = pg_dump resizable_textareas = enabled secure_cookies = disabled show_email_addresses = disabled show_ip_addresses = disabled timeout = 20 use_base_url_for_redirect = disabled [versioncontrol] allowed_repository_dir_prefixes = default_repository_type = svn [wiki] ignore_missing_pages = disabled max_size = 262144 render_unsafe_content = disabled safe_schemes = cvs,file,ftp,git,irc,http,https,news,sftp,smb,ssh,svn,svn+ssh split_page_names = disabled