SonarQubeという静的コード解析ツールがありますが、
解析結果をGitHubのPullRequestステータスに連携すると、
開発フローのプロセスに、コードの品質チェックを組み込むことができます。

SonarQube
https://www.sonarqube.org/

このエントリでは、SonarQubeとGitHubのAPIを利用して、
SonarQubeの解析結果を、GitHubのPullRequestのステータスに反映する手順を説明します。

前提とする環境:
SonarQube 7.0
※ブランチ毎にSonarQube ScannerでSonarQubeに解析結果を連携済みのこと。
※GitHubでPullRequstによるレビューを運用していること。

SonarQubeとGitHub連携には、
SonarQubeでの指摘をPullRequestにコメントする方法もありますが。
このエントリでは、
品質基準を満たしていないPullRequestのステータスをfailureにして
masterブランチへ、mergeさせ無いように連携させることにします。

以下の流れで連携させていきます。

  • SonarQubeの解析結果の取得
  • GitHub PullRequestへのステータス設定

ここでは、以下のいずれかの条件に合致する場合はstatusを失敗にします。

  • QualityGateでERRORが存在する
  • BLOCKER, CRITICALレベルのIssueが1件でも存在する

SonarQubeの解析結果の取得

まず、SonarQubeAPIを使って解析結果を取得します。
QualityGateの結果、Issueの件数を順に取得していきます。

SonarQubeAPIの呼び出し方

SonarQubeAPIの呼び出し方は、以下のURLの通りです。

Web API | Doc SonarQube
https://docs.sonarqube.org/display/DEV/Web+API

ログインIDとパスワードを使う場合:
※$SONARQUBE_HOST/MY_LOGIN/MY_PASSWORDは自分の環境のものに置き換えます。

curl -u MY_LOGIN:MY_PASSWORD https://$SONARQUBE_HOST/api/user_tokens/search

tokenを使う場合:
※$SONARQUBE_HOST/SONARQUBE_TOKENは自分の環境のものに置き換えます。

curl -u SONARQUBE_TOKEN https://$SONARQUBE_HOST/api/user_tokens/search

APIによるQualityGate結果の取得

SonarQubeには、QualityGateという
コード品質のプラクティスを遵守させるためのQualityGateという仕組みがあります。
※具体的には、新規追加コードのテストカバレージ・コピー&ペーストしたコードの量などの基準が指定できます。

SonarQubeAPIで、この解析結果を取得することにします。

以下のようにするとQualityGateの結果を取得できます。
※$SONARQUBE_HOST/$PROJECT_KEY/SONARQUBE_TOKENは自分の環境のものに置き換えます。

curl -u $SONARQUBE_TOKEN https://$SONARQUBE_HOST/api/qualitygates/project_status?projectKey=$PROJECT_KEY

ここでは、OK/ERRORのステータスだけあれば十分なので、以下のようにjqで加工します。

curl -u $SONARQUBE_TOKEN https://$SONARQUBE_HOST/api/qualitygates/project_status?projectKey=$PROJECT_KEY | jq -r '.projectStatus.status'

後で使いやすいように、シェルの関数にまとめておきます。

#!/bin/bash
get_sonarqube_qualitygate_status() {
  SONARQUBE_HOST=$1
  SONARQUBE_TOKEN=$2
  PROJECT_KEY=$3
  REQUEST_URL="https://$SONARQUBE_HOST/api/qualitygates/project_status?projectKey=$PROJECT_KEY"
  STATUS=$(curl -u $SONARQUBE_TOKEN -s $REQUEST_URL | jq -r '.projectStatus.status')
  echo $STATUS
}

get_sonarqube_qualitygate_status $SONARQUBE_HOST $SONARQUBE_TOKEN $PROJECT_KEY

マニュアルからは、このAPIの記述が見つけられなかったのですが、
以下のissueに使用例が書かれていました。

api/resources is deprecated since SonarQube 5.6 | GitHub
https://github.com/andnyb/sonarqube-slack-pusher/issues/20

APIによるIssue結果の取得

次にIssue(SonarQubeによる指摘事項)の件数を、取得することにします。

以下のようにするとIssueの一覧を取得できます。
※$SONARQUBE_HOST/$PROJECT_KEY/$SONARQUBE_TOKENは自分の環境のものに置き換えます。

curl -u $SONARQUBE_TOKEN -s "https://$SONARQUBE_HOST/api/issues/search?componentRoots=$PROJECT_KEY&severities=BLOCKER,CRITICAL&statuses=OPEN,REOPENED"

jqで加工し、Issueの件数を数えます。

curl -u $SONARQUBE_TOKEN -s "https://$SONARQUBE_HOST/api/issues/search?componentRoots=$PROJECT_KEY&severities=BLOCKER,CRITICAL&statuses=OPEN,REOPENED" | jq '.issues[].key' | wc -l

こちらも、後で使いやすいようにシェルの関数にまとめておきます。

#!/bin/bash
count_sonarqube_issues() {
  SONARQUBE_HOST=$1
  SONARQUBE_TOKEN=$2
  PROJECT_KEY=$3
  SEVERITIES=$4
  REQUEST_URL="https://$SONARQUBE_HOST/api/issues/search?componentRoots=$PROJECT_KEY&severities=$SEVERITIES&statuses=OPEN,REOPENED"
  COUNT=$(curl -u $SONARQUBE_TOKEN -s $REQUEST_URL | jq '.issues[].key' | wc -l)
  echo $COUNT
}

count_sonarqube_issues $SONARQUBE_HOST $SONARQUBE_TOKEN $PROJECT_KEY BLOCKER,CRITICAL

