Ubuntu 16.04: Gerrit and Jenkins for code review

This article will describe installing gerrit which is web base code review tool. Gerrit server hostname is ubuntu-16.04.hiroom2.com.

1 gerrit server configuration

Here will describe configuration for running gerrit server on Ubuntu 16.04.

1.1 Install gerrit package

Install gerrit package with apt.

$ sudo apt install -y openjdk-8-jdk
$ sudo su -c 'echo "deb mirror://mirrorlist.gerritforge.com/deb gerrit contrib" \
> /etc/apt/sources.list.d/gerritforge.list'
$ sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 1871F775
$ sudo apt-get update -y
$ sudo apt-get install -y gerrit
$ sudo systemctl enable gerrit
$ sudo systemctl start gerrit

Now you can access to gerrit with below URL.

http://<server>:8080

1.2 Change TCP port

The gerrit default port is 8080. This port can be changed with following.

The domain name in canonicalWebUrl will be used for page transition. You can use IP address instead of the domain name. But these must be accessible from other machine. The domain name is set to hostname by default.

$ diff -uprN /etc/gerrit/gerrit.config{.org,}
--- /etc/gerrit/gerrit.config.org       2016-07-25 19:59:41.598934505 +0900
+++ /etc/gerrit/gerrit.config   2016-07-25 19:59:55.114798625 +0900
@@ -1,6 +1,6 @@
 [gerrit]
        basePath = git
-       canonicalWebUrl = http://ubuntu-16.04-gerrit.hiroom2.com:8080/
+       canonicalWebUrl = http://ubuntu-16.04-gerrit.hiroom2.com:8081/
 [database]
        type = h2
        database = db/ReviewDB
@@ -13,7 +13,7 @@
 [sshd]
        listenAddress = *:29418
 [httpd]
-       listenUrl = http://*:8080/
+       listenUrl = http://*:8081/
 [cache]
        directory = cache
 [receive]

Restart gerrit.

$ sudo systemctl restart gerrit

Now you can access to gerrit with below URL.

http://<server>:8081

1.3 Use Apache2

Apache2's proxy_http will provides access via <server>/gerrit instead of <server>:<port>. Apache2's auth_digest will provides authentication for accessing gerrit URL.

Make gerrit to use Apache2.

$ diff -uprN /etc/gerrit/gerrit.config{.org,}
--- /etc/gerrit/gerrit.config.org       2016-07-25 20:02:36.845106678 +0900
+++ /etc/gerrit/gerrit.config   2016-07-25 20:03:24.788584852 +0900
@@ -1,6 +1,6 @@
 [gerrit]
        basePath = git
-       canonicalWebUrl = http://ubuntu-16.04-gerrit.hiroom2.com:8081/
+       canonicalWebUrl = http://ubuntu-16.04-gerrit.hiroom2.com/gerrit
 [database]
        type = h2
        database = db/ReviewDB
@@ -13,7 +13,7 @@
 [sshd]
        listenAddress = *:29418
 [httpd]
-       listenUrl = http://*:8081/
+       listenUrl = proxy-http://127.0.0.1:8081/gerrit
 [cache]
        directory = cache
 [receive]

Restart gerrit.

$ sudo systemctl restart gerrit

Install apache2 package with apt.

$ sudo apt install -y apache2

Create Apache2 configuration.

  • Use proxy_http for rewrite <server>:<port> to <server>/gerrit.
  • Use auth_digest for authentication gerrit server URL.
  • Use nocanon for accessing URL which has a slash like path/to/src.c.
$ sudo su -c 'cat << EOF > /etc/apache2/mods-enabled/gerrit.conf
ProxyPass           /gerrit  http://localhost:8081/gerrit nocanon
ProxyPassReverse    /gerrit  http://localhost:8081/gerrit nocanon
ProxyRequests       Off

<Proxy http://localhost:8081/gerrit>
  Order deny,allow
  Allow from all
</Proxy>

<Location /gerrit>
  AuthType Digest
  AuthName "gerrit"
  AuthUserFile /etc/apache2/.htdigest
  Require valid-user
</Location>
EOF
'

Create digest authentication password file.

$ sudo htdigest -c /etc/apache2/.htdigest "gerrit" hiroom2
Adding password for hiroom2 in realm gerrit.
New password:
Re-type new password:

