🥂

【Android】ktlint を導入してみた

2022/07/10に公開約4,000字

ktlintについて

ktlintとは、Kotlin(kotlinlang.org)とAndroid(Android Kotlin Style Guide)で推奨されているコーディング規約を元に、コードスタイルをチェックしてくれるツールです。

ktlint_image

規約を簡単に紹介

パッケージ名

  • パッケージ名は小文字にする。アンダースコアは使わない
    • OK: org.example.project
    • NG: org.example.my_project
  • 複数の単語を使用することは、推奨されていない。どうしても使いたいときは、単語を「連結させる」か「キャメルケース」にする(キャメルケースは、Androidの規約ではダメってなってた)
    • 連結: org.example.myproject
    • キャメルケース: org.example.myProject

プロパティ

  • シングルトンの参照をもつプロパティは、オブジェクトと同じ名前にすることができる
// singleton
val PersonComparator: Comparator<Person> = /*...*/
  • クラス内に同じ概念のプロパティが2つあり、1つを外部に公開したい場合は、プライベートプロパティのプレフィックス(文字の先頭)に、アンダースコアを付ける
class C {
    private val _elementList = mutableListOf<Element>()

    val elementList: List<Element>
         get() = _elementList
}

関数名の例外

  • 関数名は基本、小文字から始めるキャメルケースだが、クラスのインスタンスを生成に使うファクトリ関数は、返す抽象の型と同じ名前にすることができる
interface Foo { /*...*/ }
class FooImpl : Foo { /*...*/ }
fun Foo(): Foo { return FooImpl() }

テストメソッド

  • バッククオートで囲まれたスペースを含むメソッド名を使うことができる
  • メソッド名には、アンダースコアを含めることができる
  • Androidの規約では、スペースを含めるのは推奨されていないので、アンダースコアの方を使うのが良さそう
class MyTestCase {
  @Test fun `ensure everything works`() { /*...*/ }
  @Test fun ensureEverythingWorks_onAndroid() { /*...*/ }
}

空のブロック

  • 改行する
// WRONG!(よくこういう書き方してしまう)
try {
    doSomething()
} catch (e: Exception) {}

// Okay
try {
    doSomething()
} catch (e: Exception) {
}

ktlintの導入

app/build.gradleに、以下を追加します。

android {
    ...
}

// Add
configurations {
    ktlint
}

// Add コードスタイルをチェック
task ktlint(type: JavaExec, group: "verification") {
    description = "Check Kotlin code style."
    // main = "com.pinterest.ktlint.Main" は非推奨
    mainClass.set("com.pinterest.ktlint.Main")
    classpath = configurations.ktlint
    args "src/**/*.kt", "--color", "--reporter=plain", "--reporter=checkstyle,output=${buildDir}/ktlint.xml"
}

// Add
check.dependsOn ktlint

// Add 自動でフォーマットしてくれる
task ktlintFormat(type: JavaExec, group: "formatting") {
    description = "Fix Kotlin code style deviations."
    // main = "com.pinterest.ktlint.Main" は非推奨
    mainClass.set("com.pinterest.ktlint.Main")
    classpath = configurations.ktlint
    args "-F", "src/**/*.kt"
}

dependencies {
    ...
    // Add
    ktlint "com.pinterest:ktlint:0.43.1"
}

ktlintの実行

build.gradle、または、コマンドでktlintを実行できます。

build.gradle。左の「▶︎」で実行

build_gradle

コマンドで実行

$ ./gradlew ktlint
$ ./gradlew ktlintFormat

Android Studioでのコードスタイルの設定

ktlintREADME.md で、ktlintに引っかからないようにするIntelliJIDEAの設定方法が紹介されています。

intellijidea_settings

毎回手動で ktlint やるのはめんどいな?

毎回手動でktlintを実行するのは手間だったり、忘れたりしそうなので、「コミット時」または「Github Actionを実行したとき」に、解析が行われるようにしてみたいと思います。

コミット時

pre-commitを使います。

pre-commitとは、コミット時にスクリプトを実行できる機能です。

以下のコマンドを実行することで、[project name]/.git/hooks/pre-commitファイルを作成します。

# ktlintがインストールされているかチェック。インストールされてなければ、brew install ktlint
$ ktlint --version
0.40.0

$ ktlint --install-git-pre-commit-hook

すでにpre-commitファイルが存在する場合は、以下をpre-commitファイルに追記します。

git diff --name-only --cached --relative | grep '\.kt[s"]\?$' | xargs ktlint --relative .
if [ $? -ne 0 ]; then exit 1; fi

不要になったときは、rm .git/hooks/pre-commitでファイルを削除するか、pre-commitのスクリプトを削除すると良さそうです。

Github Actions

[Project name]/.github/workflows/.ymlファイルを作成します。

作成済みの場合は、steps:name:run:を追記します。今回は新規にktlint.ymlを作成したいと思います。

ktlint.yml

name: ktlint

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main
jobs:
  ktlint:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v2
      - name: Run ktlint
        run: ./gradlew ktlint

Discussion

ログインするとコメントできます