静的解析ツールを導入しているが、事前に解析結果をチェックしてといってもなかなか潰しきれない。
GitサーバーとしてGitLabを使っているので、マージリクエスト(GitHubなどのプルリクエストに相当、以下MR)でコードレビューを行っているが、静的解析結果をMRのコメントとして連携できれば、コードレビューを依頼する前にある程度取り切れるかと思い、調査したところ、うまいことやってくれるツールがあったのでメモ。
環境
Gradle v6.6.1、GitLab CE v13.6.1。
ツールの概要
「Violations」および「Violation Comments」というツールがあった。
上記リンクの「Violations Lib」というJavaライブラリがあり、そこからGitHubやBitbucketのプルリクエスト、およびGitLabのマージリクエストへのコメント連携用に「Violation Comments to ~ Lib」がサポートされている。
さらに、「Violation Comments」を実行するためのコマンドラインツールの「Violation Comments to ~ Command Line」や、Maven/Gradle/Jenkinsプラグインの「Violation Comments to ~ Plugin」が用意されている。
利用可能な静的解析ツールの詳細は、上記リンク先参照。
JavaやGroovy、Kotlin、ScalaといったJVM言語だけでなく、C++、Go、JavaScript、TypeScript、PHP、Python、Ruby、またAnsibleLintやCloudFormation Linterなどにも対応している。
使用例
今回は、Javaプロジェクトに対し、GitLab CIからGradleプラグインで、SpotBugs、PMD/CPD、Checkstyleの解析結果を連携してみる。
build.gradle
プラグインの追加
各プラグイン追加方法は以下の通り。
- SpotBugs
- PMD
- CPD
- CPDは公式プラグインがないのか、いくつか種類があったが、設定が楽だったのでこちらを利用
- Checkstyle
- Violation Comments
記述は以下のようになる。PMDとCheckstyleは、コアプラグインなのでバージョン指定不要っぽい。
// plugins DSLの場合 plugins { id 'com.github.spotbugs' version '4.7.2' id 'pmd' id 'de.aaschmid.cpd' version '3.2' id 'checkstyle' id 'se.bjurr.violations.violation-comments-to-gitlab-gradle-plugin' version '1.41.3' } // legacy plugin applicationの場合 buildscript { repositories { maven { url 'https://plugins.gradle.org/m2/' } } dependencies { classpath 'gradle.plugin.com.github.spotbugs.snom:spotbugs-gradle-plugin:4.7.2' classpath 'se.bjurr.violations:violation-comments-to-gitlab-gradle-plugin:1.41.3' classpath 'de.aaschmid:gradle-cpd-plugin:3.2' } } apply plugin: 'com.github.spotbugs' apply plugin: 'pmd' apply plugin: 'de.aaschmid.cpd' apply plugin: 'checkstyle' apply plugin: 'se.bjurr.violations.violation-comments-to-gitlab-gradle-plugin'
静的解析ツールプラグインの設定
各プラグインの設定方法を元に、設定していく。
- SpotBugs
- 2021/8/15時点で、READMEのConfigure SpotBugs Pluginが古いっぽい。値の型が
org.gradle.api.provider.Property
になっているので、代入ではなくset
メソッドを使わないと警告が出る模様。
- 2021/8/15時点で、READMEのConfigure SpotBugs Pluginが古いっぽい。値の型が
- PMD
- CPD
- Checkstyle
記述は以下のようになる。
spotbugs { toolVersion.set('4.3.0') ignoreFailures.set(true) showStackTraces.set(false) } pmd { toolVersion = '6.36.0' ignoreFailures = true consoleOutput = false // ルールセットはとりあえず全部入り ruleSets = [ 'category/java/bestpractices.xml', 'category/java/codestyle.xml', 'category/java/design.xml', 'category/java/documentation.xml', 'category/java/errorprone.xml', 'category/java/multithreading.xml', 'category/java/performance.xml', 'category/java/security.xml', ] } cpd { // 解析結果ファイルの文字コード。未設定だとOSのデフォルトエンコーディングになるため、WindowsでCP932になるのを抑制するために指定 encoding = 'UTF-8' language = 'java' ignoreFailures = true ignoreLiterals = true // リテラル値の違いを無視 ignoreIdentifiers = true // 変数名の違いを無視 ignoreAnnotations = true // アノテーションを無視 } checkstyle { toolVersion = '8.45.1' configFile = file('config/checkstyle/google_checks.xml') ignoreFailures = true showViolations = false } // 解析時間の短縮のため、ユニットテストに対しては解析対象から除外する spotbugsTest.enabled = false pmdTest.enabled = false cpdCheck { source = sourceSets.main.allJava } checkstyleTest.enabled = false
Violation Commentsプラグインの設定
GradleプラグインページからはGitLab Gradle Pluginへリンクしているが、設定できるパラメーターの詳細はGitLab Command Lineを見たほうがいい。
記述は以下のようになる。type: import se.bjurr.violations.comments.gitlab.plugin.gradle.ViolationCommentsToGitLabTask
のタスクを宣言し、設定していく。
import se.bjurr.violations.comments.gitlab.plugin.gradle.ViolationCommentsToGitLabTask task violationCommentsToGitLab(type: ViolationCommentsToGitLabTask) { // GitLabのURL、プロジェクトID、MRのインターナルID、コメント用ユーザーのAPIトークンはCIから引数で渡す gitLabUrl = project.findProperty("gitLabUrl") projectId = project.findProperty("projectId") mergeRequestIid = project.findProperty("mergeRequestIid") apiToken = project.findProperty("gitLabApiToken") apiTokenPrivate = true createCommentWithAllSingleFileComments = false // 問題をまとめたコメントをしない createSingleFileComments = true // ディスカッションとしてコメントする minSeverity = "INFO" // コメントする最小の重大度 // 解析ツールの設定を配列で指定する // 1. READMEで指定された解析ツールの種類 // 2. 解析結果の検索ディレクトリ // 3. 解析結果ファイル検索用正規表現 // 4. コメントに表示する解析ツール名(レポーター) // ディレクトリは絶対パスで指定しないと、Windows環境ではファイルの検索ができなかった。該当部分のソースがおかしかった気がする。 // また、ディレクトリ指定とファイル検索の正規表現が、組み合わせによって検索できないことが多々あり、この記述に落ち着いた。 violations = [ ['FINDBUGS', file("${buildDir}/reports/spotbugs").absolutePath, '.*/main\\.xml\$', 'Spotbugs'], ['PMD', file("${buildDir}/reports/pmd").absolutePath, '.*/main\\.xml\$', 'PMD'], ['CPD', file("${buildDir}/reports/cpd").absolutePath, '.*/cpdCheck\\.xml\$', 'CPD'], ['CHECKSTYLE', file("${buildDir}/reports/checkstyle").absolutePath, '.*/main\\.xml\$', 'Checkstyle'], ] }
コメント形式のカスタマイズ
前述の設定だと、こちらのデフォルト形式でコメントされる。
PMDで System.out.println
があった場合に、実際にコメントされるテキストは以下。
SourceにGradleプロジェクトのgroupやnameが設定されるが、ファイルパスが分かれば十分だし、縦に長いので調整したい。
コメントテンプレートは、 commentTemplate
として mustacheテンプレートで記述可能。mustacheの実装としてはMustache.javaが使われる。
前述の task violationCommentsToGitLab
配下に、以下を追加。
task violationCommentsToGitLab(type: ViolationCommentsToGitLabTask) { // 中略 // コメント形式のカスタマイズ、 commentTemplate = """\ **重大度**: {{violation.severity}}, **解析ツール**: {{violation.reporter}}{{#violation.rule}}, **ルール**: {{violation.rule}}{{/violation.rule}} **対象**: {{changedFile.filename}} \\# {{violation.startLine}}行目{{#violation.endLine}}~{{violation.endLine}}行目{{/violation.endLine}} **内容**: {{violation.message}} """.stripIndent() }
すると、コメントされるテキストは以下のようになる。
GitLab設定
コメント用のユーザーを作成し、ユーザー設定 > アクセストークンから、apiスコープのパーソナルアクセストークンを作成しておく。
.gitlab-ci.yml設定
GitLab CI/CD Variablesとして、先ほど作成したユーザーのアクセストークンを保存しておく。名前は VIOLATION_API_TOKEN
とした。
マージリクエストに限定されたジョブに対し、以下のscriptを記述すると、MRの変更箇所に対して静的解析結果がコメントされる。
script: - |- ./gradlew check violationCommentsToGitLab -x test \ -PgitLabUrl=${CI_SERVER_URL} \ -PprojectId=${CI_PROJECT_ID} \ -PmergeRequestIid=${CI_MERGE_REQUEST_IID} \ -PgitLabApiToken=${VIOLATION_API_TOKEN}
振り返り
最悪、自前でGitLab APIを叩いて実現しようと思っていたので、ものすごく助かった。
めちゃくちゃ便利だと思ったんだけど、2021/8/15時点で日本語の紹介を見つけられなかった。マイナーなのか、有償の機能とか使ってやっているのかな?