gerrit will use %2F instead of slash. Make AllowEncodedSlashes On in VirtualHost directive.

$ diff -uprN /etc/apache2/sites-available/000-default.conf{.org,}
--- /etc/apache2/sites-available/000-default.conf.org   2016-07-25 20:21:33.160757290 +0900
+++ /etc/apache2/sites-available/000-default.conf       2016-07-25 20:21:52.892879066 +0900
@@ -26,6 +26,8 @@
        # following line enables the CGI configuration for this host only
        # after it has been globally disabled with "a2disconf".
        #Include conf-available/serve-cgi-bin.conf
+
+       AllowEncodedSlashes On
 </VirtualHost>

 # vim: syntax=apache ts=4 sw=4 sts=4 sr noet

Enable proxy_http and auth_digest with a2enmod. Restart Apache2.

$ sudo a2enmod proxy_http auth_digest
$ sudo systemctl restart apache2

2 Add admin user who can use HTTP authentication

Add admin user with following.

  • Create SSH public key on gerrit client.
  • Change gerrit authentication to HTTP.
  • Access to gerrit server and create user.
  • Change gerrit authentication to DEVELOPMENT_BECOME_ANY_ACCOUNT.
  • Add user to Administrators group with Administrator user.
  • Change gerrit authentication to HTTP.
  • Register email via SSH.
  • This user can add user belonging Administrators group without DEVELOPMENT_BECOME_ANY_ACCOUNT.

Create SSH public key on gerrit client. If you have SSH public key already, please use it.

$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/home/hiroom2/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/hiroom2/.ssh/id_rsa.
Your public key has been saved in /home/hiroom2/.ssh/id_rsa.pub.
<snip>
$ cat .ssh/id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQ<snip>ooooWgJfN/LHp hiroom2@ubuntu-16

Change gerrit authentication to HTTP.

$ diff -uprN /etc/gerrit/gerrit.config{.org,}
--- /etc/gerrit/gerrit.config.org       2016-07-28 00:24:10.609299708 +0900
+++ /etc/gerrit/gerrit.config   2016-07-28 00:24:24.629195859 +0900
@@ -7,7 +7,7 @@
 [index]
        type = LUCENE
 [auth]
-       type = DEVELOPMENT_BECOME_ANY_ACCOUNT
+       type = HTTP
 [sendemail]
        smtpServer = localhost
 [sshd]

Restart gerrit.

$ sudo systemctl restart gerrit

Access to gerrit server with below URL.

http://<server>/gerrit

The dialog for digest authentication is displayed. Input username and password which are created with htdigest.

0001_digest.png

Register Fullname and SSH public key. You must use Fullname which is different from username. Unfortunatelly, email might cannot be registered via HTTP. email will be registered via SSH later.

0002_Create-User.png

Change gerrit authentication to DEVELOPMENT_BECOME_ANY_ACCOUNT.

$ diff -uprN /etc/gerrit/gerrit.config{.org,}
--- /etc/gerrit/gerrit.config.org       2016-07-28 00:37:43.171990767 +0900
+++ /etc/gerrit/gerrit.config   2016-07-28 00:39:11.763461918 +0900
@@ -7,7 +7,7 @@
 [index]
        type = LUCENE
 [auth]
-       type = HTTP
+       type = DEVELOPMENT_BECOME_ANY_ACCOUNT
 [sendemail]
        smtpServer = localhost
 [sshd]

Restart gerrit.

$ sudo systemctl restart gerrit

Reload browser and select "Switch Account" at the upper right.

0003_Switch-Account.png

Select Administrator.

0004_Administrator.png

Select "List Groups" of "People" at the upper.

0005_Groups.png

Add user to Administrators group.

0006_Members.png

Change gerrit authentication to HTTP.

$ diff -uprN /etc/gerrit/gerrit.config{.org,}
--- /etc/gerrit/gerrit.config.org       2016-07-28 00:39:38.915300824 +0900
+++ /etc/gerrit/gerrit.config   2016-07-28 00:40:21.187050856 +0900
@@ -7,7 +7,7 @@
 [index]
        type = LUCENE
 [auth]
-       type = DEVELOPMENT_BECOME_ANY_ACCOUNT
+       type = HTTP
 [sendemail]
        smtpServer = localhost
 [sshd]

Restart gerrit.

$ sudo systemctl restart gerrit

