Code Review and Audit with Phabricator

Wiki

Phabricator is a collection of web applications which help software companies build better software. Including the Differential code review tool, the Diffusion repository browser, the Herald change monitoring tool, the Maniphest bug tracker and the Phriction wiki. Phabricator integrates with Git, Mercurial, and Subversion. It is available as free software under the Apache License, version 2. Phabricator was originally developed as an internal tool at Facebook. Phabricator's principal developer is Evan Priestley. Priestley left Facebook to continue Phabricator's development in a new company called Phacility.

Phabricator Homepage secure.phabricator.com

This post contains basic install instructions to get Phabricator up and running. Phabricator is a LAMP (Linux, Apache, MySQL, PHP) application. To install Phabricator, you will need reference: Phabricator User Documentation. There are auto install script on Configuration Guide page, but I recommend manual installation.

Because some of the components of the older version, resulting in difficult to maintain. To setup LAMP server reference Configure LAMP ( Apache, MySQL, PHP ) on RedHat Enterprise Linux 6 or Installation LAMP ( Apache+MySQL+PHP ) on Ubuntu.

Environment Versions

Operating System     : Ubuntu 16.10 Yakkety Yak 64-bit Server Edition
PHP Version          : 7.1.2
MySQL Version        : 5.7.17 (Note that Phabricator will be use about 58 databases)
Node Version         : 7.7.2
Node Version Manager : 0.33.1
Virtual Host Name    : codereview.xuri.me
Virtual LDAP Server  : ldap.xuri.me
Virtual SMTP Server  : smtp.xuri.me
Virtual Email        : [email protected]

Create necessary users and add phd user to sudoers:

$ sudo adduser phd --home /home/phd
$ sudo adduser phd sudo
$ sudo adduser git

And create repo directory if Phabricator will be hosting repos:

$ sudo mkdir /var/repo && sudo chmod phd:phd -R /var/repo

Install Phabricator

After installation, download Phabricator from git:

$ su phd
$ cd /home/phd
$ git clone https://github.com/phacility/phabricator.git

Installing APCu

APCu is APC stripped of opcode caching, it can provide a compatibility mode, such that it can provide a drop in replacement for the applicable parts of APC.

$ sudo apt-get install php-apcu

To config MySQL settings with command:

$ cd /home/phd/phabricator
$ ./bin/config set mysql.host codereview.xuri.me
$ ./bin/config set mysql.port 3306
$ ./bin/config set mysql.user username
$ ./bin/config set mysql.pass password

Config Phabricator daemons process lanuch user:

$ ./bin/config set phd.user phd

Config Phabricator Base URI:

$ ./bin/config set phabricator.base-uri https://codereview.xuri.me/

SSHD Setup:

$ ./bin/config set diffusion.ssh-port 2222,
$ ./bin/config set diffusion.ssh-user git,

For local-disk file storage only:

$ mkdir /home/phd/phabricator-files
$ chmod -R 755 /home/phd/phabricator-files
$ ./bin/config set storage.local-disk.path /home/phd/phabricator-files

Set true if you want to allow public http cloning:

$ ./bin/config set policy.allow-public true

Set to true if you want to allow http pushes

$ ./bin/config set diffusion.allow-http-auth false

You most likely want prototype apps, they are very useful:

$ ./bin/config set phabricator.show-prototypes true

Recommended silliness-enabling settings:

$ ./bin/config set files.enable-imagemagick true
$ ./bin/config set remarkup.enable-embedded-youtube true
$ ./bin/config set audit.can-author-close-audit false

Configuring Outbound E-mail

Instructions for configuring Phabricator to send mail. Phabricator can send outbound email via several different providers, called "Adapters". Such as use sendmail:

$ sudo apt-get install sendmail imagemagick python-pygments

Configuring mail adapter:

$ ./bin/config set metamta.mail-adapter PhabricatorMailImplementationPHPMailerAdapter

PHP Mailer settings:

$ ./bin/config set phpmailer.mailer smtp
$ ./bin/config set phpmailer.smtp-host smtp.xuri.me
$ ./bin/config set phpmailer.smtp-port 465
$ ./bin/config set phpmailer.smtp-user [email protected]
$ ./bin/config set phpmailer.smtp-password password

Set custom E-mail meta to [CodeReview] if you want:

$ ./bin/config set metamta.conpherence.subject-prefix [CodeReview]
$ ./bin/config set metamta.differential.subject-prefix [CodeReview]
$ ./bin/config set metamta.diffusion.subject-prefix [CodeReview]
$ ./bin/config set metamta.files.subject-prefix [CodeReview]
$ ./bin/config set metamta.maniphest.subject-prefix [CodeReview]
$ ./bin/config set metamta.macro.subject-prefix [CodeReview]
$ ./bin/config set metamta.paste.subject-prefix [CodeReview]
$ ./bin/config set metamta.pholio.subject-prefix [CodeReview]
$ ./bin/config set metamta.phriction.subject-prefix [CodeReview]

