Edgewall Software

Version 42 (modified by Dave Abrahams <dave@…>, 17 years ago) ( diff )

A Comprehensive Multi-Project Solution

Abstract: we present a comprehensive solution for many of the problems of maintaining or participating in multiple projects under Trac. We briefly analyze some of the other approaches and explain why we're not using them.

This method supports:

  • Cross-project ticket queries. Find out what your most important issues are in one swell foop.
  • Single sign-on. Login once and skip around between Trac instances at will (subject to per-instance permissions, of course).
  • Central configuration. You'll no longer have to edit multiple trac.ini files just to make a configuration change across many Trac instances.
  • Common permissions. User permissions can be granted across all projects or on a per-project basis [this is misphrased; I haven't figured out how to say what I actually mean precisely, but it's good]

Note: this solution obviously owes much to the work of others; I'm not claiming to be an innovator here, just an integrator.

Caveats

  • This page is a work in progress. I've solved the technical issues on my local development server and am composing this page as I port my real Trac instances over to the new system.
  • This method requires the use of not-yet-released versions of Trac, Genshi, and several plugins, available from those projects' Subversion repositories.
  • This approach isn't going to work for everybody. Ticket #130 contains a long, rambling, and extremely enlightening discussion of what people need from multi-project support. Anyone who has read that thread, and the other pages proposing different ways to support multiple projects, knows that no single approach is going to satisfy everyone. Nonetheless, I think I've hit a "sweet spot" that gives most people most of what they're looking for, and more importantly, can be (relatively) easily set up today by anyone who is willing to use bleeding-edge Trac software and my choice of database technology.

Motivation

When you host multiple trac projects, its very common to have users that are members of more than one of these projects. If, like me, you're using your Tracs to support multiple customers, you probably need to be a member of each trac instance. Even when using a TRAC_ENV_PARENT_DIR setup, one typically ends up having to log in to three or four Trac instances a day as you review your most active projects. Just having to log in over and over is problematic in itself. Worse, if you're tracking tickets in several projects, there's no way to see them all together in stock Trac. You actually need to log into each project and check your tickets there, and it becomes very likely that you'll miss something really important.

The other problem is that, when administering multiple similar tracs, you end up repeating the same configuration steps over and over. The tedious jobs include:

  • creation and initialization of new trac instances
  • setting up common user permissions
  • making trac.ini adjustments across all projects

Basic Strategy

  • Use PostGreSQL to collect all projects into a single database using separate PostGreSQL "schemas."
  • Change the SQL of your ticket report queries to:
    • Do the query in all schemas
    • Add an extra column to the report that names each ticket's project.
  • Use trachacks:MultipleProjectQueryFilterPlugin to fix up urls in those query results to point to the right tickets.
  • Use TracForge to get:
    • Single Sign-On
    • An improved project list
    • Global “member” and “admin” roles that represent standard sets of permissions
  • Use the inherit feature to avoid massive replication of trac.ini configuration information
  • Share a single Subversion repository between all your trac instances, and use the SVN AuthzGroups Plugin to avoid replicating group membership information between your SVN authz file and your trac instances.

Requirements

Creating Multiproject Queries

As noted above, the strategy is to change the SQL queries to iterate over all projects and aggregate the results, while adding an extra column to indicate which project will be used. This information will be used by trachacks:MultipleProjectQueryFilterPlugin to fix up the URLs in each row of the resulting HTML table.

Getting my queries to have an extra project column without tying my SQL to the current Trac ticket table layout was lots of fun. I have to thank Pedro Gimeno Fortea for his SQL expertise and help in working it out. The problem is as follows:

  • The core multi-project query logic is nontrivial. You want to factor it into an SQL function so you don't repeat it in every query.
  • Functions' return types need to be declared. In this case, the return type is a table just like Trac's ticket table, but with one extra column
  • We don't want our SQL to be dependent on the format of the ticket table, since that could change when you upgrade Trac or even when you install a plugin.

We ended up doing a bit of SQL metaprogramming: we wrote a function called declare_multitrac_query that declares the actual multiproject query function. If you call declare_multitrac_query() at the beginning of every query, you can then use multitrac_query( WHERE-condition-as-string ) to get the actual table. Just make sure to add the project column to the beginning of the result. I know this sounds complicated, but it's actually a simple transformation of any of your existing queries. For example, here's what happened to report:1:

  • .sql

    old new  
     1set search_path to trac_instance_master,public;
     2SELECT declare_multitrac_query();
     3
    14SELECT p.value AS __color__,
    2    id AS ticket, summary, component, version, milestone, t.type AS type,
     5   project, id AS ticket, summary, component, version, milestone, t.type AS type,
    36   (CASE status WHEN 'assigned' THEN owner||' *' ELSE owner END) AS owner,
    47   time AS created,
    58   changetime AS _changetime, description AS _description,
    69   reporter AS _reporter
    7   FROM ticket t WHERE status <> 'closed'
     10  FROM multitrac_query('status <> ''closed''') t
    811  LEFT JOIN enum p ON p.name = t.priority AND p.type = 'priority'
    912  ORDER BY p.value, milestone, t.type, time