Register email via SSH.

$ # ssh -p 29418 <server> gerrit set-account --add-email <mail> <user>
$ # ssh -p 29418 <server> gerrit set-account --preferred-email <mail> <user>
$ ssh -p 29418 ubuntu-16.04-gerrit.hiroom2.com gerrit set-account \
--add-email "hiroom2@ubuntu-16.04-client.hiroom2.com" hiroom2
$ ssh -p 29418 ubuntu-16.04-gerrit.hiroom2.com gerrit set-account \
--preferred-email "hiroom2@ubuntu-16.04-client.hiroom2.com" hiroom2

This user can add the other user to Administrators group without switching HTTP and DEVELOPMENT_BECOME_ANY_ACCOUNT.

In case of HTTP user like commiter and reviewer is as below.

  • Add username and password to digest authentication password file.
  • Access to gerrit via HTTP with username and password and create user.
  • Update user's email via SSH

In case of Non-HTTP user like Jenkins is as below.

  • Create user via SSH.

3 gerrit client configuration

Here will describe configuration for using gerrit server from client on Ubuntu 16.04.

3.1 no matching key exchange method found. Their offer: diffie-hellman-group1-sha1

While gerrit will use diffie-hellman-group1-sha1, OpenSSH 7 client does not support old encryption including diffie-hellman-group1-sha1 by default. This will cause SSH connection error.

$ # OpenSSH 7 client
$ ssh -p 29418 ubuntu-16.04-gerrit.hiroom2.com gerrit
Unable to negotiate with 192.168.11.86 port 29418: no matching key
exchange method found. Their offer: diffie-hellman-group1-sha1

For avoiding this issue, use the following alias in .bashrc.

alias ssh='ssh -oKexAlgorithms=+diffie-hellman-group1-sha1'

You can also use the following configuration in .ssh/config.

$ cat .ssh/config
Host ubuntu-16.04-gerrit.hiroom2.com
  KexAlgorithms +diffie-hellman-group1-sha1

3.2 Install git-review package

Install git-review package which is a tool for sending a patch to gerrit server.

$ sudo apt-get install -y git-review

4 Create new project

Create new project with the following SSH command. If you do not import existing repository to the project, you must use –empty-commit option.

ssh -p 29418 <server> gerrit create-project --empty-commit <name> \
--description "'<desc>'"

Create new project which names new-project.

$ ssh -p 29418 ubuntu-16.04-gerrit.hiroom2.com gerrit create-project \
--empty-commit new-project.git --description "'Create project from SSH command'"

git clone via HTTP.

$ git clone http://ubuntu-16.04-gerrit.hiroom2.com/gerrit/new-project.git
$ cd new-project

Create .gitreview which is configuration file for git-review. Please change host and project to your environment.

$ cat <<EOF > .gitreview
[gerrit]
host=ubuntu-16.04-gerrit.hiroom2.com
port=29418
project=new-project
defaultbranch=master
EOF

Commit patch.

$ echo hello > hello.txt
$ git add hello.txt
$ git commit -m "Hello, World"

git log before git-review is as below.

$ git log
commit e2b5070e2cdc576a2e669366f85797ebfdc29b49
Author: hiroom2 gerrit <hiroom2@ubuntu-16>
Date:   Thu Jul 28 05:29:51 2016 +0900

    Hello, World

commit bc9d23cb95c8d94ae8e3544bac512c4210c7c443
Author: hiroom2 gerrit <hiroom2@ubuntu-16.04-gerrit.hiroom2.com>
Date:   Thu Jul 28 05:25:49 2016 +0900

    Initial empty repository

git-reviewを実行します。

$ git review # or git review -R
Creating a git remote called "gerrit" that maps to:
        ssh://hiroom2@ubuntu-16.04-gerrit.hiroom2.com:29418/new-project
Your change was committed before the commit hook was installed.
Amending the commit to add a gerrit change id.
remote: Processing changes: new: 1, refs: 1, done
remote:
remote: New Changes:
remote:   http://ubuntu-16.04-gerrit.hiroom2.com/gerrit/1 Hello, World
remote:
To ssh://hiroom2@ubuntu-16.04-gerrit.hiroom2.com:29418/new-project
 * [new branch]      HEAD -> refs/publish/master