Note that change settings in Phabricator need restart to take effect.

Configure MySQL and storage:

Add these new lines to /etc/mysql/my.cnf under the [mysqld] heading:

port = 4443
max_allowed_packet = 32M
innodb_buffer_pool_size = 1600M
sql_mode = STRICT_ALL_TABLES

Restart MySQL and run Phabricator storage upgrade:

$ sudo service mysql restart
$ ./bin/storage upgrade

Configure PHP

Adjust the following fields in /etc/php/7.1/cli/php.ini

post_max_size = 8M
date.timezone = Etc/UTC
opcache.validate_timestamps = 0

Then restart Apache

$ sudo service apache2 restart

Custom homepage HTML title by edit file src/view/page/AphrontPageView.php:

$title = $this->getTitle();
// Hacked here:
if ($title == 'Phabricator') {
    $title = 'Code Review';
}

Set mail sender as custom name to edit file externals/phpmailer/class.phpmailer.php, change $this->FromName = $name; to $this->FromName = 'CodeReview';.

Restart phd daemons:

$ ./bin/phd restart

Setup Phabricator via browser. Create administrator's account and resolve setup issues.

Configure the Phabricator LDAP Login

First of all, you should test your LDAP service with following script to see whether the service itself works:

$ ldapsearch -h ldap.xuri.me -p 389 -x -b "dc=domain,dc=com" -D "domain\yourLDAPusername" "(sAMAccountName=targetUser)" -W | less

This command will let you search the targetUser with yourLDAPusername and yourLDAPpassword(-W), so if this works, means that you have almost all the information for configuring the Phabricator LDAP Login.

Next, try to configure your LDAP Login in the Phabricator with following data:

Hostname                : ldap.xuri.me
Port                    : 389 (can be 3268)
Base Distinguished Name : dc=domain,dc=com
Search Attribute        : (sAMAccountName=$(login))
Always Search           : Yes
Anonymous Username      : com\yourLDAPusername
Anonymous Password      : yourLDAPpassword
Username Attributes     : sAMAccountName
Real Name attributes    : displayName
LDAP Version            : 3
Referrals               : No
Use TLS                 : No
Active Directory Domain : domain

Now you can test this configuration in the command line:

$ ./bin/auth ldap

Import LDAP Users

Applications → People → USER ADMINISTRATION → Import from LDAP

For example, if your want to filer people in group:

memberOf:: bWVtYmVyb2Y9Q049R3JvdXAsQ049VXNlcnMsREM9ZG9tYWluLERDPWNvbQ==

Use the LDAP query:

memberof=CN=Group,CN=Users,DC=domain,DC=com

Notifications Server Setup and Configuration

By default, Phabricator delivers information about events (like users creating tasks or commenting on code reviews) through email and in-application notifications.

Installing Node and modules. The notification server uses Node.js, so you'll need to install it first. Install Node Version Manager (nvm) use cURL:

$ curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.1/install.sh | bash

To download, compile, and install the latest release of node:

$ nvm install node
$ nvm use 7.7.2

You will also need to install the ws module for Node. This needs to be installed into the notification server directory:

$ cd ~/phabricator/support/aphlict/server/
$ npm install ws

Once Node.js and the ws module are installed, you're ready to start the Aphlict server:

$ ./bin/aphlict start

Configuring Phabricator

After starting the server, configure Phabricator to connect to it by adjusting notification.servers. This configuration option should have a list of servers that Phabricator should interact with.

Normally, you'll list one client server and one admin server, like this (~/phd/phabricator/conf/aphlict/aphlict.default.json):

{
    "servers": [{
        "type": "client",
        "port": 22280,
        "listen": "0.0.0.0",
        "ssl.key": null,
        "ssl.cert": null,
        "ssl.chain": null
    }, {
        "type": "admin",
        "port": 22281,
        "listen": "127.0.0.1",
        "ssl.key": null,
        "ssl.cert": null,
        "ssl.chain": null
    }],
    "logs": [{
        "path": "/var/log/aphlict.log"
    }],
    "pidfile": "/var/tmp/aphlict/pid/aphlict.pid"
}

Provides Chinese Locales for Phabricator

Drop the code phabricator-zh_CN into phabricator/src/extensions/, Configure Settings → Personal Account → Settings Account → Account Settings → Translation select Chinese (zh_CN) and Config global user default preference at Settings → All Settings → Global Default Settings as administrator.

Passphrase

Set credential in Application → Passphrase → Create Credential.

Project

Organize users and objects with projects. Create a project first and active it, add related user into this project.

Repository Hosting

Phabricator can host repositories and provide authenticated read and write access to them over HTTP and SSH. Create Repository in the Diffusion.

