静的コード解析ツール「Detekt」を導入してみる - Hands on

# What is Detekt

https://detekt.dev/docs/intro/

Kotlinのための静的コード解析ツール

[不吉なにおい](https://martinfowler.com/bliki/CodeSmell.html)の解析

このHand-onでは、Spring Bootで作成したプロジェクトを利用します。CIにはGitHub Actionsを利用します。

# Prerequisites

- Spring Bootプロジェクトの作成
- GitHubアカウントとリモートリポジトリ

# Quick start tutorial

新規作成したSpring Bootプロジェクト、もしくは[Spring Initializer](https://start.spring.io/#!type=gradle-project&language=kotlin&platformVersion=2.6.7&packaging=jar&jvmVersion=11&groupId=com.example&artifactId=demo&name=demo&description=Demo%20project%20for%20Spring%20Boot&packageName=com.example.demo)で作成したプロジェクトを開く。

# Install Detekt with Gradle plugin

Gradleプロジェクトビルドファイルに次の構成を適応する。

```
plugins {
    id("io.gitlab.arturbosch.detekt") version "1.20.0"
}
```

:::note info
**Tip:** JET BRAINSのマーケットプレイスで公開されている[Detekt](https://plugins.jetbrains.com/plugin/10761-detekt)のプラグインをインストールすることで、IDEAと統合することができます。
:::

# Run Detekt using Gradle Task

検証用に次のコードを追加する。

```kotlin
package com.example.demo.domain.talent

data class TalentName(val value: String) {
    init {
        if (value.length > 255) {
            throw Exception("名前は255文字以下で入力してください")
        }
    }
}
```

CLIからコマンドを実行する。
```console
C:\Users\Umizoko\Downloads\demo\demo>gradlew detekt       

> Task :detekt FAILED
C:\Users\Umizoko\Downloads\demo\demo\src\test\kotlin\com\example\demo\DemoApplicationTests.kt:10:21: This empty block of code can be removed. [EmptyFunctionBlock]
C:\Users\Umizoko\Downloads\demo\demo\src\main\kotlin\com\example\demo\domain\talent\TalentName.kt:6:13: Exception is a too generic Exception. Prefer throwing specific exceptions that indicate a specific error case. [TooGenericExceptionThrown]
C:\Users\Umizoko\Downloads\demo\demo\src\main\kotlin\com\example\demo\DemoApplication.kt:10:33: In most cases using a spread operator causes a full copy of the array to be created before calling a method. This may result in a performance penalty. [SpreadOperator]
C:\Users\Umizoko\Downloads\demo\demo\src\main\kotlin\com\example\demo\domain\talent\TalentName.kt:9:2: The file C:\Users\Umizoko\Downloads\demo\demo\src\main\kotlin\com\example\demo\domain\talent\TalentName.kt is not ending with a new line. [NewLineAtEndOfFile]
C:\Users\Umizoko\Downloads\demo\demo\src\main\kotlin\com\example\demo\domain\talent\TalentName.kt:5:28: This expression contains a magic number. Consider defining it to a well named constant. [MagicNumber]


FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':detekt'.
> Analysis failed with 5 weighted issues.

* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 1s
1 actionable task: 1 executed
```

上記のようにルールに違反したものを列挙する。公式ドキュメントからルールを確認することで、より準拠したコードにすることができる。

## EmptyFunctionBlock

https://detekt.dev/docs/rules/empty-blocks/#emptyfunctionblock

> Empty blocks of code serve no purpose and should be removed.

## TooGenericExceptionThrown

https://detekt.dev/docs/rules/exceptions/#toogenericexceptionthrown

> This rule reports thrown exceptions that have a type that is too generic. It should be preferred to throw specific exceptions to the case that has currently occurred.

## SpreadOperator

https://detekt.dev/docs/rules/performance/#spreadoperator

> In most cases using a spread operator causes a full copy of the array to be created before calling a method. This has a very high performance penalty. 

## NewLineAtEndOfFile

https://detekt.dev/docs/rules/style/#newlineatendoffile

> This rule reports files which do not end with a line separator.

## MagicNumber

https://detekt.dev/docs/rules/style#magicnumber

> This rule detects and reports usages of magic numbers in the code. Prefer defining constants with clear names describing what the magic number means.

# Automate Detekt with GitHub Actions

GitHub Actionsのワークフローに統合する。

ワークフローファイルに次の構成を適応する。

\<SpringBoot Root Directory>/.gihub/workflows/detekt.yaml

```yaml
name: detekt

on: push

jobs:
  static-code-analyze:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout project sources
        uses: actions/checkout@v2
      - name: Setup Gradle
        uses: gradle/gradle-build-action@v2
      - name: Executable Gradlew file
        run: chmod +x ./gradlew
      - name: Run detekt
        run: ./gradlew detekt
```

[ジョブのログ](https://github.com/Umizoko/learn-detekt-with-spring-boot/runs/6441499926?check_suite_focus=true)で失敗していることを確認する。

:::note info
**Tip:**　レポートの出力フォーマットにSARIFをサポートしています。出力されたSARIFファイルをアップロードすることでCode Scanningと統合することができます。
:::

## Integrate Code Scanning

リモートリポジトリから

ワークフローの設定ファイルに次の構成を追加する。

```yaml
name: detekt

on: push

jobs:
  static-code-analyze:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout project sources
        uses: actions/checkout@v2
      - name: Setup Gradle
        uses: gradle/gradle-build-action@v2
      - name: Executable Gradlew file
        run: chmod +x ./gradlew
      - name: Run detekt
        run: ./gradlew detekt
      - name: Upload SARIF to Github using the upload-sarif action
        uses: github/codeql-action/upload-sarif@v2
        if: success() || failure()
        with:
          sarif_file: build/reports/detekt/detekt.sarif
```

SARIFファイルのアップロードが完了すると、Code scanningページからルール違反の項目がアイテムベースでリストされる。

![スクリーンショット 2022-05-15 233708.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99208/0fdd67bc-2f50-bc60-7082-6594e16c81e1.png)