Java
Jenkins
SonarQube
コードレビュー
静的解析

Jenkins/SonarQubeではじめるお手軽コードレビュー:静的解析

はじめに

この記事では、グループ内の複数人で行う開発(プログラミング)フェーズ1内でコードの静的解析2を行い、新規に開発した箇所に含まれる問題を早期に取り除くために役立つ方法を紹介します。

前提

  • 「masterブランチから開発用ブランチを作成し、開発が終わればそのブランチからdevelopブランチに対してプルリクエストを送る」という開発フローを基本として想定しています。
  • reviewerの数がrevieweeに対して多くない状況を想定しています。

問題

  • reviewerの負担を減らす
    • 同じコーディングルール違反について何度も指摘した経験、ありませんか(ログの使い方、アノテーションの漏れ、etc.)?
    • SearchBucketで検索した時、同じキーワードで同じような指摘がいくつもヒットしませんか?
  • 意図しない不具合がリリースされることを未然に防ぐ
    • メモリリーク、NullPointerException、etc.
  • 新規に開発される箇所を対象にする
    • 既存の問題はいったん保留にしたいと思います。

SonarQube

上の問題の解決を目指して、今回はSonarQubeの導入・活用を試します。
SonarQubeは、一言で説明すれば「コードの品質を継続的に計測・管理するためのOSS」です。

  • 決められたコーディングルールに違反している箇所があればそれを洗い出して(解決方針とともに)提示してくれる
  • そうした違反箇所などを「技術的負債」として管理し、時系列で推移を記録してくれる

詳細は公式ドキュメントを参照して下さい。

SonarQube® software (previously called Sonar) is an open source quality management platform, dedicated to continuously analyze and measure technical quality, from project portfolio to method.

SonarQube Serverのインストール

まず、サーバを構築して、コード品質の計測結果を溜める場所を用意します。
詳細な手順は公式ドキュメントこちらの資料が詳しいです。

SonarQube Scannerによる静的解析と結果確認

サーバを構築できれば、次はいよいよコードの静的解析です。

静的解析: Maven

Analyzing with SonarQube Scanner for Mavenに従って設定した後、pom.xmlのあるディレクトリで

mvn sonar:sonar

を実行すると、静的解析が実行されます。

結果確認: webコンソール上で

その結果は、

sonar.host.url

で設定したURL以下で、このような画面として見ることができます。(画像はJavaのコードを対象にしたものではないようですが、参考まで。)

aMjqj.png

出典: https://stackoverflow.com/questions/36940993/vs-unit-test-results-file-trx-is-not-displayed-into-sonarqube

検出された問題とその解決方針は、例えばこのように提示されます。

Selection_964.jpg

出典: https://docs.sonarqube.org/display/SONARQUBE52/Technical+Debt

結果確認: IDEで

解析結果をSonarQube Server上に登録していれば、IDE上で確認することも可能です。そのためには、お使いのIDEに合わせて、SonarLintradar-netbeansをインストールします。
例えばNetBeansにradar-netbeansをインストールすると、このように問題("Issues")が指摘されます。コードの該当箇所と問題の詳細な記述の間を行き来しながらスムーズに修正を行うことができます。

1463108950_radar-thumbnail-2.PNG

出典: http://plugins.netbeans.org/plugin/51532/radar-netbeans

メリット・デメリット

実際にSonarQubeを導入して感じたメリットとデメリットを挙げてみます。

  • メリット
    • webコンソールやIDE上で開発中に素早く問題点を見つけること(そして修正すること)ができる。
  • デメリット
    • SonarQube Scannerについては、エンジニアがそれぞれインストール・設定をする必要がある。
    • 指摘された問題点を無視しようと思えば無視できる。

Jenkinsと組み合わせる

SonarQube Scannerについては、エンジニアがそれぞれインストール・設定をする必要がある。

というデメリットを軽減できるように、グループ共通で使っているJenkinsから手軽に実行できるジョブを作ることを考えてみます。

スクリプト

開発用ブランチごとに、以下のようにSonarQubeの静的解析を行うためのスクリプト(analyze.sh)を作成します。

  • masterブランチに対する解析を(まだ行っていなければ行う)
  • 開発用ブランチに対する解析を行う
  • sonar.projectKeyは開発用ブランチごとに一意
  • sonar.projectVersionはmasterブランチ解析時と開発用ブランチ解析時で変える
analyze.shの概要
#!/bin/sh -xe

(中略)

SONARQUBE_PROJECT_KEY_FROM_API=`curl -s -X GET "${SONARQUBE_HOST_URL}/api/components/show?key=${SONARQUBE_PROJECT_KEY}" | python [Path to file]/json_value.py component key`