Herald

Herald allows you to write rules which run automatically when objects (like tasks or commits) are created or updated. For instance, you might want to get notified every time someone sends out a revision that affects some file you're interested in, even if they didn't add you as a reviewer. Create Herald Rule in Application → Herald → Create Herald Rule.

Legalpad

Using Legalpad to track agreements and signatures on legal documents. Legalpad is a simple application for tracking signatures on legal agreements. You can add legal documents, users can sign them, and you can keep track of who has signed what. Create a documents for sign in Application → Legalpad → Create → Document.

Phabricator supports two similar but separate code review workflows: "review" and "audit". Review occurs in Differential, before changes are published. Audit occurs in Diffusion, after changes are published. Discusses the differences between "review" and "audit" workflows reference User Guide: Review vs Audit.

Review

Config on client install Arcanist and libphutil. Arcanist is a command-line interface to Phabricator and the libphutil is a shared library Arcanist depends upon.

$ git clone https://github.com/phacility/libphutil.git
$ git clone https://github.com/phacility/arcanist.git

Add command arcanist:

$ export PATH="$PATH:/home/phd/arcanist/bin/"

Install Arcanist certificate for Phabricator authority.

$ arc install-certificate

Configuring a new project, edit .arcconfig file under project root folder:

{
    "project_id" : "name",
    "conduit_uri" : "http://codereview.xuri.me/"
}

Submit review in your project:

$ git commit -a
$ arc diff

When your revision has been accepted, you can usually push it like this in Git (Merges <branch> into master and pushes):

$ arc land <branch>

arc land makes some assumptions about your workflow which might not be true. Consult the documentation before you use it. You should also look at arc amend, which may fit your workflow better.

Audit

Create a Herald rule to trigger the audit.

Issues

If you got Call to undefined function utf8_decode() when create question in ponder, replace strlen(utf8_decode($string)); with count(phutil_utf8v($string)); in the file ~/libphutil/src/utils/utf8.php.

Other Custom Settings

  • Enable show filetree

Auto enable a sidebar showing affected files. Enable Show Filetree in Settings → Global Default Settings → Diff Preference. When this option is enabled, press F to show or hide the sidebar. Auto show sidebar hack code ~/phabricator/webroot/rsrc/js/core/behavior-phabricator-nav.js, replace .setData({ key : 'nav-collapsed', value : (collapsed ? 1 : 0) }) with .setData({ key : 'nav-collapsed', value : (collapsed ? 1 : 1) }).

  • Custom logo and header color

Set custom value for ui.header-color, ui.logo and ui.footer-items in Application → Config → User Interface User → Interface Configuration.

  • Query a git repository for ref information at a specific commit

Sometimes we need to query audit status by given commit ID. Phabricator provides developer API method in Application → Conduit → diffusion.refsquery. But I always got ERR-CONDUIT-CORE error code and following info:

[You Shall Not Pass: Restricted Repository] (Can View) You do not have permission to view this object. // Members of the project "Restricted Project" can take this action.

Since I did not find a better solution, I created a custom script to handle this:

<?php
/**
 * Licensed under The MIT License
 * Redistributions of files must retain the above copyright notice.
 *
 * @uses        Phabricator https://www.phacility.com
 * @author      Ri Xu https://xuri.me
 * @copyright   Copyright (c) 2013 - 2017 Ri Xu Online All rights reserved.
 * @link        https://xuri.me/2017/03/14/code-review-and-audit-with-phabricator.html
 * @since       14 Mar, 2017
 * @license     Licensed under The MIT License http://www.opensource.org/licenses/mit-license.php
 * @version     0.1
 *
 * This script provieds method to query audit status by given commit ID. PDO
 * extensions is required. Place this file in the Plabricator webroot/rsrc
 * directory. Request URL like this:
 * http://codereview.xuri.me/rsrc/audit.status.php?repo=name&commit=commitid&token=token
 */

// Get the browser request.
$repo  = $_GET["repo"];
$cmit  = $_GET["commit"];
$token = $_GET["token"];
$error = array(
        'result' => [],
        'error_code' => 500,
        'error_info' => null
    );

// Token validation.
if ($token != "token") {
    $error['error_info'] = "Token is invalid";
    echo json_encode($error);
    return;
}

// Parameters validation.
if (empty($repo) && empty($cmit)) {
    $error['error_info'] = "repo and commit field can't be empty";
    echo json_encode($error);
    return;
}

// Database connection use PDO.
$username = 'username';
$passwd   = 'password';

try {
    $dbh = new PDO('mysql:host=localhost;port=3306;dbname=phabricator_repository;', $username, $passwd);
} catch (PDOException $e){
    $error['error_info'] = 'Database connection failed: ' . $e->getMessage();
    echo json_encode($error);
}

