Code review goodness with ReviewBoard

The City of Ottawa’s Web group required a code inspection tool to comply with the Payment Card Industry Data Security Standard (PCI DSS) section 6.6 “Code Review”. I investigated a couple of solutions, both open source and commercial, that fit our need for a fast, Web 2.0, side-by-side diff, inline comments, LDAP supported, and feature rich tool. It seems that Atlassian’s Crucible (purchase), Smartbear’s CodeCollaborator (purchase), and ReviewBoard (open source MIT license) were the pack leaders. I had used Crucible at Nortel and I really liked that product, but it was costly. Since I ❤ open source alternatives rather to paying for license fees, I opted to try ReviewBoard first. There was one problem though, the City is a Microsoft shop and we use Team Foundation Server (TFS) <grumble> as our source control repository but ReviewBoard didn’t list TFS in its supported repositories. Hrm…

Server Setup

The ReviewBoard v1.0.9 environment was installed on Windows XP, Apache 2.2, PHP 5.3.2, MySQL 5.1, Python 2.6 (2.7 was the most recent however all of the required plugins for ReviewBoard were not Python 2.7 compatible yet so I moved down to 2.6). After the installation my environment variable for PATH is:

C:GnuWin32bin;C:PHP;%SystemRoot%system32;%SystemRoot%;%SystemRoot%System32Wbem;c:progra~1oracleora10bin;C:MySQLMySQL Server 5.1bin;C:Python26;C:Python26Scripts;C:Program FilesMicrosoft Visual Studio 9.0Common7IDE


I followed all of the steps in the 1.0 stable and also the 1.5 development documentation. Here are some notes on that install:

  1. Installing Python Support: you can use Python 2.6 safely. Also install the setuptools for 2.6.
  2. Installing patch: I had trouble getting this into the correct path for ReviewBoard to see it. I had to put it in C:GnuWin32bin and C:Windowssystem32 and set my PATH environment variable as seen above.
  3. Installing memcached: follow the steps listed
  4. Installing Python Imaging Library (PIL): follow the steps
  5. Installing PyCrypto: follow the steps
  6. Installing Review Board: follow the steps
  7. Installing Database Bindings: try using the command but I had trouble using easy_install to get mysql-python. I had to go out and download the binary from a website and install it manually. I found MySQL-python-1.2.3c1.win32-py2.6.exe from a website (sorry cant remember which but let me know if you find it).
  8. Installing Source Control Components: it sounds a little odd, but we install the Subversion component to connect to TFS. Later we will install an adapter that will interpret SVN commands and forward them to the TFS server. Install the PySVN component as instructed.
  9. Syntax Highlighting: ReviewBoard can take advantage of syntax highlighting through a program call Pygments. To install Pygments run the following command from the command prompt:
    easy_install pygments

    Re-start Apache, login to ReviewBoard as the admin user, go to Admin -> Settings -> Diff Viewer -> Enable Syntax Highlighting. Click “Save”. Each existing user will need to enable syntax highlighting in their preferences. New users will be created with syntax highlighting enabled by default.

Create Site