# clone repository
git clone ssh://git@[Bitbucket SSH URL with port]/${BITBUCKET_PROJECT_NAME}/${REPOSITORY_NAME}.git
cd ${REPOSITORY_NAME}; pwd

# if key not exists in SonarQube server
if [ -z ${SONARQUBE_PROJECT_KEY_FROM_API} ]; then
    # (a) mvn clean package (baseline branch)
    git checkout ${GIT_BRANCH_BASELINE}
    ${MAVEN_COMMAND} clean package -Dmaven.test.skip=true

    # (b) run sonar:sonar to ${SONARQUBE_PROJECT_NAME} (baseline branch)
    ${MAVEN_COMMAND} sonar:sonar -Dsonar.projectKey=${SONARQUBE_PROJECT_KEY} -Dsonar.projectName=${SONARQUBE_PROJECT_NAME} -Dsonar.projectVersion=${SONAR_PROJECT_VERSION_BASELINE} -Dsonar.sources=src/main
fi

# (c) mvn clean package (developing branch)
git checkout ${GIT_BRANCH}
${MAVEN_COMMAND} clean package -Dmaven.test.skip=true

# (d) run sonar:sonar to ${SONARQUBE_PROJECT_NAME} (developing branch)
${MAVEN_COMMAND} sonar:sonar -Dsonar.projectKey=${SONARQUBE_PROJECT_KEY} -Dsonar.projectName=${SONARQUBE_PROJECT_NAME} -Dsonar.projectVersion=${SONAR_PROJECT_VERSION} -Dsonar.sources=src/main
json_value.py
# encoding: utf-8
import json
import sys

# Get JSON from stdin
dic = json.loads(raw_input())

# get keys from arguents
args = sys.argv
# args[0] is this program name
args.pop(0)

for key in args:
    if isinstance(dic , list): 
        # if current value is list, change key to int
        key=int(key)
    try:
        # set dic[key] as next value (or result)
        dic=dic[key]
    except: 
        # If failed, print empty string and exit. (might be on KeyError and IndexError)
        print ''
        sys.exit()

# print result
print dic

Jenkinsのジョブ設定

Freestyle jobを作成し、"This project is parameterized"にチェックを入れてスクリプトに必要なパラメータを指定できるようにします。

sonarqube_analyze-setting.JPG

Jenkinsのジョブ実行

ジョブを実行すると、webコンソール上に開発用ブランチに対応するダッシュボードが作成されます(もしまだなければ)。masterブランチと開発用ブランチの差分(新規に開発した箇所)で検出された問題が"New Issues"のところに出てきます。赤丸で囲んでいる部分をクリックすると、問題の詳細を確認できます。

new_issues.JPG

メリット・デメリット

  • メリット
    • 開発中のブランチ名などいくつかの項目を指定するだけで修正すべき問題点を見つけることができる。
  • デメリット
    • 指摘された問題点を無視しようと思えば無視できる。

まとめと今後の課題

「reviewerの負担を減らす」「意図しない不具合がリリースされることを未然に防ぐ」を目的に、Jenkins/SonarQubeを使って、新規に開発した箇所に含まれる問題を早期に取り除くために役立つ方法を紹介しました。

一方で、このような課題も見つかりました。

指摘された問題点を無視しようと思えば無視できる。

これはJenkinsやSonarQubeというよりは、むしろグループの事情によるところが大きいと思います。実際にJenkinsのジョブを使ったメンバーに話を聞いたりした上で、以下のような点がネックになりそうだと思っています。

  • 静的解析の重要性があまり理解・共有されていない
    • これまでの手動レビューで特に大きな問題はなかった(あるいはトラブルが起こったとしても対応できてきた)。
    • SonarQubeデフォルトのルールに基づく指摘は、必ずしもグループの現状に合っているとは言えない。
  • 「(reviewerではなく)revieweeの負担」はむしろ増えている
    • 開発途中に(他にやるべきことが多すぎて)このJenkinsジョブのことを思い出せない。
    • 思い出したとしても、スケジュール・スキル・既存コードとの整合性などの問題で積極的に直すことが難しい。

次回の関連記事では、

  • SonarQubeのルール選定
  • 自動化

のどちらかについて、改善の目途が立てば共有したいと思います。

関連する記事


  1. Javaを用いたウォーターフォール・モデル開発を想定して書いていますが、ウォーターフォール・モデルに限らず適用できると思いますし、SonarQube自体はJava以外の言語にも対応させることが可能です。 

  2. コンピュータのソフトウェアの解析手法の一種であり、実行ファイルを実行することなく解析を行うこと。(Wikipediaより)