detektをCLIで使用する
はじめに
detektとはKotlinにおける静的解析ツールです。detektは知っている前提で進めるため、詳しくは公式ドキュメントや既出の記事を参照して下さい。
detektの実行方法は主に以下2パターンがあります。- gradle/mavenタスクとして実行
- CLIで実行
本記事は表題の通り、CLIで実行する場合のポイントをまとめた記事になります。公式にも設定方法があるのですが、いざ導入しようとするとつまりポイントが多かったため、記事として残しておきます。
インストール方法
インストール方法は、基本的に公式ドキュメント通りで問題ないです。
linuxなど、公式でいうところのAnyOSの手順でインストールする場合は少し注意が必要です。
手順通りにzipファイルを解凍すると、以下ディレクトリ・ファイルがダウンロードされます。
detekt-cli-${version}
|-bin
|-detekt-cli
|-detekt-cli.bat
|-lib
|-detekt-cli-${version}-all.jar
インストールのみだと、PATHが設定されていないので、この状態でdetekt-cli --help
などを実行しても、以下のようなエラーになります。
bash: detekt-cli: command not found
bashがdetekt-cliを探し出せるように、detekt-cli-${version}/bin
をPATHに設定しましょう。PATHの設定は既出の記事を参考にして下さい。
使ってみる
インストール・PATHの設定が完了すれば、基本的に以下コマンドで実行できます。
configファイルの記載方法などは公式に詳しく記載があるので参照して下さい。
detekt-cli --input ${対象ファイルのパス} --config ${configファイルのパス}
オプションはこちら
詳細設定
ここから本題です。CLIで実行する場合に苦戦した箇所を記載します。内容は主に以下です。
- detekt-formattingのプラグインを使用する方法
- Type Resolutionを有効にする方法
- lint-staged & pre-commitでステージされているファイルのみを対象に実行する方法
detekt-formattingのプラグインを使用する方法
そもそもdetektには、detekt-formattingというプラグインがあります。
ktlintという同じくKotlinのlinterのラッパーです。
gradle/mavenで使用する場合は、プラグインとして記載するだけで簡単に使用できるのですが、CLIの場合は以下二点に対応する必要があります。
- detekt-formatting用のjarファイルをダウンロードし、パスをオプションで設定する
- detekt-formatting用のconfigを追記する
では順番に設定していきましょう。
- detekt-formatting用のjarファイルをダウンロードし、パスをオプションで設定する
以下のmavenリポジトリからdetekt-formattingのjarファイルをダウンロードしましょう。
curlでダウンロードする場合はこちら
curl -sSLO https://repo1.maven.org/maven2/io/gitlab/arturbosch/detekt/detekt-formatting/1.23.6/detekt-formatting-1.23.6.jar
jarファイルのダウンロードが完了したら、任意のディレクトリに配置し、実行時の--plugins
オプションにパスを渡すように追記して下さい。
detekt-cli --plugins ${jarファイルのパス} ~~以下省略
- detekt-formatting用のconfigを追記する
以下ファイルの内容を、既存のconfigに追記して下さい。CLIの場合はconfigに追記がないと動きませんでした。以下issueを参考にしました。
以上二つを設定すると、detekt-formattingがCLIで使用できるようになるはずです。
Type Resolutionを有効にする方法
こちらもまずは前提からですが、detektにはType Resolutionというものがあります。こちらも詳細は公式を確認して下さい。
簡単にいうと、このType Resolutionが有効になっていないと使用できないルールがあります。
gradle/mavenの場合は、公式にもある通り、タスクがいい感じにやってくれます。
detekt - Runs detekt WITHOUT type resolution
detektMain - Runs detekt with type resolution on the main source set
detektTest - Runs detekt with type resolution on the test source set
つまり、detektMainとdetektTestを使用するだけで良いということですね。
CLIの場合はオプションで設定する必要があります。こちらも公式に記載されています。
上記に記載がある通り、以下二つのオプションを渡す必要があります。
-
--classpath
- Kotlinプロジェクトのjarファイルが配置されるパスを渡す
-
--jvm-target
- 使用しているjvmのバージョン
以下のようなイメージになります。
detekt-cli --classpath "sample/build/lib" --jvm-target "17"
これでCLIでもType Resolutionを有効にできたはずです。試しに、Type Resolutionが必要なルールに反したコードを書き、detekt-cliを実行してみて下さい。
lint-staged & pre-commitでステージされているファイルのみを対象に実行する方法
最後になりますが、そもそもdetektをCLIで実行しようとしたのはここがモチベーションでした。
「ステージされているファイルのみを対象に実行する方法」というのは公式にも記載されています。
筆者の環境は、モノリポになっており、既にlint-stagedを使用してステージされているファイルの拡張子ごとに適切なlinterを当てるという仕組みが備わっていたため、それに乗じて.kt
ファイルの場合はdetektを実行しよう、というものです。
ということでlint-staged & pre-commit & detekt-cliの設定方法を記載します。
lint-stagedやpre-commitがよくわからない方は、ここでは説明しないので、既出の記事や公式を参考にして下さい。
必要なのは以下三ファイルで、以下のように設定しています。
- pre-commit ~ コミットの際にlint-stagedを起動するため
- lint-staged.config.mjs ~
.kt
ファイルの場合にdetekt-cliを実行するため - detekt.sh ~ 必須ではない。公式に倣ってdetektコマンドを切り出し
npx lint-staged --no-stash
export default {
"*.kt": (abusolutePaths) => {
const 相対パス = getRelativePaths(abusolutePaths) // 絶対パスから相対パスを取得する
return ["${detekt.shのパス}" "${相対パス}"]
}
}
#!/bin/bash
echo "Running detekt check..."
fileArray=($@)
detektInput=$(IFS=,;printf "%s" "${fileArray[*]}")
echo "Input files: $detektInput"
OUTPUT=$(detekt-cli --input "$detektInput" ~その他オプション省略 2>&1)
EXIT_CODE=$?
if [ $EXIT_CODE -ne 0 ]; then
echo $OUTPUT
echo "***********************************************"
echo " detekt failed "
echo " Please fix the above issues before committing "
echo "***********************************************"
exit $EXIT_CODE
fi
detekt.sh
は基本公式をそのまま使っています。オプションをこの記事で紹介したように追加しているだけです。
lint-stagedのオプションに--no-stash
を渡していますが、これはdetektのauto-correct
を実現するためです。lint-stagedは実行結果がエラーの場合は、デフォルトの挙動としてファイルを実行前の状態に戻します。そのため、detektのauto-correct
で修正された差分まで戻してしまうためです。
最後は雑でしたが、これでステージされているファイルのみを対象にしてdetektを実行できるようになったのではないでしょうか。
最後に
CLIだと結構設定が複雑で、基礎知識がかなり求められるなと感じました。
detektはgradle/mavenでしか使用したことがなく、設定も簡単だったため、舐めていました、、
それにしても、詰まったところには必ず同じような悩みのissueがある。素晴らしい世界ですね。形はどうであれ、積極的に発信していきましょう。
Discussion