The installation of RB is pretty much complete so now we need to set up a “site” and hook it into Apache to be served.

  1. Beginning Installation: run the rb-site command to create a new instance of the site. I used the command:
    rb-site install C:wwwrb

    but you have to be sure that the rb-site command’s directory C:Python26Scripts is in the PATH var (see above).

  2. It is also recommended to install mod_python for Apache. Find mod_python for Python 2.6 and install it. I found a package at I installed mod_python-3.3.2-dev-20080819.win32-py2.6.exe and it is working well. Note: Sorin Popa mentioned in the comments that you can also get the file from
  3. After the site is created, copy to contents of the C:wwwrbconfapache-modpython.conf file and append it to the Apache virtual host file C:Apache2.2confextrahttpd-vhosts.conf. I had to change the DocumentRoot and I also added some logging. See below my httpd-vhosts.conf:
    NameVirtualHost *:80
    <VirtualHost *:80>
      ServerName myHostName
      DocumentRoot "C:/www"
      # Error handlers
      ErrorDocument 500 /errordocs/500.html
      # Serve django pages
      <Location "/rb/">
        PythonPath "['C:/www/rb/conf'] + sys.path"
        SetEnv DJANGO_SETTINGS_MODULE reviewboard.settings
        SetEnv PYTHON_EGG_CACHE "C:/www/rb/tmp/egg_cache"
        SetHandler mod_python
        PythonHandler django.core.handlers.modpython
        PythonAutoReload Off
        PythonDebug Off
        # Used to run multiple mod_python sites in the same apache
        PythonInterpreter reviewboard_rb
      # Serve static media without running it through mod_python
      # (overrides the above)
      <Location "/rb/media">
        SetHandler None
      <Location "/rb/errordocs">
        SetHandler None
      <Directory "C:/www/rb/htdocs">
        AllowOverride All
      # Alias static media requests to filesystem
      Alias /rb/media "C:/www/rb/htdocs/media"
      Alias /rb/errordocs "C:/www/rb/htdocs/errordocs"
      ErrorLog "C:/logs/apache/reviewboard-error.log"
      CustomLog "C:/logs/apache/reviewboard-access.log" common
  4. After a restart of Apache you should be able to access RB in a browser (http://myHostName/rb)


I set up email support so that reviewers can be notified of new reviews or comments. Visit http://myHostName/rb/admin/settings/email/ and set:

  • Mail Server: mailhost
  • Port: 25

Logging can also be set in http://myHostName/rb/admin/settings/logging/:

  • Log directory: C:logsreviewboard

Active Directory

I also installed the optional LDAP module python-ldap-2.3.11.win32-py2.6.msi so that I could enable AD user connections. In the RB Admin console (http://myHostName/rb/admin/settings/general/) select:

  • Advanced Authentication
    • Authentication Method: Active Directory
  • Active Directory Authentication Settings
    • Domain name: your domain
    • Domain controller: host name of AD and domain above (FQDN)
    • OU name: OU of user accounts
    • Recursion Depth: -1

At this point we were able to log in using our City network accounts. If the Administrator set up groups then the user can select their own groups when the sign in for the first time.


There is a nifty tool from Codeplex (hosted by Microsoft) called SVNBridge that allows you set a service that will accept SVN commands but forwards them as TFS commands to the TFS server. This tool is useful to us as ReviewBoard does not support TFS out of the box but does support SVN. For the SVNBridge Server installation, I had to deviate a bit from their instructions as WinXp uses IIS 5.1 and not the recommended 6 or 7 version:

  1. Ensure that IIS 5.1 and ASP 2.0.50727 are installed.
  2. Get the Server version of SVNBridge and extract it to C:SvnBridge.
  3. For the most part we can follow the instructions for SvnBridge Server as if installing on IIS 6.
  4. Change the Web.config file to something similar to:
    <?xml version="1.0" encoding="UTF-8"?>
        <add key="TfsUrl" value="http://cmap041:8080" />
        <add key="LogPath" value="C:SvnBridgeLogs" />
        <httpRuntime maxRequestLength="500000" /> <!-- 250+ MB -->
        <customErrors mode="Off" />
        <compilation debug="true" />
        <authentication mode="Windows" />
        <identity impersonate="true" />
        <!-- Uncomment this httpHandlers element if running under IIS 6.0 / 5.1 -->
          <add verb="*" path="*" type="SvnBridgeServer.SvnBridgeHttpHandler, SvnBridgeServer" />
        <defaultProxy enabled="true" />
      <!-- IIS 7.0 -->
        <validation validateIntegratedModeConfiguration="false" />
          <remove name="ServiceModel" />
          <add name="SvnBridgeHandler"
            preCondition="integratedMode" />
            <requestLimits maxAllowedContentLength="262144000" /> <!-- 250 MB -->
  5. Using IIS Manager, create a new website (or rename the default as I did) called “SvnBridge”:
    1. Web Site tab: Change the TCP port to 8081.
    2. Home Directoy tab: Local path is the folder where you unzipped the contents of (ex C:SvnBridge). Also, click the Configuration button, and click the Add button to add a new wildcard application map. Specify “c:Windowsmicrosoft.netframeworkv2.0.50727aspnet_isapi.dll” as the executable, set the extension to “.*”, set All Verbs, and UNCHECK “Check that file exists” checkbox.
    3. ASP.NET tab: make sure the version is set to 2.0.50727
    4. Directory Security tab: click the Edit button for “Anonymous access and authentication control”, and make sure only Basic authentication is checked using your domain as both the Default domain and Realm. (Note: this deviates from the instructions. I couldn’t get it to work with Digest).
  6. Restart the IIS server.
  7. If you have SVN installed you can run the command to list the projects:
    svn ls http://myHostName:8081/projA

    This will connect to the TFS server and list everything in the projA project. If you see output then the connector is working correctly.

Link to TFS

  1. To set up a repository, visit http://myHostName/rb/admin/db/scmtools/repository/ and select “Add Repository”.
  2. Set the following:
    1. General Information
      1. Name: TFS
      2. Path: http://myHostName:8081
      3. Repository Type: Subversion
      4. Bug tracker URL: http://myBugzillaServer/cgi-bin/bugzilla/show_bug.cgi?id=%s
    2. Authentication
      1. Username: choose a user that has full access TFS
      2. Password: password
  3. This will connect into the SVNBridge and return the TFS content even though ReviewBoard thinks it is SVN. Woohoo 🙂

Post-Review tool to create reviews automatically

Visit the post-review site or the dev doc version. I created a php script to auto-create reviews to make it easy for our developers. The root of the web server (http://myHostName) will display a script which allows a user to simply enter their name and the TFS changeset. The script then calls post-review on the server to retrieve the diff and create the review for the user.

  1. Using the admin console (http://myHostName/rb/admin/db/auth/user/), create a user “Poster” and give them the permissions:
    1. reviews | review request | Can submit as user
    2. reviews | review request | Can edit review request

    This will allow the Poster user to be used in the script to create requests on behalf of another user.

  2. Create default.css on the root of the webserver:
    div.row {
      clear: both;
      padding-top: 10px;
    div.row span.label {
      float: left;
      width: 75px;
      text-align: right;
    div.row span.formw {
      float: right;
      width: 510px;
      text-align: left;
      width: 600px;
      background-color: #D3D3D3;
      border: 1px dotted #333;
      padding: 5px;
      margin: 5px
    div.spacer {
      clear: both;
  3. Create index.html on the root of the webserver:
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "">
    <html xmlns="" xml:lang="en" lang="en">
      <title>Create Review | Review Board</title>
      <link rel="icon" type="image/png" href="/rb/media/rb/images/favicon.png" />
      <link rel="stylesheet" type="text/css" href="/rb/media/rb/css/common.css" />
      <link rel="stylesheet" type="text/css" href="default.css" />
      <script type="text/javascript">
        var MEDIA_URL = "/rb/media/";
      <!--[if lt IE 7.]>
      <style type="text/css">
        body {
          behavior: url("/rb/media/rb/js/");
        img {
          behavior: url("/rb/media/rb/js/");
      <!--[if lt IE 8.]>
      <link rel="stylesheet" type="text/css" href="/rb/media/rb/css/ie_hacks.css"></script>
      <div id="container">
        <br />
        <div id="rbinfo">
          <img id="logo" src="/rb/media/rb/images/logo.png" />
          <h1 id="title">Review Board</h1>
        <br />
        <form action="createReview.php" method="post">
          <h2>Required Fields</h2>
          <div class="borderBox">
            <div class="row">
              <span class="label">Name:</span>
              <span class="formw">
                <select name="yourname">
                  <option value=""></option>
                  <option value="dev1">dev1</option>
                  <option value="dev2">dev2</option>
                  <option value="dev3">dev3</option>
                </select><br />
                <i>(Your Windows network login without the domain; eg: pinetti, zhuma, patchmi, etc)</i>
            <div class="row">
              <span class="label">Changeset:</span>
              <span class="formw">
                <div id="divChangeSetLow" style="display:none">
                  <input type="text" name="changesetLow" /> to
                <input type="text" name="changesetHi" />
                <input type="checkbox" name="modtype" value="Range" onclick="showMe('divChangeSetLow', 'inline', this)" />Range<br />
                <i>(The changeset submitted to TFS that you want to be reviewed. Number only.)</i>
            <div class="row">
              <span class="label">TFS Path:</span>
              <span class="formw">
                <input type="text" name="tfsPath" /><br />
                <i>(Sets the TFS path for the project. For example, if work was done in TFS on project $/projA/trunk/ then this value should be "projA/trunk/".)</i>
            <div class="spacer">
          <h2><input type="checkbox" name="modtype" value="Optional" onclick="showMe('divOptional', 'block', this)" />Optional Fields</h2>
          <div class="borderBox" id="divOptional" style="display:none">
            <div class="row">
              <span class="label">Summary:</span>
              <span class="formw">
                <input type="text" name="summary" /><br />
                <i>(Sets the review request's summary field to the specified text.)</i>
            <div class="row">
              <span class="label">Branch:</span>
              <span class="formw">
                <input type="text" name="branch" /><br />
                <i>(Sets the review request's branch field to the specified text.)</i>
            <div class="row">
              <span class="label">Bugs:</span>
              <span class="formw">
                <input type="text" name="bugs" /><br />
                <i>(Specifies a list of bug numbers to use for the Bugs Closed section in the review request. This should be a comma-separated list. Check our <a href="http://dc01app02ps/cgi-bin/bugzilla/index.cgi">Bugzilla page</a> for the list of bugs.)</i>
            <div class="row">
              <span class="label">Groups:</span>
              <span class="formw">
                <input type="text" name="groups" /><br />
                <i>(Provides a list of groups that should be on the reviewer list. This should be a comma-separated list.)</i>
            <div class="row">
              <span class="label">People:</span>
              <span class="formw">
                <input type="text" name="people" /><br />
                <i>(Provides a list of usernames that should be on the reviewer list. This should be a comma-separated list.)</i>
            <div class="row">
              <span class="label">Description:</span>
              <span class="formw">
                <textarea cols="40" rows="4" name="description" ></textarea><br />
                <i>(Sets the description for the review request to the specified text.)</i>
            <div class="row">
              <span class="label">Testing:</span>
              <span class="formw">
                <textarea cols="40" rows="4" name="testing" ></textarea><br />
                <i>(Sets the testing done text for the review request to the specified text.)</i>
            <div class="spacer">
          <p><input type="submit" value="Create review"></p>
      <script type="text/javascript">
        function showMe (it, displayStyle, box) {
          var vis = (box.checked) ? displayStyle : "none";
          document.getElementById(it).style.display = vis;
  4. Create createReview.php on the root of the webserver:
    set_time_limit(200); //200 seconds
    $TFS_SERVER_URL = "http://myHostName:8081/";
    $RB_SERVER_URL = "http://myHostName/rb";
    $POSTER_USER = "poster";
    $POSTER_PASS = "p@ssw0rd";
    print("<h1>Debug ReviewBoard post-review command</h1>");
    $yourname    = check_input($_POST['yourname'], "***Required field missing: You must enter your name.***");
    $changesetHi    = check_input($_POST['changesetHi'], "***Required field missing: You must enter a changest number.***");
    check_number($changesetHi, "***Invalid parameter: The changest value must be a valid number.***");
    $changesetLow = check_input($_POST['changesetLow']);
    if (empty($changesetLow)){
      $changesetLow = $changesetHi - 1;
    } else {
      check_number($changesetLow, "***Invalid parameter: The lower bound of the changest range value must be a valid number.***");
      if ($changesetLow >= $changesetHi){
        show_error("***Invalid parameter: The lower bound of the changest range value must be lower than the higher bound.***");
    $tfsPath    = check_input($_POST['tfsPath'], "***Required field missing: You must enter the TFS Project Path.***");
    $summary    = check_input("TFS ".$changesetLow.":".$changesetHi." -- ".$_POST['summary']);
    $branch        = check_input($_POST['branch']);
    $bugs         = check_input($_POST['bugs']);
    $groups        = check_input($_POST['groups']);
    $people        = check_input($_POST['people']);
    $description= check_input($_POST['description']);
    $testing    = check_input($_POST['testing']);
    $command = "post-review --repository-url="$TFS_SERVER_URL$tfsPath" --server="$RB_SERVER_URL" --username="$POSTER_USER" --password="$POSTER_PASS"";
    $command .= " --submit-as="$yourname" --revision-range="$changesetLow:$changesetHi"";
    $command .= " --summary="$summary" --branch="$branch" --bugs-closed="$bugs" --target-groups="$groups"";
    $command .= " --target-people="$people" --description="$description" --testing-done="$testing"";
    if (!$isDEBUG) {
      $command .= " --debug";
    print("<h2>Compiled command</h2>".$command);
    $output = shell_exec($command);
    print("<h2>Command Output</h2>".$output);
    $pos = strripos($output, $RB_SERVER_URL."/r/");
    if ($pos === false) {
      show_error("The review was not created correctly.");
    } else {
      $newReviewURL = substr($output, $pos);
      print("<h2><span style="color:green;">Success</span></h2>");
      print("New review created: <a href="".$newReviewURL."">$newReviewURL</a>");
      if (!$isDEBUG) {
       header("Location: $newReviewURL") ; //redirect to new review page
    function check_input($data, $problem='') {
      $data = trim($data);
      $data = stripslashes($data);
      $data = htmlspecialchars($data);
      if ($problem && strlen($data) == 0) {
      return $data;
    function check_number($data, $problem='') {
      if (preg_match("/D/",$data)) {
    function show_error($myError) {
      print("<h2><span style="color:red;">Failure</span></h2>");
      print("<p>Press the back button and correct the error</p>");

Change the “diff” behaviour

Due to the way that we have TFS running through SVNBridge and ReviewBoard thinks it is calling a SVN repo, we have encountered an error with binary files. Reviewboard has coded into post-review to use:

svn diff --diff-cmd=diff

which tells SVN diff to use the external tool “diff” which is assumed to be GNU diffutils. SVN diff will only accept exit codes of 0/same and 1/different (Ref: However, GNU diffutils will return exit codes 0/same, 1/different, and 2/trouble (Ref: An error occurs when a binary file (ex: image) is passed to the diff tool. The ReviewBoard log will spit out:

'Index: Reports/toolbar/upd.gifn','===================================================================n',
'Files Reports/toolbar/ 0) and Reports/toolbar/ 17489) differn',
"svn: 'diff' returned 2n",'svn:Error reading spooled REPORT request responsen']

indicating that GNU diff returned 2 meaning “trouble”. The trouble occurs because SVN should not be trying to diff binary files. SVN determines if a file is binary by checking the SVN property svn:mime-type to see if the file is binary or not. According to, when a file is checked into a SVN repo it is checked for mime and then the svn:mime-type is assigned. When a subsequent SVN diff is called, it will check the svn:mime-type property to see if it is text or binary. If no property is set, then SVN diff assumes that the file is text. Since our files are checked into TFS, there are no SVN properties assigned and so when a binary file is retrieved through SVNBridge, there is also no property. This is why SVN treats a binary as text and this is why we are getting errors.

To fix this I had to modify post-review. Since C:Python26Scriptspost-review.exe is a compiled executable, I downloaded rbtools 0.2 stable from, made my changes to, and reinstalled the package using the command:

python install

at the rbtool root. In I changed the code snippet:

["svn", "diff", "--diff-cmd=diff"]


["svn", "diff", "--diff-cmd=C:\GnuWin32\bin\diffForceText.bat"]

in 3 places in the file and reinstalled (using above python command). I created the wrapper script (DOS batch) diffForceText.bat and put it in to C:GnuWin32bin and it simply includes:

@echo off
diff %* --text

This wrapper essentially passes all parameters onto GNU diff but also adds the –text parameter which will force diff to read a binary file as text ( This will appear a little garbled in ReviewBoard but it does avoid the above error, allow the review to be created and let the reviewer know that a binary file has changed. This is more than good enough for us.

I opened a couple bugs/discussions on the topic of the diff issue:


This whole solution would have been so much easier if we were using Git or Subversion but I was only able to play with the environment that we already had in place. I am extremely happy with the outcome and feature set that ReviewBoard provides. I have demoed this solution to the other developers and management and they are all surprised by how well the solution performs.

If you want an open source product that surpasses Crucible/CodeCollaborator and has an active community, then try ReviewBoard and, for the love of god, help to get it working natively with TFS!

33 thoughts on “Code review goodness with ReviewBoard

  1. Thank you so much for posting this, it’s helped me greatly in setting up both SVNBridge and the script giving us a stop-gap measure to post-commit reviews until I can integrate with TFS work items. Great work 🙂

  2. @Ian

    My pleasure. I am hoping that this helps out as many people as possible to get the TFS crowd onto this great code review tool.

    It would be even better to get TFS working natively with ReviewBoard without the SVNBridge step.

    *wink to some eager dev*

  3. @Allan

    I originally investigated teamreview however I dont like being tied to an IDE, and in teamreview’s case, Visual Studio. Our apps are all Java/PERL/PHP so this did not work for us as Eclipse IDE users.

  4. Hi Tim,
    I am trying to use reviewboard 1.5 and I can’t seem to get past step 3.
    I am using your httpd-vhosts.conf in
    C:Documents and SettingsarazaMy Documentsbackupapacheconfextra

    I even tried reviewboard documentation
    “If you do not have a sites-available or sites-enabled directory, you’ll need to embed the configuration file in your global Apache configuration file (usually /etc/httpd/httpd.conf or /etc/httpd/apache2.conf).”

    If I go to http://localhost/rb I get
    oops! This links appears to be broken on my chrome browser

    The apache error.log has
    [Wed Dec 08 17:25:33 2010] [notice] Parent: Created child process 3916
    [Wed Dec 08 17:25:33 2010] [notice] Child 3916: Child process is running
    [Wed Dec 08 17:25:33 2010] [notice] Child 3916: Acquired the start mutex.
    [Wed Dec 08 17:25:33 2010] [notice] Child 3916: Starting 64 worker threads.
    [Wed Dec 08 17:25:33 2010] [notice] Child 3916: Starting thread to listen on port 80.
    [Wed Dec 08 17:25:43 2010] [error] [client] File does not exist: C:/Program Files/Apache Software Foundation/Apache2.2/htdocs/reviewboard
    [Wed Dec 08 17:25:43 2010] [error] [client] File does not exist: C:/Program Files/Apache Software Foundation/Apache2.2/htdocs/favicon.ico
    [Wed Dec 08 17:25:44 2010] [error] [client] File does not exist: C:/Program Files/Apache Software Foundation/Apache2.2/htdocs/favicon.ico
    [Wed Dec 08 17:25:46 2010] [error] [client] File does not exist: C:/Program Files/Apache Software Foundation/Apache2.2/htdocs/rb
    [Wed Dec 08 17:25:46 2010] [error] [client] File does not exist: C:/Program Files/Apache Software Foundation/Apache2.2/htdocs/favicon.ico

    Any idea what I am doing wrong. I couldn’t find the files above even in this folder


    • @Ali

      First of all the sites-availabe and sites-enabled is a Linux/Ubuntu environment specific setup with Apache and I can see you are trying this on Windows.

      Next, do not use my httpd-vhosts.conf…you have to use the one that reviewboard auto creates for you after you enter all of the parameters via the CLI during installation. Take YOUR file and use it as is. If that doesnt work then try changing the DocumentRoot as I did.

      As for the errors in the apache log, those look pretty normal to me except that in your httpd-vhosts.conf comment you mentioned C:Documents and SettingsarazaMy Documentsbackupapacheconfextra
      and then the apache error log mentions C:/Program Files/Apache Software Foundation/Apache2.2/. Where are you modifying your virtual hosts?

      Also in the error log I see:
      C:/Program Files/Apache Software Foundation/Apache2.2/htdocs/reviewboard
      C:/Program Files/Apache Software Foundation/Apache2.2/htdocs/rb
      Did you install twice? What is the correct path to your reviewboard instance?

  5. Hi Tim,
    Thanks for the feedback.
    Still some problems. I couldn’t get the ditto file to work so I changed the document root (still no luck).

    I only changed two lines from the autogenerated one
    ServerName reviewboard
    DocumentRoot “C:/www/rb/htdocs”

    Here is what I have currently.

    ServerName localhost
    DocumentRoot “C:/www”

    # Error handlers
    ErrorDocument 500 /errordocs/500.html

    # Serve django pages

    PythonPath “[‘C:/www/rb/conf’] + sys.path”
    SetEnv DJANGO_SETTINGS_MODULE reviewboard.settings
    SetEnv PYTHON_EGG_CACHE “C:/www/rb/tmp/egg_cache”
    SetEnv HOME “C:/www/rb/data”
    SetHandler mod_python
    PythonHandler django.core.handlers.modpython
    PythonAutoReload Off
    PythonDebug Off
    # Used to run multiple mod_python sites in the same apache
    PythonInterpreter reviewboard_rb

    # Serve static media without running it through mod_python
    # (overrides the above)

    SetHandler None

    SetHandler None

    AllowOverride All

    # Alias static media requests to filesystem
    Alias /media “C:/www/rb/htdocs/media”
    Alias /errordocs “C:/www/rb/htdocs/errordocs”

    the reason you probably see duplicate entries is because i tried to http://localhost/rb and then I thought I maybe it was http://localhost/reviewboard

    I did it again and it shows

    [Wed Dec 08 19:55:15 2010] [error] [client] File does not exist: C:/Program Files/Apache Software Foundation/Apache2.2/htdocs/rb
    [Wed Dec 08 19:55:15 2010] [error] [client] File does not exist: C:/Program Files/Apache Software Foundation/Apache2.2/htdocs/favicon.ico

    The path to my installation is

    did you install 1.5 or 1.0 ?

    • @ Ali

      I have installed both 1.5 and 1.0.9 and the same instructions worked for each version.

      I am assuming that all of the VirtualHost, Location, and Directory tags were stripped in the WordPress editor for your Vhosts file that you copied in.

      The fact that you still see:
      * File does not exist: C:/Program Files/Apache Software Foundation/Apache2.2/htdocs/rb
      means to me that your Apache Vhost isnt set to look in:
      * C:wwwrb
      like you want it to.

      Are you sure that after installation you copied the text in C:wwwrbconfapache-modpython.conf file and appended it to the Apache virtual host file C:Apache2.2confextrahttpd-vhosts.conf, edited C:Apache2.2confhttpd.conf and uncomment the include of the vhost file near the bottom of the file:

      # Virtual hosts
      Include conf/extra/httpd-vhosts.conf

      Save all files and restart Apache.

  6. Hi Tim,
    Thanks for all your help. I now have reviewBoard up and running.
    I have one more question. In your tutorial I think you have reviewboard and TFS on the same server. Hence, you can use Path: http://myHostName:8081 when specifying a repository. This suggestion was rejected by our TFS admin but I have able to deploy svnbridge server on the tfs server.

    I am running reviewboard on a separate system. My question is how can I configure apache/reviewboard so the repository path maps to my tfs installation. I have verified that I can use svn via the svn bridge on the reviewboard server to see tfs source.


    • @ Ali

      That is great news that you got it up and running. My setup is that reviewboard and the SVNBridge are running on the same server and the true TFS is off somewhere else. The SVNBridge is pointing to the TFS server obviously and the last thing you need to do is to log in to your Reviewboard as ‘admin’, go in to the Repositories section and add the URL to the SVNBridge which can be on a different server from your reviewboard server. Follow ReviewBoard’s documentation for adding a new repository and make sure to select the Type: Subversion.

      Let me know if there is anything else.

  7. Hi Tim,
    Making good progress. I did all of your other steps. I am little confused about the last few.

    I downloaded rbtools, changed the source, and then did python install

    Towards the end, you mention
    I created the wrapper script (DOS batch) diffForceText.bat to simply include:

    @echo off
    diff %* –text

    I am not sure how this applies to what I am doing. So once I create this batch file, what do I do with it ? do i place it somewhere ?



  8. @ Ali

    The script was my way around SVNBridge returning a binary file to the svn diff command causing failure. This script simply tells the diff tool to think of everything as a text file even if it is passed a binary. This is an SVNBridge problem in that it doesnt properly convert the TFS parameter Binary to SVN parameter Binary as stated above. If you do not work with binary files (images, jars, etc) then this can be ignored.

  9. Pingback: Migrate TFS to SVN | tim.pinet's Blog

  10. Hi,
    I followed the steps mentioned in the blog to install reviewboard with apache. I am not able to see the reviewboard. I can see only the directory listings under Document Root.

    I am attaching the httpd.conf, httpd-vhosts.conf and apache-modpython.conf.


    # This is the main Apache HTTP server configuration file. It contains the
    # configuration directives that give the server its instructions.
    # See for detailed information.
    # In particular, see
    # for a discussion of each configuration directive.
    # Do NOT simply read the instructions in here without understanding
    # what they do. They’re here only as hints or reminders. If you are unsure
    # consult the online docs. You have been warned.
    # Configuration and logfile names: If the filenames you specify for many
    # of the server’s control files begin with “/” (or “drive:/” for Win32), the
    # server will use that explicit path. If the filenames do *not* begin
    # with “/”, the value of ServerRoot is prepended — so “logs/foo.log”
    # with ServerRoot set to “C:/Program Files/Apache Software Foundation/Apache2.2” will be interpreted by the
    # server as “C:/Program Files/Apache Software Foundation/Apache2.2/logs/foo.log”.
    # NOTE: Where filenames are specified, you must use forward slashes
    # instead of backslashes (e.g., “c:/apache” instead of “c:apache”).
    # If a drive letter is omitted, the drive on which httpd.exe is located
    # will be used by default. It is recommended that you always supply
    # an explicit drive letter in absolute paths to avoid confusion.

    # ServerRoot: The top of the directory tree under which the server’s
    # configuration, error, and log files are kept.
    # Do not add a slash at the end of the directory path. If you point
    # ServerRoot at a non-local disk, be sure to point the LockFile directive
    # at a local disk. If you wish to share the same ServerRoot for multiple
    # httpd daemons, you will need to change at least LockFile and PidFile.
    ServerRoot “C:/Program Files/Apache Software Foundation/Apache2.2”

    # Listen: Allows you to bind Apache to specific IP addresses and/or
    # ports, instead of the default. See also the
    # directive.
    # Change this to Listen on specific IP addresses as shown below to
    # prevent Apache from glomming onto all bound IP addresses.
    Listen 8080
    #Listen 8082
    # Dynamic Shared Object (DSO) Support
    # To be able to use the functionality of a module which was built as a DSO you
    # have to place corresponding `LoadModule’ lines at this location so the
    # directives contained in it are actually available _before_ they are used.
    # Statically compiled modules (those listed by `httpd -l’) do not need
    # to be loaded here.
    # Example:
    # LoadModule foo_module modules/
    LoadModule actions_module modules/
    LoadModule alias_module modules/
    LoadModule asis_module modules/
    LoadModule auth_basic_module modules/
    #LoadModule auth_digest_module modules/
    #LoadModule authn_alias_module modules/
    #LoadModule authn_anon_module modules/
    #LoadModule authn_dbd_module modules/
    #LoadModule authn_dbm_module modules/
    LoadModule authn_default_module modules/
    LoadModule authn_file_module modules/
    #LoadModule authnz_ldap_module modules/
    #LoadModule authz_dbm_module modules/
    LoadModule authz_default_module modules/
    LoadModule authz_groupfile_module modules/
    LoadModule authz_host_module modules/
    #LoadModule authz_owner_module modules/
    LoadModule authz_user_module modules/
    LoadModule autoindex_module modules/
    #LoadModule cache_module modules/
    #LoadModule cern_meta_module modules/
    LoadModule cgi_module modules/
    #LoadModule charset_lite_module modules/
    #LoadModule dav_module modules/
    #LoadModule dav_fs_module modules/
    #LoadModule dav_lock_module modules/
    #LoadModule dbd_module modules/
    #LoadModule deflate_module modules/
    LoadModule dir_module modules/
    #LoadModule disk_cache_module modules/
    #LoadModule dumpio_module modules/
    LoadModule env_module modules/
    #LoadModule expires_module modules/
    #LoadModule ext_filter_module modules/
    #LoadModule file_cache_module modules/
    #LoadModule filter_module modules/
    #LoadModule headers_module modules/
    #LoadModule ident_module modules/
    #LoadModule imagemap_module modules/
    LoadModule include_module modules/
    #LoadModule info_module modules/
    LoadModule isapi_module modules/
    #LoadModule ldap_module modules/
    #LoadModule logio_module modules/
    LoadModule log_config_module modules/
    #LoadModule log_forensic_module modules/
    #LoadModule mem_cache_module modules/
    LoadModule mime_module modules/
    #LoadModule mime_magic_module modules/
    LoadModule negotiation_module modules/
    #LoadModule proxy_module modules/
    #LoadModule proxy_ajp_module modules/
    #LoadModule proxy_balancer_module modules/
    #LoadModule proxy_connect_module modules/
    #LoadModule proxy_ftp_module modules/
    #LoadModule proxy_http_module modules/
    #LoadModule proxy_scgi_module modules/
    #LoadModule reqtimeout_module modules/
    #LoadModule rewrite_module modules/
    LoadModule setenvif_module modules/
    #LoadModule speling_module modules/
    #LoadModule ssl_module modules/
    #LoadModule status_module modules/
    #LoadModule substitute_module modules/
    #LoadModule unique_id_module modules/
    #LoadModule userdir_module modules/
    #LoadModule usertrack_module modules/
    #LoadModule version_module modules/
    #LoadModule vhost_alias_module modules/

    LoadModule python_module modules/

    # If you wish httpd to run as a different user or group, you must run
    # httpd as root initially and it will switch.
    # User/Group: The name (or #number) of the user/group to run httpd as.
    # It is usually good practice to create a dedicated user and group for
    # running httpd, as with most system services.
    User daemon
    Group daemon

    # ‘Main’ server configuration
    # The directives in this section set up the values used by the ‘main’
    # server, which responds to any requests that aren’t handled by a
    # definition. These values also provide defaults for
    # any containers you may define later in the file.
    # All of these directives may appear inside containers,
    # in which case these default settings will be overridden for the
    # virtual host being defined.

    # ServerAdmin: Your address, where problems with the server should be
    # e-mailed. This address appears on some server-generated pages, such
    # as error documents. e.g.
    ServerAdmin naik

    # ServerName gives the name and port that the server uses to identify itself.
    # This can often be determined automatically, but we recommend you specify
    # it explicitly to prevent problems during startup.
    # If your host doesn’t have a registered DNS name, enter its IP address here.
    ServerName myhostname:8080

    # DocumentRoot: The directory out of which you will serve your
    # documents. By default, all requests are taken from this directory, but
    # symbolic links and aliases may be used to point to other locations.

    DocumentRoot “C:/Program Files/Apache Software Foundation/Apache2.2/htdocs”
    #DocumentRoot “C:/www/rb/htdocs”

    # Each directory to which Apache has access can be configured with respect
    # to which services and features are allowed and/or disabled in that
    # directory (and its subdirectories).
    # First, we configure the “default” to be a very restrictive set of
    # features.

    Options FollowSymLinks
    AllowOverride All
    Order deny,allow
    Allow from all

    # Note that from this point forward you must specifically allow
    # particular features to be enabled – so if something’s not working as
    # you might expect, make sure that you have specifically enabled it
    # below.

    # This should be changed to whatever you set DocumentRoot to.

    # Possible values for the Options directive are “None”, “All”,
    # or any combination of:
    # Indexes Includes FollowSymLinks SymLinksifOwnerMatch ExecCGI MultiViews
    # Note that “MultiViews” must be named *explicitly* — “Options All”
    # doesn’t give it to you.
    # The Options directive is both complicated and important. Please see
    # for more information.
    Options Indexes FollowSymLinks

    # AllowOverride controls what directives may be placed in .htaccess files.
    # It can be “All”, “None”, or any combination of the keywords:
    # Options FileInfo AuthConfig Limit
    AllowOverride ALL

    # Controls who can get stuff from this server.
    Order allow,deny
    Allow from all

    # DirectoryIndex: sets the file that Apache will serve if a directory
    # is requested.
    # DirectoryIndex index.html

    # The following lines prevent .htaccess and .htpasswd files from being
    # viewed by Web clients.

    Order allow,deny
    Deny from all
    Satisfy All

    # ErrorLog: The location of the error log file.
    # If you do not specify an ErrorLog directive within a
    # container, error messages relating to that virtual host will be
    # logged here. If you *do* define an error logfile for a
    # container, that host’s errors will be logged there and not here.
    ErrorLog “logs/error.log”

    # LogLevel: Control the number of messages logged to the error_log.
    # Possible values include: debug, info, notice, warn, error, crit,
    # alert, emerg.
    LogLevel warn

    # The following directives define some format nicknames for use with
    # a CustomLog directive (see below).
    LogFormat “%h %l %u %t “%r” %>s %b “%{Referer}i” “%{User-Agent}i”” combined
    LogFormat “%h %l %u %t “%r” %>s %b” common

    # You need to enable mod_logio.c to use %I and %O
    LogFormat “%h %l %u %t “%r” %>s %b “%{Referer}i” “%{User-Agent}i” %I %O” combinedio

    # The location and format of the access logfile (Common Logfile Format).
    # If you do not define any access logfiles within a
    # container, they will be logged here. Contrariwise, if you *do*
    # define per- access logfiles, transactions will be
    # logged therein and *not* in this file.
    CustomLog “logs/access.log” common

    # If you prefer a logfile with access, agent, and referer information
    # (Combined Logfile Format) you can use the following directive.
    #CustomLog “logs/access.log” combined

    # Redirect: Allows you to tell clients about documents that used to
    # exist in your server’s namespace, but do not anymore. The client
    # will make a new request for the document at its new location.
    # Example:
    # Redirect permanent /foo

    # Alias: Maps web paths into filesystem paths and is used to
    # access content that does not live under the DocumentRoot.
    # Example:
    # Alias /webpath /full/filesystem/path
    # If you include a trailing / on /webpath then the server will
    # require it to be present in the URL. You will also likely
    # need to provide a section to allow access to
    # the filesystem path.

    # ScriptAlias: This controls which directories contain server scripts.
    # ScriptAliases are essentially the same as Aliases, except that
    # documents in the target directory are treated as applications and
    # run by the server when requested rather than as documents sent to the
    # client. The same rules about trailing “/” apply to ScriptAlias
    # directives as to Alias.
    ScriptAlias /cgi-bin/ “C:/Program Files/Apache Software Foundation/Apache2.2/cgi-bin/”

    # ScriptSock: On threaded servers, designate the path to the UNIX
    # socket used to communicate with the CGI daemon of mod_cgid.
    #Scriptsock logs/cgisock

    # “C:/Program Files/Apache Software Foundation/Apache2.2/cgi-bin” should be changed to whatever your ScriptAliased
    # CGI directory exists, if you have that configured.

    AllowOverride None
    Options None
    Order allow,deny
    Allow from all

    # DefaultType: the default MIME type the server will use for a document
    # if it cannot otherwise determine one, such as from filename extensions.
    # If your server contains mostly text or HTML documents, “text/plain” is
    # a good value. If most of your content is binary, such as applications
    # or images, you may want to use “application/octet-stream” instead to
    # keep browsers from trying to display binary files as though they are
    # text.

    DefaultType application/octet-stream

    # TypesConfig points to the file containing the list of mappings from
    # filename extension to MIME-type.
    TypesConfig conf/mime.types

    # AddType allows you to add to or override the MIME configuration
    # file specified in TypesConfig for specific file types.
    #AddType application/x-gzip .tgz
    # AddEncoding allows you to have certain browsers uncompress
    # information on the fly. Note: Not all browsers support this.
    #AddEncoding x-compress .Z
    #AddEncoding x-gzip .gz .tgz
    # If the AddEncoding directives above are commented-out, then you
    # probably should define those extensions to indicate media types:
    AddType application/x-compress .Z
    AddType application/x-gzip .gz .tgz

    # AddHandler allows you to map certain file extensions to “handlers”:
    # actions unrelated to filetype. These can be either built into the server
    # or added with the Action directive (see below)
    # To use CGI scripts outside of ScriptAliased directories:
    # (You will also need to add “ExecCGI” to the “Options” directive.)
    #AddHandler cgi-script .cgi

    # For type maps (negotiated resources):
    #AddHandler type-map var

    # Filters allow you to process content before it is sent to the client.
    # To parse .shtml files for server-side includes (SSI):
    # (You will also need to add “Includes” to the “Options” directive.)
    #AddType text/html .shtml
    #AddOutputFilter INCLUDES .shtml

    # The mod_mime_magic module allows the server to use various hints from the
    # contents of the file itself to determine its type. The MIMEMagicFile
    # directive tells the module where the hint definitions are located.
    #MIMEMagicFile conf/magic

    # Customizable error responses come in three flavors:
    # 1) plain text 2) local redirects 3) external redirects
    # Some examples:
    #ErrorDocument 500 “The server made a boo boo.”
    #ErrorDocument 404 /missing.html
    #ErrorDocument 404 “/cgi-bin/”
    #ErrorDocument 402

    # EnableMMAP and EnableSendfile: On systems that support it,
    # memory-mapping or the sendfile syscall is used to deliver
    # files. This usually improves server performance, but must
    # be turned off when serving from networked-mounted
    # filesystems or if support for these functions is otherwise
    # broken on your system.
    #EnableMMAP off
    #EnableSendfile off

    # Supplemental configuration
    # The configuration files in the conf/extra/ directory can be
    # included to add extra features or to modify the default configuration of
    # the server, or you may simply copy their contents here and change as
    # necessary.

    # Server-pool management (MPM specific)
    #Include conf/extra/httpd-mpm.conf

    # Multi-language error messages
    #Include conf/extra/httpd-multilang-errordoc.conf

    # Fancy directory listings
    #Include conf/extra/httpd-autoindex.conf

    # Language settings
    #Include conf/extra/httpd-languages.conf

    # User home directories
    #Include conf/extra/httpd-userdir.conf

    # Real-time info on requests and configuration
    #Include conf/extra/httpd-info.conf

    # Virtual hosts
    Include conf/extra/httpd-vhosts.conf

    # Local access to the Apache HTTP Server Manual
    #Include conf/extra/httpd-manual.conf

    # Distributed authoring and versioning (WebDAV)
    #Include conf/extra/httpd-dav.conf

    # Various default settings
    #Include conf/extra/httpd-default.conf

    # Secure (SSL/TLS) connections
    #Include conf/extra/httpd-ssl.conf
    # Note: The following must must be present to support
    # starting without SSL on platforms with no /dev/random equivalent
    # but a statically compiled-in mod_ssl.

    #Include “C:/CLM/review/conf/apache-modpython.conf”

    SSLRandomSeed startup builtin
    SSLRandomSeed connect builtin


    # Virtual Hosts
    # If you want to maintain multiple domains/hostnames on your
    # machine you can setup VirtualHost containers for them. Most configurations
    # use only name-based virtual hosts so the server doesn’t need to worry about
    # IP addresses. This is indicated by the asterisks in the directives below.
    # Please see the documentation at
    # for further details before you try to setup virtual hosts.

    ServerName myhostname:8080

    DocumentRoot “c:/www”

    # Error handlers

    ErrorDocument 500 /errordocs/500.html

    # Serve django pages

    PythonPath “[‘c:/www/rb/conf’] + sys.path”

    SetEnv DJANGO_SETTINGS_MODULE reviewboard.settings

    SetEnv PYTHON_EGG_CACHE “c:/www/rb/tmp/egg_cache”

    SetEnv HOME “c:/www/rb/data”

    SetHandler mod_python

    PythonHandler django.core.handlers.modpython

    PythonAutoReload Off

    PythonDebug Off

    # Used to run multiple mod_python sites in the same apache

    PythonInterpreter reviewboard_rb

    # Serve static media without running it through mod_python

    # (overrides the above)

    SetHandler None

    SetHandler None

    AllowOverride All

    # Alias static media requests to filesystem

    Alias /media “c:/www/rb/htdocs/media”

    Alias /errordocs “c:/www/rb/htdocs/errordocs”


    ServerName myhostname:8080

    DocumentRoot “c:/www/rb/htdocs”

    # Error handlers

    ErrorDocument 500 /errordocs/500.html

    # Serve django pages

    PythonPath “[‘c:/www/rb/conf’] + sys.path”

    SetEnv DJANGO_SETTINGS_MODULE reviewboard.settings

    SetEnv PYTHON_EGG_CACHE “c:/www/rb/tmp/egg_cache”

    SetEnv HOME “c:/www/rb/data”

    SetHandler mod_python

    PythonHandler django.core.handlers.modpython

    PythonAutoReload Off

    PythonDebug Off

    # Used to run multiple mod_python sites in the same apache

    PythonInterpreter reviewboard_rb

    # Serve static media without running it through mod_python

    # (overrides the above)

    SetHandler None

    SetHandler None

    AllowOverride All

    # Alias static media requests to filesystem

    Alias /media “c:/www/rb/htdocs/media”

    Alias /errordocs “c:/www/rb/htdocs/errordocs”

    Any help would be greatly appreciated.

    Thanks in advance,
    Mahendra Naik

  11. Hey Tim… thank you man! I don’t think people bother to say this enough to you man. I haven’t tried to follow your steps yet, but if I run into some trouble I’ll be back.

    So long friend!

  12. Hi,

    Finally I was able to setup reviewboard. It works like a charm now 🙂 Facing problems with post-review now. I get the following error after post-review.

    >>> HTTP POSTing to {‘basedir’: ‘/clm/client/ClmClient.c’}
    >>> Got API Error 207 (HTTP code 200): The file was not found in the repository
    >>> Error data: {‘deprecated’: {‘in_version’: ‘1.5’}, ‘stat’: ‘fail’, ‘file’: ‘/trunk/pltf-repository/clm/client/ClmClient.c/ClmClient.c’, ‘err’: {‘msg’: ‘The file was not found i
    the repository’, ‘code’: 207}, ‘revision’: ‘2471’}

    Error uploading diff

    Your review request still exists, but the diff is not attached.

    Any help would be greatly appreciated.

    Thanks and Regards,
    Mahendra Naik

  13. Hi,

    I wanted to integrate cgi script with reviewboard. I have added the entry

    Buts its giving error while opening the browser

    Request Method: GET
    Request URL: http:///cgi-bin/dashboard

    Using the URLconf defined in djblets.util.rooturl, Django tried these URL patterns, in this order:

    ^ ^admin/
    ^ ^media/(?P.*)$
    ^ ^account/
    ^ ^api/
    ^ ^r/
    ^ ^reports/
    ^ ^dashboard/$
    ^ ^users/$
    ^ ^users/(?P[A-Za-z0-9@_-.]+)/$
    ^ ^groups/$
    ^ ^groups/(?P[A-Za-z0-9_-]+)/$
    ^ ^groups/(?P[A-Za-z0-9_-]+)/members/$
    ^ ^feeds/rss/(?P.*)/$
    ^ ^feeds/atom/(?P.*)/$
    ^ ^account/logout/$
    ^ ^$
    ^ ^iphone/

    The current URL, cgi-bin/dashboard, didn’t match any of these.

    Please let me know if i am missing any thing here, I am using Linux OS

  14. Pingback: TFS (2010) to SVN using a modified tfs2svn | tim.pinet's Blog

  15. Hi Tim,

    thanks for the detailled explanation of the installation process.
    One thing I currently do not unterstand is how you use ReviewBoard as the person who wants to initiate a review for a set of files stored in TFS. How do you tell ReviewBoard to “send a review for all files in TFS folder $/MyProject/Branch_v1/MyApp/MyModule to user xyz”? I have found no way in the ReviewBoard GUI to do that. When I use “New Review Request” and enter the name of the TFS branch, the created review simply is empty and no files are displayed anywhere. Am I overlooking something?

    – Frank

  16. Pingback: Process of Improving Software Quality « Fransiscus Setiawan

  17. great post tim –
    have you been keeping this deployment running for the last two years?
    are there any updates that you might want to share?

  18. This was extremely helpful in setting up ReviewBoard to work with TFS! I had to perform a number of additional steps, though, to make it work on our configuration. Some points of interest:
    1. SvnBridge does NOT necessarily have to be installed on TFS App tier. Just specify a proper TFS URL in its config file and it’ll work just fine. However, I found it necessary to run SvnBridge under a domain account that has read-only permissions to TFS.
    2. I am running SvnBridge with NTLM authentication (Digest is not an option because we do NOT have reversible encryption in our AD). SvnBridge needs to be patched to support NTLM; the patch is very simple and can be found on the Web.
    3. I had to patch ReviewBoard itself so it could work with AD servers over TLS in cases when the AD server has a self-signed certificate.
    4. I am running ReviewBoard under the same domain account as SvnBridge – this helps ReviewBoard seamlessly authenticate against SvnBridge. I did NOT enable SSPI authentication for the ReviewBoard virtual host in Apache, though, as ReviewBoard does not support NTLM auth anyway.

  19. Hello,thanks for the detailled explanation of the installation process.
    Now we use TFS2012 ,I tried to use SvnBridge ,but the official stite of SvnBridge said that it only can support TFS2010,Is there any solution to resolve my problem

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s