git log after git-review is as below. "Change-ID:xxx" has been appended. The "Change-ID:xxx" is ID for managing the patch on gerrit server.

$ git log
commit 2fc75bd27f6bb323ad19a39b74c68b5faafac848
Author: hiroom2 gerrit <hiroom2@ubuntu-16>
Date:   Thu Jul 28 05:29:51 2016 +0900

    Hello, World

    Change-Id: I965523a134ac6bfaae91491bc7d53d153a93e3d2

commit bc9d23cb95c8d94ae8e3544bac512c4210c7c443
Author: hiroom2 gerrit <hiroom2@ubuntu-16.04-gerrit.hiroom2.com>
Date:   Thu Jul 28 05:25:49 2016 +0900

    Initial empty repository

This "Change-Id:xxx" is appended by .git/hooks/commit-msg which was downloaded when running git-review. If you have the other .git/hooks/commit-msg, git-review won't download .git/hooks/commit-msg for gerrit and "Change-Id:xxx" will not appended. In this case, you need to download .git/hooks/commit-msg for gerrit via following URL.

$ # wget http://<server>/gerrit/tools/hooks/commit-msg
$ wget http://ubuntu-16.04-gerrit.hiroom2.com/gerrit/tools/hooks/commit-msg
$ mv commit-msg .git/hooks/commit-msg_gerrit

Rename existing .git/hooks/commit-msg to .git/hooks/commit-msg_xxx. Add the following .git/hooks/commit-msg which will call .git/hooks/commig_msg-xxx.

$ cat .git/hooks/commit-msg
#!/bin/sh

for script in `ls ${0}_*`; do
  echo "Running ${script}"
  ${script} $@
  ret=$?
  if [ ${ret} -ne 0 ]; then
    echo "${script} is FAILED."
    exit ${ret}
  fi
done

Select "Open" of "All" at the upper. The patch by git-review is displayed.

0007_Open.png

Select patch and detail page is displayed. You can check patch on this page. Select "Abandon" if there is a problem, select "Code-Review+2" if there is no problem. This article will select "Code-Review+2".

0008_Code-Review.png

"Submit" will be enabled when value of "Code-Review" is +2. "Submit" will push the patch to repository.

0009_Submit.png

5 Import existing repository to gerrit

Create project without –empty-commit option.

$ ssh -p 29418 ubuntu-16.04-gerrit.hiroom2.com gerrit create-project \
existing-project.git --description "'Create existing project from SSH command'"

Create bare repository of existing repository.

$ git clone --bare <path-to-repo>/existing-project.git
$ cd existing-project.git

Push bare repository to gerrit server via SSH.

$ git push ssh://ubuntu-16.04-gerrit.hiroom2.com:29418/existing-project.git *:*

After this, you can git clone via HTTP. The hash value is the same with existing repository.

$ git clone http://ubuntu-16.04-gerrit.hiroom2.com/gerrit/existing-project.git

6 Jenkins

Add Verified score in addition to Code-Review score and make Jenkins verified automatically. Adding Verified score will provides build and test result before code review. Submit can be done when Code-Review score is over than +2 and Verified score is over than + 1. This article will use existing-project which was used before.

6.1 Add Verified score

Code-Review score is defined by Code-Review label in project configuration. Define Verified label in All-Projects's project configuration. All-Project is the base project for creating project.

Select "Open" of "All" menu and select All-Project.

0010_All-Projects.png

Select "Edit Project Config".

0011_Edit-Config.png

Project configuration is displayed.

0012_project-config.png

Change project configuration with as below.

diff --git a/project.config b/project.config
index 2584f6b..919288d 100644
--- a/project.config
+++ b/project.config
@@ -28,6 +28,9 @@
        label-Code-Review = -2..+2 group Administrators
        label-Code-Review = -2..+2 group Project Owners
        label-Code-Review = -1..+1 group Registered Users
+       label-Verified = -1..+1 group Administrators
+       label-Verified = -1..+1 group Project Owners
+       label-Verified = -1..+1 group Registered Users
        submit = group Administrators
        submit = group Project Owners
        editTopicName = +force group Administrators
@@ -40,6 +43,8 @@
        push = group Project Owners
        label-Code-Review = -2..+2 group Administrators
        label-Code-Review = -2..+2 group Project Owners
