detektを2.0に移行する 〜カスタムルールを添えて〜
はじめに
detektを2.0(alpha)に移行したので、私の環境でやったことをまとめます。
個人的な調査であり、設定が誤っている可能性もあるので、参考程度にご覧ください。
背景
Kotlinを2.3.0に上げようとしたところ、detektでエラーが出たので調べたのだが「Releasing further 1.x releases is heavily unpractical」という言及を見つけた。
そのため、alphaではあるものの、動かしてみようと思い、移行してみることになった。
環境
AGP:8.13.2
detekt:1.23.8 -> 2.0.0-alpha.1
移行手順
公式にマイグレーションガイドがありそうに見えるが、差分が載っているだけでガイドは見つからなかった。 差分やエラー、issue、Githubリポジトリを見ながら修正していった。
パッケージ名の修正
ライブラリのパッケージ名が変更になっている。
io.gitlab.arturbosch.detekt -> dev.detekt
tomlファイルやbuild.gradleファイルで定義しているものを修正する
build.gradleの記載を修正
レポート設定
xmlをcheckstyleに変更
reports {
xml.required.set(true)
xml.outputLocation = file(...)
}
reports {
checkstyle.required.set(true)
checkstyle.outputLocation = file(...)
}
Excludeがうまく効かないことの修正
同じソースでdetektを実行した際、1.x系では除外できていたAnnotationProcessingによる自動生成物までが検査対象になってしまっていた。(RoomのDaoやshowkaseなど)
通常のExcludeでは効かなかったので以下のような対応を行った。
(何か設定が誤っているせいで起きている可能性もあるため、正しい解決策ではないかもしれない。)
tasks.withType(Detekt).configureEach {
exclude { f -> f.file.path.contains("/build/generated/")
}
指摘ファイルのパス変更に伴う対応
「detekt結果のxml(checkstyle)をDangerに流してプルリクでコメントする」という使い方をしていたが、そこでエラーが起きた。
原因は指摘対象ファイル名のパス変換エラーで、detekt1.x系は絶対パスだったのに対し、detekt2.x系では相対パスになっていた。
絶対パスを相対パスに変換して処理していたので、その処理を消したらうまく動くようになった。
<file name="/Users/.../module/src/main/.../Hoge.kt">
<error ... />
</file>
<file name="module/src/main/.../Hoge.kt">
<error ... />
</file>
SARIFに関して相対パスの方が良いという議論があり、この関連でcheckStyleもそうなったと考えられる。
一方でドキュメントでは、「basePathを設定すると相対パスに出来る」といったことが書いあり、絶対パスにしたい場合どうすればよいかは分かっていない。
実行した感じでは、指定してもしなくても相対パスだが、指定することでルートを変化させることが出来た。
個人的には相対パスで問題ないので、調査はここまでにした。
Configファイルの修正
使われなくなった項目
以下の項目が使われなくなったので削除(これは私のConfigファイルの場合)
build:
maxIssues:
excludeCorrectable:
weights:
...
config:
validation: true
warningsAsErrors: false
...
processors:
...
console-reports:
...
output-reports:
...
maxIssuesがなくなったことにより、一つでもエラーがある場合にdetekt実行結果がエラーになるようになった。
もしエラーにしたくない場合は、build.gradleのdetektブロックでignoreFailures = trueを指定すれば良い。
detekt{
ignoreFailures = true
}
(根本的には、baselineを使うなどして、ignoreしない方が良い)
変更が必要なもの
ルールに設定できるしきい値thresholdが、それぞれに具体化された名前に変更になっている。
例えば、CognitiveComplexMethodはallowedComplexity、ComplexConditionはallowedConditionsという具合に。
慣れていると全部thresholdでいいのではと思うが、より分かりやすくしたのだと推測。
2.xのドキュメントにそれぞれの設定名が載っているので、調べて正しいものに修正する。
名前が変わっているルールもありそうだったが、詳細はdetekt実行時のログに書いてあるので見てほしい。
カスタムルールの修正
自前でルールクラスを作っている場合に必要な修正。
コンストラクタ
カスタムルールが継承するRuleクラスからissueプロパティが無くなり、Ruleのコンストラクタにdescriptionを渡すようになった。
class CustomRule(config: Config) : Rule(config) {
override val issue: Issue = Issue(
id = "CustomRule",
severity = Severity.Style,
description = "This is Custom rule",
debt = Debt.FIVE_MINS
)
class CustomRule(config: Config) : Rule(
config,
description = "This is Custom rule",
) {
idはクラス名が使われ、severityはconfig.xmlで設定するようになった。
idも aliasesとしてconfig.xmlで設定可能。
2.0で利用者側の自由度が上がった形だ。
これらは、configファイルのドキュメントに記載がある。
debtは廃止された様子。
CodeSmellがなくなった
Findingを使えば良さそう。
issueは渡さなくなる(Ruleにissueがなくなったので修正は容易)
report(
CodeSmell(issue, entity, message)
)
report(
Finding(entity, message)
)
RuleSetProvider
ruleSetIdがクラス定義になった。
instanceから引数が無くなり、configを受け取ってRuleを返す関数を定義するようになった。
class CustomRuleSetProvider : RuleSetProvider {
override val ruleSetId: String = "custom"
override fun instance(config: Config): RuleSet = RuleSet(ruleSetId, listOf(
CustomRule(config),
))
class CustomRuleSetProvider : RuleSetProvider {
override val ruleSetId: RuleSet.Id = RuleSet.Id("custom")
override fun instance() = RuleSet(ruleSetId, listOf(
::CustomRule,
))
}
カスタムルールのテストの修正
compileAndLintがなくなったので、lintに修正。
compileAndLintと同様にList<Finding>が返るので、使い方は今までと変わらない。
val code = "target code here"
val result = CustomRule(Config.empty).compileAndLint(code)
// ... resultをテストする
val code = "target code here"
val result = CustomRule(Config.empty).lint(code)
// ... resultをテストする
余談だが、テストは「想定コードの文字列をルールに渡して検出されるか」をテスト出来るので重宝している。
最後に
これでdetektを2.0に上げて、kotlin2.3.0で動かすことが出来た。
Excludeがうまく行かない問題は、力技で解決している感があり、なにか私の設定が誤っている可能性があるかもしれない。
指摘やマサカリがあれば投げてほしい。
Discussion