このAPIの使い方は以下を参照してください。

Web Service /api/issues | Doc SonarQube
https://docs.sonarqube.org/pages/viewpage.action?pageId=2392181

GitHub PullRequestへのステータス設定

次に、取得した解析結果をGitHubのステータスに反映します。

GitHubAPIによるStatusの設定方法

GitHubAPIによるStatusの設定方法は、以下のURLの通りです。

REST API v3 | GitHub Developer
https://developer.github.com/v3/

Statuses | GitHub REST API v3
https://developer.github.com/v3/repos/statuses/

具体的には、以下のように呼び出します。

curl -u $GH_ACCESS_TOKEN \
     -X POST \
     --data "{\"state\":\"${GH_STATE}\", \"target_url\":\"${GH_TARGET_URL}\", \"description\":\"${GH_DESCRIPTION}\", \"context\":\"${GH_CONTEXT}\"}" \
     https://api.github.com/repos/$GH_PROJECT_USERNAME/$GH_PROJECT_REPONAME/statuses/${GIT_SHA1}

変数は、それぞれ以下のように置き換えます。

  • $GH_ACCESS_TOKENは、GitHubの設定からpersonal access tokenを取得したものに置き換え。
  • $GH_STATEは、success/failureのいずれか
  • $GH_TARGET_URLは、statusにつけるリンク先(SonarQubeのDashboardなど)
  • $GH_DESCRIPTIONは、ステータスの説明(BLOCKER/CRITICALのissue数など)
  • $GH_CONTEXTは、ステータスを識別する名称(「sonarqube-status」などを指定)
  • $GH_PROJECT_USERNAME,$GH_PROJECT_REPONAMEは、GitHubリポジトリの名称
  • $GIT_SHA1は、GitのコミットのSHA1

これもシェルの関数にしておきます。

#!/bin/bash
gh_status_post() {
  GH_ACCESS_TOKEN=$1
  GH_PROJECT_USERNAME=$2
  GH_PROJECT_REPONAME=$3
  GIT_SHA1=$4
  GH_STATE=$5
  GH_TARGET_URL=$6
  GH_DESCRIPTION=$7
  GH_CONTEXT=$8
  POST_URL=https://api.github.com/repos/${$GH_PROJECT_USERNAME}/${GH_PROJECT_REPONAME}/statuses/${GIT_SHA1}
  curl -u $GH_ACCESS_TOKEN \
       -X POST \
       --data "{\"state\":\"${GH_STATE}\", \"target_url\":\"${GH_TARGET_URL}\", \"description\":\"${GH_DESCRIPTION}\", \"context\":\"${GH_CONTEXT}\"}" \
       $POST_URL
}

gh_status_post $GH_ACCESS_TOKEN $GH_PROJECT_USERNAME $GH_PROJECT_REPONAME $GIT_SHA1 $GH_STATE $GH_TARGET_URL $GH_DESCRIPTION $GH_CONTEXT

SonarQube解析結果をステータスに反映

ここまでの内容をまとめると、
SonarQubeの解析結果をGitHubのステータスに反映できます。

#!/bin/bash
get_sonarqube_qualitygate_status() {
  ※同じなので省略
}

count_sonarqube_issues() {
  ※同じなので省略
}

gh_status_post() {
  ※同じなので省略
}

SONARQUBE_HOST=<SonarQubeホスト名を指定>
SONARQUBE_TOKEN=<SonarQubeトークンを指定>
PROJECT_KEY=<SonarQubeのキー名を指定>

GH_ACCESS_TOKEN=<GitHubのトークンを指定>
GH_PROJECT_USERNAME=<GitHubリポジトリのUser/Organization名を指定>
GH_PROJECT_REPONAME=<GitHubリポジトリ名を指定>
GIT_SHA1=<GitのCommitのSHA1を指定>
GH_CONTEXT=<GitHubステータスの識別名を指定>

QG_STATUS=$(get_sonarqube_qualitygate_status $SONARQUBE_HOST $SONARQUBE_TOKEN $PROJECT_KEY)
CRITICAL_CNT=$(count_sonarqube_issues $SONARQUBE_HOST $SONARQUBE_TOKEN $PROJECT_KEY BLOCKER,CRITICAL)

GH_STATE=success
if [ "$QG_STATUS" = "ERROR" ]; then
  GH_STATE=failure
  GH_DESCRIPTION="Quality Gate Failed, $CRITICAL_CNT Critical Issues"
else
  GH_DESCRIPTION="Quality Gate Passed, $CRITICAL_CNT Critical Issues"
fi
if [ $CRITICAL_CNT -gt 0 ]; then
  GH_STATE=failure
fi
GH_TARGET_URL=https://$SONARQUBE_HOST/dashboard?id=$PROJECT_KEY

gh_status_post $GH_ACCESS_TOKEN $GH_PROJECT_USERNAME $GH_PROJECT_REPONAME $GIT_SHA1 $GH_STATE $GH_TARGET_URL "$GH_DESCRIPTION" $GH_CONTEXT

このスクリプトをCIツールに組み込み、
SonarQube Scannerを実行した後で呼び出せばステータスが反映されるようになります。
※リポジトリ名やブランチ名は利用しているCIツールの仕様に合わせて取得してください。

仕上げにGitHubのProtectedBranch機能で、
ここで作成したステータスを必須にすれば、
開発フローでSonarQubeの品質チェックを遵守させることが出来るようになります。

Enabling required status checks | GitHub Help
https://help.github.com/articles/enabling-required-status-checks/