$stmt = $dbh->prepare("SELECT phid FROM repository_commit WHERE repositoryID = (SELECT id FROM repository WHERE name = :name) AND commitIdentifier = :cmit;");
$stmt->bindValue(':name', $repo, PDO::PARAM_STR);
$stmt->bindValue(':cmit', $cmit, PDO::PARAM_STR);
$stmt->execute();
$phid = $stmt->fetchAll();

if (empty($phid)) {
    $error['error_info'] = 'Failed to get audit status';
    echo json_encode($error);
    return;
}

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "http://codereview.xuri.me/api/audit.query");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, "api.token=api-token&commitPHIDs[0]=".$phid[0]["phid"]);
curl_setopt($ch, CURLOPT_POST, 1);

$headers   = array();
$headers[] = "Content-Type: application/x-www-form-urlencoded";
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

$result = curl_exec($ch);
if (curl_errno($ch)) {
    curl_close ($ch);
    $error['error_info'] = 'Error:' . curl_error($ch);
    echo json_encode($error);
    return;
}
curl_close ($ch);
echo $result;
return;

API response like this:

{
    "result": [
        {
            "id": "200",
            "commitPHID": "PHID-CMIT-commit",
            "auditorPHID": "PHID-PROJ-project",
            "reasons": [
                "H3 Triggered Audit"
            ],
            "status": "accepted"
        }
    ],
    "error_code": null,
    "error_info": null
}
  • Import SVN repositories by sub-path

If you using a SVN library (for example svn.xuri.me) without grant root directory permissions, only import the subdirectory of the subversion repositories (https://svn.xuri.me/svn/subdir1/subdir2), but Phabricator doesn't support this feature currently, there are ways to circumvent this issue. Set https://svn.xuri.me/svn/subdir1/ for repo's URI and set subdir2/ in Subversion → Import Only config Edit function newFormattedCommand in the file src/applications/diffusion/protocol/DiffusionSubversionCommandEngine.php before return statement:

foreach($argv as $key => $value) {
    if (substr_count($value, '/subdir1/subdir2')) {
        $argv[$key] = str_replace('/subdir1/subdir2/subdir1/subdir2', '/subdir1/subdir2', $value);
    }
    if (substr_count($value, '/subdir1')) {
        $argv[$key] = str_replace('/subdir1/subdir1', '/subdir1', $value);
    }
}

Or use regular expression matching:

$repo    = $this->getRepository();
$baseURI = $repo->getSubversionBaseURI();
$pathURI = $repo->getSubversionPathURI();
preg_match('/^.*svn/', $pathURI, $matches, PREG_OFFSET_CAPTURE);
$base    = str_replace($matches[0][0],"",$pathURI);
$sub     = str_replace("/@","",str_replace($pathURI,"",$baseURI));
foreach ($argv as $key => $value) {
    if (substr_count($value, $base.$sub)){
      $argv[$key] = str_replace($base.$sub.$base.$sub,$base.$sub, $value);
    }
    if (substr_count($value, $base)){
      $argv[$key] = str_replace($base.$base,$base, $value);
    }
}

This hack method only support 2-level deep subdirectories, if we need import more deep subdirectories, we need to get permission access root dir.

Create SVN Repo

$ mkdir /var/repo && svnadmin create /var/repo/svn

Config access permission in the file /var/repo/svn/conf/svnserve.conf:

[general]
anon-access = none
auth-access = write
password-db = passwd
authz-db = authz

Config user's password in the file /var/repo/svn/conf/password:

[users]
harry = harryssecret
sally = sallyssecret
lucy = lucyssecret
michael = michaelssecret

Config authorization in the file /var/repo/svn/conf/authz:

[groups]
admin = harry,michael
dev = sally,lucy

[/repo1]
@admin = rw
@dev = r

[/path/to/repo2]
@admin = rw
@dev =

Start SVN Server

$ svnserve -d -r /var/repo/svn --listen-host svn.xuri.me

Get SVN repo info

$ svn info -username harry svn://svn.xuri.me/

Commit and push changed to the repo

$ mkdir repo1
$ svn add repo1
$ svn commit -m "create repo1" repo1
  • Add encoding support for repositories

For example, add GBK encoding support for repoistory. Choose "Basics" in the "Manage Repository" page, add new text Enconding in by click "Edit Text Encoding".

Conclusion

Phabricator not only a place for repository hosting or issue tracking. It represents the workflow that we agree with. Phabricator is suitable for a complete project because it contains many different units for project management. In the way of dealing with the project, Phabricator is different with GitHub Enterprise and Atlassian series tools. Github responds to the fork and merge pattern, each project and developer's degree of freedom is quite high. Phabricator's rebase mode is more conducive to management and suitable for agile software development.

Code Review and Audit with Phabricator
7 votes, 4.43 avg. rating (89% score)