Notes:

  • Line 1: we set the PostgreSQL schema search path so that declare_multitrac_query, which is in the public schema, can be found. trac_instance_master is the schema I used for the TracForge master trac instance.
  • Line 5: we prepend the project name to the results
  • Line 10: Notice that the WHERE condition has been string-ized. Single quotes are embedded in strings by doubling them.

Why Some Other Approaches Don't Quite Work (Yet)

As #130 shows, this has been an area of intense interest, and several alternative techniques have been tried. To date, none of them meet my basic requirements. I explain why below.

Sharing a Single Trac

There's been some work on managing multiple projects within a single Trac instance, but Trac can't yet give my customers the privacy they need in a shared environment. Sometimes we're working on proprietary software that must be kept private, but even when we're working on open source, customers generally feel uncomfortable when all their issues are automatically exposed to the world.

There are at least two areas that need to be improved in Trac to make a shared Trac workable:

  • TracDev/SecurityBranch (which, despite its name, is on Trac's trunk now) needs to be extended to cover Trac resources other than Wiki pages.
  • We need a flexible and automatic way to attach these permissions to resources upon creation. In my usage model, when a customer enters a ticket, it should be visible to and writable by everyone in his company and everyone in my company, but nobody else. Also, I occasionally need to create a ticket myself, with those same properties, and assign it to the customer. These capabilities are outside the scope of TracDev/SecurityBranch, so they need to be addressed separately. The Private Tickets Plugin can do the first part of the job for me (using the old permissions system), but not the second. See also #1316.

Naturally, the same kinds of issues apply to other resources such as Milestones. Upon creation by a customer, they need to be private to that customer and (some subset of) my people. When I create these resources there needs to be an easy way to make them private to a particular customer's company. And I'm sure that other people will have vastly different "permission workflow" requirements than I do, so the system probably needs to be more flexible than what I've described.

I do think this approach has a future, I just don't know how close it is.

Previous Attempts to Leverage PostGreSQL Schemas

TracMultipleProjects/MultipleEnvironmentsSingleDatabase describes two approaches to using PostGreSQL schemas for doing multi-project reports:

  • The first (main) approach probably works, but it fundamentally changes the layout of the database, so trac-admin upgrade will fail, maybe even spectacularly. That wasn't acceptable to me.
  • The second (alternative) approach is much simpler, but it also has a fatal flaw: although you can get a report, clicking any of the tickets still takes you to the ticket with the same ID as the ticket you clicked, but in the current project.

TracForge Ticket Subscriptions

TracForge contains a feature called "subscriptions" that replicates data from one trac instance to another. One could try to use that to make all those tickets available in a master project where they can be queried. However,

  1. Once the subscription is established, tickets are only replicated across Trac instances when new tickets are created. That means I'm out-of-luck when it comes to querying across my pre-existing Trac instances.
  2. All that data replication is wasteful (this is minor) and probably slow (could be significant).
  3. This approach has basically the same flaw as the alternative approach above using PostGreSQL, just one level removed: yes, if you click on a ticket in the report, it takes you to a ticket with the right content… but that ticket is still in the wrong context… so any project-local links to wiki pages changesets, etc. that are embedded in the ticket will take you to the wrong place. One could try to patch the subscription code to fix them up, but in my opinion that is a hopeless fight. To handle all the possible wiki syntax, you'd have to do a reverse translation for all the macros, plugins, etc. that introduce new wiki syntax. There are just too many to possibly account for all of them, and trying to keep up with new ones would be a maintenance nightmare.


Discussion

  • Just a quick feedback to mention that at this point, what your solution is isn't immediately clear ;-). From the requirements section below it appears that it is based on TracForge, so maybe this should be made more clear from the start. Note that TracForge itself is probably implementing something quite close to what is discussed in TracMultipleProjects/MultipleEnvironments.
    — cboos 06/25/2007 11:02:46 AM
  • Yes, I know. As I said, "work in progress," which I hope to have finished in the next few days. I'm actually combining stuff from TracForge, various other plugins from TracHacks, stuff from TracMultipleProjects/MultipleEnvironmentsSingleDatabase, and some of my own code. The approach for supporting cross-project queries is entirely different from what TracForge does, so characterizing it as "based on TracForge" won't really tell people what it is.
    — dave 06/28/2007 04:45:59 PM
  • If you can tell me more than 3 persons who could understand how tracforge did solve "cross project queries" i pay you a beer, and based on tracforge is confusing as you solved it differently. otherwise "based on tracforge" is not confusing as anyway nobody knew it, or tracforge did not solve it, and you just extended it to make it a workable solution …
    ThurnerRupert 06/29/2007 05:14:04 PM
  • Did what? Also, sorry, but everything after "beer" doesn't parse for me … and that's not just selective listening!
    — dave 06/30/2007 06:11:46 AM

Attachments (3)

Download all attachments as: .zip

Note: See TracWiki for help on using the wiki.