日々常々

ふつうのプログラマがあたりまえにしたいこと。

ローカルでSonarQubeを触ってみる(MavenとJaCoCoと)

  • SonarQube 9.9.1-community
  • Maven 3.9.1

これはなに

SonarQubeよくわからん人向け。

「何できるもんなんだろ」とか「現場でSonarQube使ってるものの、設定とか影響範囲わからなくて怖い」とか、色々とあると思う。ローカルでいじくれるとイメージも湧きやすいしハードル下がるかなと思って書いた。

あとJaCoCo周りはあちこちでなんやかんやあるので。

準備

SonarQube

Dockerで。

docker run -p 9000:9000 --rm sonarqube:9.9.1-community
  1. http://localhost:9000 ひらく。
  2. admin/admin でログイン
  3. パスワード設定画面になるのでパスワードを hoge に変更

通常はTokenを作成するが、ここではSonarQubeの設定も揮発するコンテナで動かしてるのでやらない。

他の形

https://www.sonarsource.com/products/sonarqube/downloads/

Docker使わないならダウンロードして解凍してでもできる。けど「とりあえず触る」にはちょっと面倒だよね。

手元に入れるの面倒ならオープンソースならSonarCloudが無料で使えるけど、このブログのタイトルから外れるので書かない。

アプリケーション(ない場合)

https://start.spring.io とかで spring-boot-starter-webpom.xml とってきて準備。

 ▼pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.0.6</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>dev.irof.suburi</groupId>
    <artifactId>spring-maven-sonar</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>すぶり</name>

    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

SpringBootである必要は何一つない。

見ての通り(本エントリはMavenある程度わかる人を想定しています)、SonarQubeに関する記述は pom.xml にはありません。 なのでもし手元に手頃なMavenプロジェクトあるならそのまま使えばいいです。

以降は ./mvnw 使ってるけど、パス通ってるMaven使うなら mvn でもいいし、 mvn wrapper:wrapper 叩いてからでもいい。

実行

./mvnw clean package sonar:sonar -Dsonar.login=admin -Dsonar.password=hoge

さっきパスワードを hoge に変えたから sonar.password はそれにしてる。他のにしてたら変えてね。 トークン使ってみたり別ユーザー作ってみたりしていじってみましょう。

説明

sonar-maven-plugin は特別扱いされてるので、 pom.xml に何も書かなくても sonar:sonar で実行できちゃいます。

とはいえ設定書きたいとかバージョン制御したいとかあると思うんで、本番では書いた方がいいかもですね。

sonar.login / sonar.password とかは前述の通り。SonarQubeのURLはデフォルト http://localhost:9000 なので何も書いてないだけです。 この辺りは設定したくなるはずなので、ドキュメント読んで settings.xml に書くとか、実行シェルに書くとかしていきませう。

ちなみにプラグインのソースは https://github.com/SonarSource/sonar-scanner-maven にあります。

なお clean package とかしなくても sonar:sonar だけでスキャンはしてくれます。 でも普通はテストの通ったものをスキャンすると思うし、テスト結果もSonarQubeにのっけときたいと思うし。

確認

前述の pom.xml を使ったなら http://localhost:9000/projects に「すぶり」ってプロジェクトができてるはず。

この名前は /project/name になる。

あとは好きにどうぞ。

もう一歩: カバレッジ

たとえばカバレッジをSonarQubeに取り込みたいなら、 jacoco-maven-plugin 入れてやってください。

...
            <plugin>
                <groupId>org.jacoco</groupId>
                <artifactId>jacoco-maven-plugin</artifactId>
                <version>0.8.8</version>
                <executions>
                    <execution>
                        <id>default-prepare-agent</id>
                        <goals>
                            <goal>prepare-agent</goal>
                        </goals>
                    </execution>
                    <execution>
                        <!-- verify -->
                        <id>default-report</id>
                        <goals>
                            <goal>report</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

prepare-agent がテストとか実行する前にJaCoCoのagentを設定するもの。 phase は指定しなくてもいい感じの場所( initialize )に入ります。

report がSonarQubeも取り込める形のJaCoCoのレポート(XML)を出力するものです。これ実行してあげないとSonarQubeのカバレッジは永久に0%と表示されます。

<execution><phase> を書かない場合、 jacoco:reportverify で実行されます。 先に挙げたコマンドだと package までしか実行してないので、JaCoCoのレポートは出力されません。

てことでこう。

./mvnw clean verify sonar:sonar -Dsonar.login=admin -Dsonar.password=hoge

<execution> 書かずに jacoco:report を手で実行してあげてもいいですけどね。

./mvnw clean package jacoco:report sonar:sonar -Dsonar.login=admin -Dsonar.password=hoge

分けるならこうでも。

./mvnw clean package
./mvnw jacoco:report
./mvnw sonar:sonar -Dsonar.login=admin -Dsonar.password=hoge

test でJaCoCoのファイル target/jacoco.exec が出来て、それを元に target/site/jacoco/* を作るのが jacoco:report です。 うまく取り込めてると sonar:sonar の時に以下のように Importing 1 report(s) が出ます。

[INFO] Sensor JaCoCo XML Report Importer [jacoco]
[INFO] 'sonar.coverage.jacoco.xmlReportPaths' is not defined. Using default locations: target/site/jacoco/jacoco.xml,target/site/jacoco-it/jacoco.xml,build/reports/jacoco/test/jacocoTestReport.xml
[INFO] Importing 1 report(s). Turn your logs in debug mode in order to see the exhaustive list.
[INFO] Sensor JaCoCo XML Report Importer [jacoco] (done) | time=7ms

ダメだったらこんな風に出るはず。

[INFO] Sensor JaCoCo XML Report Importer [jacoco]
[INFO] 'sonar.coverage.jacoco.xmlReportPaths' is not defined. Using default locations: target/site/jacoco/jacoco.xml,target/site/jacoco-it/jacoco.xml,build/reports/jacoco/test/jacocoTestReport.xml
[INFO] No report imported, no coverage information will be imported by JaCoCo XML Report Importer
[INFO] Sensor JaCoCo XML Report Importer [jacoco] (done) | time=1ms

少し前のSonarQubeは jacoco.exec を読んだのだけど、XML読むようにちょっと前に変わった。まぁ気持ちはわかる。

注意

  • SonarQubeのフッタに警告表示されてるけど、組み込みデータベース使ってるんでコンテナ落としたらデータ消えます。あくまで実験用。
  • SonarQubeのプロジェクトページ開いたら警告表示されてるけど、 sonar.password はそのうち使えなくなるらしいです。なのでここでは 9.9.1-community とバージョン固定してます。「これから使っていくためにとりあえず触る」目的なら lts とか latest とか使う方がいいと思うよ。

併せて読む必要がない

irof.hateblo.jp

JaCoCoで検索してもこの記事しかなかった。あんま書いてないのね私。SonarQubeは長年の付き合いなのに一記事も書いてなかった……。