GitLabの静的コード解析機能であるCode Qualityを活用する方法を紹介します。また、GitLab標準のCode Qualityではなく、軽量なCode Qualityの設定方法も合わせて紹介します。
このブログのサンプルコードはJavaですが、他の言語でも同じようなことができるので、他の言語の方も参考にしてください。
GitLab Code Qualityとは?
GitLab Code Qualityは静的コード解析をGitLab CIで実行し、その結果をマージリクエストなどで確認できる機能です。
Code Qualityを利用すると、次のように静的コード解析の結果がマージリクエストの概要で確認できます。この例では、Sample.java
ファイルの一行目にJavadocコメントがないと指摘されています。
マージリクエストのCode Quality結果
実際に触ってみたい方は、お試しのマージリクエストにアクセスし、コードの品質 1ポイント悪化
の行を展開
ボタンで展開してください。
なお、このマージリクエストの概要に静的コード解析の結果が表示される機能は、無償のGitLabでも利用できます。それ以外の機能については有償の契約が必要です。ただし、 GitLab.comの公開プロジェクトには、プロジェクトレベルのUltimateプランの機能が開放されている ので、OSSの開発やUltimateプランの機能を試してみたいなあというときにはGitLab.comの公開プロジェクトを利用してください。
有償のUltimateプランを利用していれば、次のようにマージリクエストの変更タブのコード差分上でも確認ができます。
コード差分でのCode Quality結果
お試しのマージリクエストにアクセスし、変更
タブでコード差分を表示してください。
このブログの執筆時点で、GitLabにはコード差分のインラインビューでCode Quality結果が表示されないバグがあります。うまく表示できない場合は、並列ビューに切り替えてください。
有償のPremium以上のプランを利用していれば、次のようにパイプラインの詳細ページでも確認ができます。
パイプライン詳細でのCode Quality結果
お試しのパイプライン詳細ページにアクセスしてください。
静的コード解析とは?
静的コード解析とは、プログラムを実行せずにソースコードの問題を検出することです。一般的に、人の目で問題を探し出すのはコードレビューと呼ばれ、静的コード解析用のツールを使うことを静的コード解析と区別することが多いです。
例えば、次のJavaのコードは括弧の位置が一般的ではありません。
class Sample
{
}
静的コード解析ツールはこのようなソースコードの問題を検出します。次はJavaの静的コード解析ツールの一つ、Checkstyleの実行結果です。
[ant:checkstyle] [WARN] /home/masakura/works/blog/code-quality/code-quality-java-sample/src/main/java/Sample.java:2:1: 'class def lcurly' のインデントレベル 0 は正しくありません。期待されるレベルは 2 です。 [Indentation]
[ant:checkstyle] [WARN] /home/masakura/works/blog/code-quality/code-quality-java-sample/src/main/java/Sample.java:2:1: 列 1 の '{' は前の行に書いてください。 [LeftCurly]
[ant:checkstyle] [WARN] /home/masakura/works/blog/code-quality/code-quality-java-sample/src/main/java/Sample.java:3:1: 'class def rcurly' のインデントレベル 0 は正しくありません。期待されるレベルは 2 です。 [Indentation]
Checkstyleはコーディングスタイル違反の検出が主ですが、SpotBugsのように、バグと思われる、もしくはバグを引き起こしやすいコードの問題を検出するツールもあります。
次のコードはJavaでコードを書いているときにありがちなミスです。
if (status == "active") {
// ...
}
SpotBugsは次のように問題があることを指摘します。
H B ES: String パラメータを == や != を使用して比較しています。Sample.main(String) 該当箇所 Sample.java:[line 3]
もちろん、このような問題をコードレビューで指摘することができますが、レビュアーにもレビューイにも大きな負荷がかかります。
括弧の位置の間違いのような些細な問題と、分かりにくい名前・アルゴリズムの間違い・仕様違反では、後者のほうが重要であることはみなさんおわかりでしょう。しかし、前者はとにかく数が多くなりがちで、コードレビューでの指摘では数の少ない後者の問題が埋もれがちになります。最悪、重要な問題が無視されることに繋がりかねません。
静的コード解析ツールを活用すると、このような些細で数の多い問題をコードレビューに回す前に解決することができます。つまり、コードレビューではより重要な問題に集中できるようになるわけですね。
余談ですが、私はいろんなプロジェクトに並行して参加しています。プログラム言語はそれぞれ違いますし、たとえ同じであってもコーディング規約が異なります。静的コード解析ツールのおかげでなんとかなってるところもあります。また、初めて使う言語でも、やってはいけない書き方を指摘してくれたりするので、とても助かっています。
GitLab標準のCode Qualityの問題
GitLab Code Qualityを使うと、静的コード解析をGitLab上で完結できます。プロジェクトに組み込むのも、公式ドキュメントにあるとおり、.gitlab-ci.yml
ファイルに次のように追加するだけです。
include:
- template: Code-Quality.gitlab-ci.yml
これだけで、冒頭で紹介したCode Qualityが使えるようになります。
大変お手軽なのですが、Code Qualityは内部でCode Climateを利用しているためか、処理に時間がかかります。(GitLab.comの共有Runnerでは6分程度でした)また、Docker Engineが利用できるGitLab Runnerが必要です。(GitLab.comの共有Runnerでは利用できます)
しかし、実はGitLabのCode QualityにCode Climateは必須ではありません。必要なことは、GitLab CIジョブで静的コード解析を実行することと、そのレポートをCode Quality形式のJSONで出力することの二点だけです。
つまり、Checkstyleなどの静的コード解析ツールを実行し、そのレポートをCode Quality形式のJSONに変換するようにGitLab CIジョブを設定すれば、Code Qualityの機能を使うことができるわけです。
CIジョブの設定を自力で書く必要がありますが、Code Climateを使わないことで、実行時間が短く、Docker Engineが利用できない自前のGitLab Runnerでも使えるCode Qualityが手に入ります。
このブログではこのCode Climateを使わない方法を軽量Code Climateと呼ぶことにします。その方法を紹介します。
軽量Code Qualityの設定
以下のものは構築済みとします。
- JDK / Gradle
- Git
- GitLabプロジェクト
なお、いちいちプロジェクト作成して設定するのだるいという方のために、サンプルプロジェクトを用意しています。次の手順に従ってプロジェクトを作成してください。
- GitLabにアクセスして、上部メニューの
+
ボタンからNew project/repository
をクリックします プロジェクトのインポート
->リポジトリ URL
をクリックしますGit リポジトリ URL
にhttps://gitlab.com/creationline/code-quality-java-sample.git
を入力し、プロジェクト名
やProject slug
や可視レベル
を適当に入力し、プロジェクトを作成
ボタンをクリックしてプロジェクトを作成します- 左側のメニューの
CI/CD
->パイプライン
より、Run pipeline
をクリックします Run for branch name
でmaster
を選んでRun pipeline
をクリックし、パイプラインを実行します- 4 と 5 の同じ手順で、
branch1
のパイプラインも実行します - 左側のメニューの
マージリクエスト
を選んで、新規マージリクエスト
をクリックします ソースブランチを選択
をクリックして、branch1
を選んだ後、Compare branches and continue
->Create マージリクエスト
をクリックし、マージリクエストを作成します- パイプラインが完了すると、マージリクエストの概要でCode Qualityの結果が確認できるようになります
対象となるGradleプロジェクトを作成
静的コード解析を行う対象のプロジェクトをまずは作成します。
プロジェクトの作業ディレクトリを作成して、次の内容でbuild.gralde
ファイルを作成して下さい。
plugins {
id 'java'
}
repositories {
mavenCentral()
}
src/main/java/Sample.java
ファイルを作成します。ここでは、静的コード検査に引っかかるようにわざと括弧の位置を間違えています。
class Sample
{
}
プロジェクトの作成できました。念の為、ビルドできるか試してみましょう。
$ gradle build --continue
BUILD SUCCESSFUL in 1s
2 actionable tasks: 2 executed
Checkstyleの組み込み
Code Qualityを実行する前に、静的コード解析ツールを組み込まなければいけません。このブログではCheckstyleを利用します。
組み込むにはbuild.gralde
ファイルに追記していくことになるのですが、何でもかんでもbuild.gradle
に書いていくと肥大化しまい、あとあとメンテナンスが大変になるので、Code Qualityに関する設定は別のファイルに分離することにします。(このサンプルレベルだとそれほど意味はありませんが…)
gradle/codequality.gradle
ファイルを次の内容で作成します。
apply plugin: 'checkstyle'
checkstyle {
toolVersion = '9.0'
ignoreFailures = false
maxWarnings = 0
}
build.gradle
ファイルでこれを読み込むように修正します。
plugins {
id 'java'
}
repositories {
mavenCentral()
}
apply from: 'gradle/codequality.gradle'
Checkstyleは検査ルールが必要です。今回はGoogle Java Style GuideのCheckstyleルールgoogle_checks.xmlを利用します。ダウンロードして、config/checkstyle/checkstyle.xml
に保存してください。
これでCheckstyleの組み込みが完了しました。build
タスクを実行して、Checkstyleで静的コード解析を行います。
$ gradle build --continue
> Task :checkstyleMain FAILED
[ant:checkstyle] [WARN] /home/masakura/tmp/project1/src/main/java/Sample.java:2:1: 'class def lcurly' has incorrect indentation level 0, expected level should be 2. [Indentation]
[ant:checkstyle] [WARN] /home/masakura/tmp/project1/src/main/java/Sample.java:2:1: '{' at column 1 should be on the previous line. [LeftCurly]
[ant:checkstyle] [WARN] /home/masakura/tmp/project1/src/main/java/Sample.java:3:1: 'class def rcurly' has incorrect indentation level 0, expected level should be 2. [Indentation]
FAILURE: Build failed with an exception.
...
実行結果にあるとおり、括弧の位置がおかしいよと指摘されています。
build/reports/checkstyle
ディレクトリに生データのXMLファイルと、人が見てわかるレポートのHTMLファイルが生成されていますので、指摘の数が多いときはこのHTMLレポートを見ると良いでしょう。
CheckstyleのHTMLレポート
Code Quality形式にレポートを変換
説明したとおり、Code Qualityを利用するために、静的コード解析の結果をCode Quality形式のJSONに変換しなければなりません。
ちなみに、Code Qulaity形式のJSONはCode Climate形式のサブセットなので、様々なレポートをCode Climate形式に変換できるGradleプラグイン、Violations Gradle Pluginが使えます。
gradle/codequality.gradle
ファイルを次のように修正します。
buildscript {
repositories {
maven { url 'https://plugins.gradle.org/m2/' }
}
dependencies {
classpath "se.bjurr.violations:violations-gradle-plugin:1.50.16"
}
}
apply plugin: 'checkstyle'
checkstyle {
toolVersion = '9.0'
ignoreFailures = false
maxWarnings = 0
}
task codequality(type: se.bjurr.violations.gradle.plugin.ViolationsTask) {
codeClimateFile = file 'build/reports/codequality.json'
violations = [
['CHECKSTYLE', buildDir.path, '.*/checkstyle/.*\\.xml$', 'Checkstyle']
]
}
checkstyleMain.finalizedBy codequality
checkstyleTest.finalizedBy codequality
タスクを実行します。
$ gradle build --continue
...
* What went wrong:
Execution failed for task ':checkstyleMain'.
> Checkstyle rule violations were found. See the report at: file:///home/.../project1/build/reports/checkstyle/main.html
Checkstyle files with violations: 1
Checkstyle violations by severity: [warning:3]
...
次のようにCode Quality形式のbuild/reports/codequality.json
ファイルが生成されているはずです。
[
{
"description": "\u0027class def rcurly\u0027 has incorrect indentation level 0, expected level should be 2.",
"fingerprint": "b17a1fc2c977f0eabf09c43918de5b01c2f1c7402220adaae9da47a67c776388",
"location": {
"path": "/home/.../project1/src/main/java/Sample.java",
"lines": {
"begin": 3
}
},
...
GitLab CIジョブで軽量Code Qualityを実行する
静的コード解析ツールの実行とレポートの変換までできましたので、GitLab CIジョブでこれを実行するようにします。
次の内容で.gitlab-ci.yml
ファイルを作成してください。
image: gradle
build:
script:
- gradle build --continue
artifacts:
reports:
codequality: build/reports/codequality.json
artifacts:reports:codequality
でCode Quality形式のレポートファイルを成果物に含めています。この指定で、GitLabはレポートをマージリクエストなどに表示できるようになります。
余談ですが、artifacts:reports
で指定できるのはcodequality
だけではありません。単体テストレポート用のjunit
などもあります。興味がある方は公式リファレンスをご覧ください。
修正がおわったら、コミットしてGitLabプロジェクトにプッシュしてください。
GitLabプロジェクトをウェブブラウザーで開いて、左のメニューよりパイプライン
を選択します。おそらくパイプラインは青い丸の実行中になっていると思います。パイプラインが完了すると赤いバツ(失敗)にステータスが変わります。
パイプラインの完了後に、各パイプラインの右にあるケバブメニューから、Code Quality形式のレポートファイルをダウロードできるようになります。
Code Quality形式のJSONファイルのダウンロード
有償のPremium以上のプランを利用していれば、該当するパイプラインをクリックして詳細ページを開くと、Code Quality
タブで静的コード解析のすべての結果にアクセスできます。
パイプライン詳細でのCode Quality結果
レポート内のコードファイルパスをCode Qualityで扱えるようにする
Code Qualityの設定は以上で完了しています。ですので、あとはマージリクエストを作って静的コード解析結果を確認するだけなのですが…
残念ながらそうもいきませんでした。
今回変換したcodequality.json
ファイルでは、問題のあるソースコードのファイルパスは絶対パスになっていました。
"location": {
"path": "/home/.../project1/src/main/java/Sample.java",
"lines": {
"begin": 3
}
}
ですが、Code Qualityは次のようにプロジェクトルートからの相対パスであることを期待しています。
"location": {
"path": "src/main/java/Sample.java",
"lines": {
"begin": 3
}
}
Violationsは元の静的コード解析結果にあるファイルパスをそのまま利用します。CheckstyleのXMLレポートは絶対パスとなっていますので、codequality.json
も絶対パスになってしまうわけです。
面倒なのですが、ファイルパスを相対指定に変換するコードを追加します。gradle/codequality.gradle
ファイルを次のように修正してください。
import groovy.json.*
buildscript {
repositories {
maven { url 'https://plugins.gradle.org/m2/' }
}
dependencies {
classpath "se.bjurr.violations:violations-gradle-plugin:1.50.16"
}
}
apply plugin: 'checkstyle'
checkstyle {
toolVersion = '9.0'
ignoreFailures = false
maxWarnings = 0
}
task codequality(type: se.bjurr.violations.gradle.plugin.ViolationsTask) {
codeClimateFile = file 'build/reports/codequality.json'
violations = [
['CHECKSTYLE', buildDir.path, '.*/checkstyle/.*\\.xml$', 'Checkstyle']
]
doLast {
def file = new File('build/reports/codequality.json')
def json = new JsonSlurper().parse(file)
for (point in json) {
point.location.path = point.location.path.replace("${rootProject.rootDir}/", '')
}
file.createNewFile()
file.text = new JsonGenerator.Options()
.disableUnicodeEscaping()
.build()
.toJson(json)
}
}
checkstyleMain.finalizedBy codequality
checkstyleTest.finalizedBy codequality
doLast
でViolationsプラグインでCode Quality形式に変換した後に、codequality.json
を読み込んで、絶対パスを相対パスに書き換えた上で上書きしています。
修正したらタスクを実行して確認します。
$ gradle build --continue
見事に変換できました!
"location": {
"path": "src/main/java/Sample.java",
"lines": {
"begin": 3
}
}
なお、ファイルパスを変換後のファイルは改行が取り除かれ、一行になっています。そのままでは読みにくいので、整形しています。
マージリクエストで静的コード解析の結果を確認する
さて、今度こそ設定は終わったので、マージリクエストを作成して、Code Qualityを使ってみましょう。
その前に、src/main/java/Sample.java
ファイルを修正して、コードの問題をすべて解消します。
class Sample {
}
タスクを実行して、問題がないことを確認してください。
$ gradle build --continue
BUILD SUCCESSFUL in 1s
4 actionable tasks: 3 executed, 1 up-to-date
問題がなければ、コミットしてプッシュしてください。
これで準備完了しました!マージリクエストを作成し、Code Qualityを堪能していきます。
まずはブランチを作成します。
$ git checkout -b branch1
src/main/java/Sample.java
ファイルを次のように修正してください。(アクセス修飾子にpublic
を追加しています)
public class Sample {
}
括弧の位置がおかしいエラーだと新鮮味がないので、Google Java Style Guideの、公開クラスにはJavadocコメントが必要というルールに違反してみました。
タスクを実行すると、Javadocコメントを書くように指摘されています。
$ gradle build --continue
> Task :checkstyleMain FAILED
[ant:checkstyle] [WARN] /home/masakura/tmp/project1/src/main/java/Sample.java:1:1: Missing a Javadoc comment. [MissingJavadocType]
FAILURE: Build failed with an exception.
...
コミットしてプッシュしてください。そして、GitLabプロジェクトの左側のメニューからマージリクエストを選んで、マージリクエストを作成してください。
マージリクエストのパイプラインが完了するまでしばらく待ちます。完了すると、マージリクエストのパイプラインの青い丸アイコンが緑の丸アイコンか(成功時)、赤いバツアイコン(失敗時)にかわります。青い丸アイコンのまま終わらないときは、ページをリロードしてください。
マージリクエストのパイプラインが完了(失敗した)
パイプラインが完了したら、コードの品質...
の右の展開
ボタンをクリックして、Code Qualityの結果を確認できます。
マージリクエストのCode Quality結果
もし、Ultimateプランを利用している場合は、マージリクエストを変更タブに切り替えることで、次のようにコード差分でも確認できるようになります。(GitLab.comの公開プロジェクトでも可)
コード差分でのCode Quality結果
最初の方で解説したとおり、インラインビューではバグがあります。表示されないなと思ったら、並列ビューに切り替えてください。
実際の開発では、このマージリクエストの結果を受けて、静的コード解析の指摘をすべて修正してからコードレビューを依頼することになります。コードレビューでは、静的コード解析ツールでは検出しづらい、不適切な名前やアルゴリズムの不備などのより重要な指摘に集中できます。
ちなみに、マージリクエストに表示されるコードの問題には、元のブランチにあるコードの問題は含まれません。マージリクエストの変更で新たに発生したコードの問題だけが表示されます。
すべての静的コード解析の指摘を取得するには、GitLabプロジェクト左側のメニューよりパイプライン
を選んで、該当するパイプラインの右端にあるケバブメニューをクリックして、アーティファクトのダウンロード
でbuild:codequality
をダウンロードしてください。
Code Quality形式のJSONファイルのダウンロード
有償のPremiumプランを利用している場合は、パイプライン一覧で選択したパイプライン詳細ページのCode Quality
タブが利用できます。(GitLab.comの公開プロジェクトでも可)
パイプライン詳細でのCode Quality結果
最後に
軽量Code Qualityいかがだったでしょうか?
GitLab標準のそれと比べると設定が面倒ですが、CIジョブの実行に時間がかからないのは大きなメリットです。CIを活用しているチームにはこの軽量Code Qualityをおすすめします。
Code Qualityとは直接は関係ありませんが、静的コード解析ツールは利用している統合開発環境やエディターに必ず組み込んでください。
統合開発環境にCheckstyleを組み込み
1,000行くらいのコードを書き終えて、GitLabにプッシュした後で「30箇所くらい括弧の位置を間違えてるから直してください」とCIから指摘されたらと、想像してみてください。修正だけならいいのですが、テストもやり直しです。とはいえ、やる気が出ないので修正後のテストをつい省いてしまってあとで痛い目を見ることになります。
統合開発環境やエディターに組み込んでおけば、コードを書いている最中に「ここ括弧の位置を間違えてるから直してください」と指摘を受けることができます。開発者はその場で間違いを修正できます。
改めて。静的コード解析ツールは利用している統合開発環境やエディターに必ず組み込んでください。
それならCode Qualityは不要なんじゃと思うかもしれません。しかし、統合開発環境やエディターに組み込んだ静的コード解析ツールは開いているファイルのみを解析することが多いため、どうしても漏れが出ます。その保険としてパイプラインでCode Qualityを実行する必要があります。
まとめです。
- 静的コード解析ツールを使いましょう!
- 統合開発環境やエディターに静的コード解析ツールを組み込みましょう!
- Code Qualityを活用しましょう!
おまけ
SpotBugsやその他の場合
SpotBugsでもCheckstyleのときと同じようにCode Qualityを利用できます。
しかし、Cyeckstyleより厄介です。問題のあるソースコードのファイルパスが、Javaパッケージのルートディレクトリからの相対パスになっているからです。(src/main/java
やtest/main/java
基準となる)
"location": {
"path": "Sample.java",
"lines": {
"begin": 3
}
},
ファイルパスを変換する必要があるのはCheckstyleと同じですが、src/main/java
を追加する必要があります。ですが、test/main/java
や他の場合もあるので、一筋縄ではいきません。
つまり、静的コード解析ツールごとにこのあたりを調整する必要があります。そこがこの方法の欠点です。どなたかがViolationsでファイルパス変換機能が実装されるか、別の変換ツールの登場を待ちましょう!
消極的な対処方法として、ファイルパスを変換しないという手もあります。マージリクエストのコード差分で静的コード解析の結果が確認できなくなったり、問題のあるファイルへのリンクをクリックしても別のページが表示されたりしますが、気にしなければそれでいいのかもしれません。
無償のGitLabですべての静的コード解析結果にアクセスする
無償のGitLabでは、パイプライン詳細ページにあるCode Quality
タブがありません。マージリクエストの概要タブに表示される分だけになりますが、これはあくまでマージリクエストで新たに発生した問題だけが表示されます。
GitLabはGitLab CIの成果物にアクセスする機能があります。この成果物にCheckstyleが出力した人が読める形式のHTMLレポートを含めると、無償のGitLabでもマージリクエストやパイプラインから省略されていない静的コード解析の結果にアクセスできるようになります。
次のようにして、.gitlab-ci.yml
でartifacts:paths
でHTML形式のレポートファイルを成果物に含めます。
image: gradle
build:
script:
- gradle build --continue
artifacts:
expose_as: codequality
paths:
- build/reports/checkstyle/main.html
reports:
codequality: build/reports/codequality.json
また、artifacts:expose_as
を指定すると、マージリクエストにリンクが表示されるので、少し便利です。