理系学生日記

おまえはいつまで学生気分なのか

GitLab CI/CDのCode QualityでCheckstyleのレポートを表示する

何をするにも「わかりやすく表示する」というのは重要です。 Checkstyleについても、その結果をGitLabやGithubでわかりやすく表示できれば、「これは直さないと」とチーム内で共有できるでしょう。

GitLabでCheckstyleのレポートを表示できるようにする

GitLabにはコード品質を表示する仕組みがあります。

今回はこの機能を利用して、以下のようにGitLab CI/CDのパイプラインにCheckstyleのレポートを表示できるようにしてみます。

実現に向けた、GitLabにおけるCode Qualityの基本知識

GitLabのCode Qualityは、静的コード解析の結果をマージリクエスト等で確認できるようにする仕組みです。 Code Quality自体は、内部でCode Climateのエンジンを使用しています。

Code Climateはコード品質を分析・定量化してくれるサービスです。 もちろん、分析・定量化のためには、コードにどのような問題があるのかを検知する必要があります。 そして、Code Climateエンジンの仕様は以下で公開されており、コードの問題に関するレポート仕様も公開されています。

{
"type": "issue",
"check_name": "Bug Risk/Unused Variable",
"description": "Unused local variable `foo`",
"content": Content,
"categories": ["Complexity"],
"location": Location,
"other_locations": [Location],
"remediation_points": 50000,
"severity": Severity,
"fingerprint": "abcd1234"
}

ここまでわかれば、Checkstyleで検知された問題を、上記の形式に変換すれば良いのでは? というところまで考えが至ります。

Checkstyleの問題をCode Climate形式に変換する

Checkstyleの出力形式

Checkstyleの課題レポートの形式は、一般にはXMLです。 maven-checkstyle-pluginでは、 デフォルトで${project.build.directory}/checkstyle-result.xmlへ 出力される設定になっています。

<?xml version="1.0" encoding="UTF-8"?>
<checkstyle version="10.0">
<file name="/Users/kiririmode/src/github.com/kiririmode/hobby/app/movie-uploader/src/main/java/de/kiririmo/memory/MovieUploader.java">
</file>
</checkstyle>

Code Climate形式への変換

Mavenで、CheckstyleやSpotbugs等のレポートをCode Climate形式に変換するMavenプラグインがあります。 それがviolations-maven-pluginです。 violations-maven-pluginsは、Checkstyleだけでなく、SpotbugsやJUnitのレポートもCode Climate形式に変換できます。

これを組み込んでみましょう。pom.xmlのbuild pluginsとして、verifyフェーズへ組み込むようにしました。 codeClimateFileで指定するのが、最終的なCode Climate形式のJSONファイルです。

diff --git a/app/movie-uploader/pom.xml b/app/movie-uploader/pom.xml
index 9a57854..f919411 100644
--- a/app/movie-uploader/pom.xml
+++ b/app/movie-uploader/pom.xml
@@ -137,6 +138,31 @@
           </execution>
         </executions>
       </plugin>
+
+      <plugin>
+        <groupId>se.bjurr.violations</groupId>
+        <artifactId>violations-maven-plugin</artifactId>
+        <version>1.48</version>
+        <executions>
+          <execution>
+            <phase>verify</phase>
+            <goals>
+              <goal>violations</goal>
+            </goals>
+            <configuration>
+              <codeClimateFile>${project.build.directory}/codeclimate.json</codeClimateFile>
+              <violations>
+                <violation>
+                  <parser>CHECKSTYLE</parser>
+                  <reporter>Checkstyle</reporter>
+                  <folder>.</folder>
+                  <pattern>.*/checkstyle-result.xml</pattern>
+                </violation>
+              </violations>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
     </plugins>
   </build>

GitLab CI/CDへの組み込み

pom.xml用のお膳立てができたら、あとはGitLab CI/CDに組み込めばOKです。 ここに何点か注意点があります。

Code Climate形式のファイルパスは相対パス

Code Climate形式のJSONファイルにおいて、ファイルパスは相対パスで表現しなければなりません。

All Locations require a path property, which is the file path relative to /code.

https://github.com/codeclimate/platform/blob/79b1ecefd4f0f8ea338ecb072bd571c461394c67/spec/analyzers/SPEC.md

しかしCheckstyleが絶対パス表現だからか、violations-Maven-pluginで出力されるファイルにおいても 絶対パスでパスが表現されてしまいます。 これを相対パスに変換するため、sedで変換するようにします。

$ sed --in-place "s|$(pwd)/||g" app/movie-uploader/target/codeclimate.json

Code Climate形式ファイルのアップロード

このままでは、Code Climate形式のファイルを作成してもGitLabがそれを認識してくれません。 これをGitLabに認識させるには、artifacts.reports.codequalityに対象ファイルを指定する必要があります。 詳細はこちらをご参照ください。

artifacts:reports:codequality

具体的なgitlab-ci.yml

だいたい以下のようにすることで、冒頭で記載したようなレポートが生成されます。

stages:
  - test

variables:
  DOCKER_VERSION: stable

unittest:
  stage: test
  image: maven:3.8.4-openjdk-17-slim
  script:
    - mvn -f app/movie-uploader/pom.xml verify
    # CheckStyleのCode Quality形式のJSONファイルにはファイルパスが絶対パスとして格納されているため、
    # プロジェクトルートからの相対パスに変換する
    - sed --in-place "s|$(pwd)/||g" app/movie-uploader/target/codeclimate.json
  artifacts:
    reports:
      codequality: app/movie-uploader/target/codeclimate.json
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
      changes:
        - "app/**/*"