+       label-Verified = -1..+1 group Administrators
+       label-Verified = -1..+1 group Project Owners
        submit = group Administrators
        submit = group Project Owners
 [access "refs/tags/*"]
@@ -57,3 +62,8 @@
        value =  0 No score
        value = +1 Looks good to me, but someone else must approve
        value = +2 Looks good to me, approved
+[label "Verified"]
+       function = MaxWithBlock
+       value = -1 Fails
+       value =  0 No score
+       value = +1 Verified

Select "Publish Edit", "Publish", "Code-Review+2", "Submit". Change is applied.

0013_submit.png

6.2 Add jenkins user to gerrit

Create Jenkins's SSH public key on Jenkins server.

$ sudo su - jenkins
$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/var/lib/jenkins/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /var/lib/jenkins/.ssh/id_rsa.
Your public key has been saved in /var/lib/jenkins/.ssh/id_rsa.pub.
<snip>

Add jenkins user to gerrit with admin user. Register Jenkins's SSH public key.

$ cat id_rsa.pub | ssh -p 29418 ubuntu-16.04-gerrit.hiroom2.com gerrit \
create-account --ssh-key - --group "'Administrators'" \
--full-name "'jenkins gerrit'" \
--email "'jenkins@ubuntu-16.04-jenkins.hiroom2.com'" jenkins

6.3 Jenkins's Gerrit Trigger Plugin

Gerrit Trigger Plugin run test on Jenkins via git-review and update Verified score to gerrit. Install Gerrit Trigger Plugin in Jenkins's Plugin Manager.

Open Gerit Trigger Plugin configuration.

Manage Jenkins -> Gerrit Trigger

0014_Add-New-Server.png

Input gerrit server.

0015_gerrit-server-name.png

Set gerrit server hostname to "Hostname", gerrit server URL to "Frontend URL" and jenkins to "Username".

http://<server>/gerrit # Frontend URL

You can check connection with "Test Connection". Save this configuration.

0016_gerrit-server-config.png

Click status of appended gerrit server. It will be blue from red.

0017_gerrit-server-status.png

Create new project with "New Item".

0018_New-Item.png

This article will use "Freestyle project".

0019_Freestyle-project.png

Select "git" at "Source Code Management".

  • Set gerrit project repository URL to "Repository URL".
  • Click "Advanced" and set $GERRIT_REFSPEC to "Refspec".
  • Click "Add Branch" and $GERRIT_MASTER to "Branch Specifier".

0020_Git-for-Gerrit.png

Check "Gerrit event" at "Build Triggers".

  • Set gerrit server to "Choose a Server". This gerrit server was appended in Gerrit Trigger Plugin configuration.
  • Append "Patchset Created" and "Draft Published" to "Trigger on".
  • Set "Plain" to "Type" and gerrit project name to Pattern in "Gerrit Project".
  • Set "Path" to "Type" and "**" to "Pattern" in "Gerrit Project's Branches".

0021_Gerrit-Trigger.png

6.4 Excecution result

Clone existing-project from gerrit server.

$ git clone http://ubuntu-16.04-gerrit.hiroom2.com/gerrit/existing-project.git
$ cd existing-project

Append .gitreview for existing-project.

$ cat <<EOF > .gitreview
[gerrit]
host=ubuntu-16.04-gerrit.hiroom2.com
port=29418
project=existing-project
defaultbranch=master
EOF

Create new file and commit it.

$ echo hello > hello.txt
$ git add hello.txt
$ git commit -m "Hello, World"

Send patchset to gerrit server with git-review.

$ git review -R
Creating a git remote called "gerrit" that maps to:
        ssh://hiroom2@ubuntu-16.04-gerrit.hiroom2.com:29418/existing-project
Your change was committed before the commit hook was installed.
Amending the commit to add a gerrit change id.
remote: Processing changes: new: 1, refs: 1, done
remote:
remote: New Changes:
remote:   http://ubuntu-16.04-gerrit.hiroom2.com/gerrit/2 Hello, World
remote:
To ssh://hiroom2@ubuntu-16.04-gerrit.hiroom2.com:29418/existing-project
 * [new branch]      HEAD -> refs/publish/master

Build history has a new build which was triggered by gerrit.

0022_Jenkins-by-git-review.png

Verified score is updated by Jenkins on gerrit server. After Code-Review score is +2, it can submit.

0023_Verified-by-Jenkins.png