2016-03-18

整合Redmine跟Gerrit

Redmind是一套用Ruby-on-Rail開發的開源專案管理軟體,知名度頗高;Gerrit則是Google工程師開發一套建構於版本控制系統之上的Code Review的系統,他的hooks功能在前篇提到一 些,這裡要介紹怎麼把Redmind開出來的issue自動被Gerrit在patchset更新的時候一併更新。

[環境]

  • OS:Ubuntu 14.04 LTS
  • Redmine version: 2.3.2.stable
  • Gerrit version: 2.11
  • Script language: Python 2.7
[安裝設定]

Step 1. 開通Redmind的REST功能,管理權限Admin登入,進行網站設定 -> 認證 -> 啟用REST,打勾然後儲存。




Step 2. 取得各自帳號的REST API key,點選右上角我的帳戶,下方API金鑰點選後會彈出key,有了他可以透過REST API對Redmine程式化操作。


Step 3. 再來是設定Gerrit的部分,我們這邊想做到的是當Developer把code commit push到Gerrit並且成功後,Gerrit可以透過hook機制自動把Redmine開立的表單進行更新,協助Developer偷懶省時;很幸運在Github上有大大提供一份python script,我順便也把源碼複製一份在下面

#!/usr/bin/python2.7
# -*- coding: utf-8 -*-
#
# Copyright (c) 2008-2011 Tobias Hieta <tobias@hieta.se>
# Copyright (c) 2014 Rossen Apostolov <rossen@mochiron.org>
#
# This is a simple Gerrit hook for updating Redmine with information about what changes
# are in Gerrit review.
#
# It parses the commit messages from Gerrit and looks for a Redmine issue in the style
# of "#1028".
#
# It then adds a informative message to the Redmine issue about the review URL and other
# data. It can also change the status of the issue to indicate that this issue is now
# under gerrit review.
#
# Script is tested with Redmine 2.4.1 and Gerrit 2.6.1
# set your API key here. You can find it under "My account" in Redmine, i.e. http://redmine.mydomain.com/my/account
REDMINE_API_KEY = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
# the hostname or ip number of redmine, do not include the port number here.
REDMINE_HOST = "redmine.mydomain.com"
# The port number on which redmine is listening, change this to 443 if the
# redmine host is using a secure connection
REDMINE_HOST_PORT_NUMBER = 80
# Set this to true if the redmine host is using a secure connection (SSL)
REDMINE_HOST_USING_SSL = False
# if you want the script to update the status of the issue in redmine
# you'll need to set this to the id number of that status. otherwise set
# it to None and it won't update the status
# the regex we use for findig the issue id
REDMINE_ISSUE_ID_REGEX = '\#(\d+)'
# which projects to run the script for
GERRIT_PROJECTS = ["PROJECT_NAME"]
import optparse
from subprocess import check_output # TODO: you'll need python 2.7 for this
import sys
import re
import httplib
import json
if __name__ == '__main__':
parser = optparse.OptionParser()
parser.add_option('-c', '--change', dest='changeid')
parser.add_option('-u', '--change-url', dest='changeurl')
parser.add_option('-p', '--project', dest='project')
parser.add_option('-b', '--branch', dest='branch')
parser.add_option('-s', '--uploader', dest='uploader')
parser.add_option('-o', '--commit', dest='commit')
parser.add_option('-a', '--patchset', dest='patchset')
parser.add_option('-d', '--is-draft', dest='isdraft')
parser.add_option('-t', '--topic', dest='topic')
(options, x) = parser.parse_args(sys.argv)
if options.project not in GERRIT_PROJECTS:
print "wrong project %s" % options.project
sys.exit(0)
commitmsg = check_output(['git','cat-file','-p', options.commit])
if not commitmsg or len(commitmsg) < 10:
print "no commit msg!"
sys.exit(0)
# update status only for the first patchset
if int(options.patchset) != 1:
print "This is not the first patchset (%s) for this issue (%s), will not update the status" % (options.patchset, options.changeurl)
sys.exit(0)
# Don't change the status, only put a note. Otherwise uncomment the lines below.
IN_REVIEW_STATUS = None
# for drafts, change the status to "In Progress"...
# if options.isdraft == str("true"):
# IN_REVIEW_STATUS = 2
# ... otherwise change to "Fix Uploaded"
# else:
# IN_REVIEW_STATUS = 10
regex = re.compile(REDMINE_ISSUE_ID_REGEX, re.IGNORECASE)
mgi = regex.finditer(commitmsg)
for mg in mgi:
redmineid = int(mg.group(1))
if not redmineid or redmineid == 0:
print "no issue set here"
sys.exit(0)
if options.isdraft == str("true"):
redminecomment = "Gerrit received a related DRAFT patchset '%s' for Issue #%d.\n" % (options.patchset, redmineid)
else:
redminecomment = "Gerrit received a related patchset '%s' for Issue #%d.\n" % (options.patchset, redmineid)
redminecomment += "Uploader: %s\n" % options.uploader
redminecomment += "Change-Id: %s\n" % options.changeid
redminecomment += "Gerrit URL: %s\n" % options.changeurl
jsonstruct = {"issue":{}}
jsonstruct["issue"]["notes"] = redminecomment
if IN_REVIEW_STATUS:
jsonstruct["issue"]["status_id"]=IN_REVIEW_STATUS
jsondata = json.dumps(jsonstruct)
puturl = "/issues/%d.json" % (redmineid)
if REDMINE_HOST_USING_SSL:
connection = httplib.HTTPSConnection(REDMINE_HOST, REDMINE_HOST_PORT_NUMBER)
else:
connection = httplib.HTTPConnection(REDMINE_HOST, REDMINE_HOST_PORT_NUMBER)
connection.request('PUT', puturl, jsondata, {"Content-Type":"application/json", "X-Redmine-API-Key":REDMINE_API_KEY})
response = connection.getresponse()
sys.exit(0)

Step 4. 請參照如何設定Gerrit的Hook機制把script設定起來,記得有幾個參數要換掉:REDMINE_API_KEY、REDMINE_HOST、REDMINE_HOST_PORT_NUMBER、REDMINE_ISSUE_ID_REGEX,再來是puturl變數依照你Redmine安裝目錄,可能會需要把prefix加上"/redmind/",成功後。

Step 5. Developer在git commit時message必須加上
       
git commit -m "Here is some description about this commit change. #${redmine-issue-id}"



你就會發現Redmine的issue就會自動根據commit message的ID進行更新,Good Luck!!



2 則留言:

  1. 請問您的git push指令是甚麼呢??
    因為我執行git push origin HEAD:refs/for/TEST1
    但是都沒有傳commit的更改描述,也就是沒有傳#${redmine-issue-id}
    我的Gerrit版本是2.11.3

    回覆刪除
  2. 快速回覆:
    1.與gerrit版本無關
    2.關於你使用git push無法使用,首先你要理解這篇文章的背後運作原理。如果你要讓兩個系統(gerrit、redmine)彼此有勾稽,是從gerrit的hook機制進行觸發,而hook的時間點將決定你使用何種git指令;這部分建議您可以先看一下我的另一篇文章http://sean666666.blogspot.tw/2016/03/gerrit-hooks-configuratoin.html

    回覆刪除