Kotlin の新規プロジェクトで最初に導入するもの
概要
個人または業務問わずに、Kotlin を用いた新規プロジェクトで、最初に導入するライブラリやセットアップをまとめる。
技術ブログのサンプルコードレベルでも共通のものを導入したい。
一覧
共通
- renovate(or dependabot)
- github actions (or 他 CI ツール)
- detekt(or ktlint)
- dokka
テスト
- Assertj
- jqwik
Spring Boot 導入時
- OpenAPI Generator
- Database Rider
renovate
Main 用途
ライブラリの自動アップデート
Sub 用途
なし
導入手順
- https://github.com/apps/renovate にアクセス
- 右上の「Configure」
- install する先を特定リポジトリにする(Only select repositories)
渡す権限
Read access to Dependabot alerts, administration, and metadata
Read and write access to checks, code, commit statuses, issues, pull requests, and workflows
Dependabot のアラート、管理、メタデータへの読み取りアクセス
チェック、コード、コミット状況、課題、プルリクエスト、ワークフローへの読み取りと書き込みのアクセス権
ユーザー権限
Read access to emails
- インストール
- インストール後、PR (Configure Rnovate)が自動でできる
renovate の設定
- PR で作成されたブランチをローカルに pull して、修正する
renovate.json
から .github/renovate.json5
に移動する。
共通
リポジトリに限らず入れる設定
{
//
// automerge
// https://docs.renovatebot.com/configuration-options/#automerge
//
// 自動マージ
// メジャーバージョン・マイナーバージョンで自動マージの true/false を設定できる
//
// default false
automerge: true,
//
// extends
// https://docs.renovatebot.com/configuration-options/#extends
//
// プリセットを使用
//
extends: [
"config:base",
//
// timezone
// https://docs.renovatebot.com/configuration-options/#timezone
// https://docs.renovatebot.com/presets-default/#timezonearg0
//
// 概要
// - タイムゾーン
//
// default: null(UTC)
//
":timezone(Asia/Tokyo)"
],
//
// labels
// https://docs.renovatebot.com/configuration-options/#labels
//
// 概要
// - 全てのPRにつけるラベル
//
labels: ["dependencies"],
//
// dependencyDashboard
// https://docs.renovatebot.com/configuration-options/#dependencydashboard
//
// 概要
// - ダッシュボード作成する・しない
//
// default: false
//
dependencyDashboard: true,
//
// packageRules
// https://docs.renovatebot.com/configuration-options/#packagerules
//
// 概要
// - updateするパッケージの単位(まとめることができる)
//
// default: なし
//
}
非共通
リポジトリによって変更する設定
{
//
// automerge
// https://docs.renovatebot.com/configuration-options/#automerge
//
// 自動マージ
// メジャーバージョン・マイナーバージョンで自動マージの true/false を設定できる
//
// default false
automerge: true,
//
// packageRules
// https://docs.renovatebot.com/configuration-options/#packagerules
//
// 概要
// - updateするパッケージの単位(まとめることができる)
//
// default: なし
//
packageRules: [
{
groupName: "org.jetbrains.kotlin.*",
description: "一緒に上げないとCIが落ちる",
matchPackagePrefixes: [
"org.jetbrains.kotlin."
]
},
]
}
github actions
github 公式の CI ツール。
lint と test を回すために設定する。
./.github/workflows/ci.yml
を作成する。
CI に make コマンドを組み込んでいるので、Makefile の作成は必須。
name: CI
on:
push:
jobs:
ci-test:
runs-on: ubuntu-latest
#
# GitHubActionsで利用可能な権限
#
# URL
# - https://docs.github.com/ja/actions/using-jobs/assigning-permissions-to-jobs
#
# もし指定したら、それ以外は全てnoneになる
#
permissions:
actions: none
checks: write
contents: read
deployments: none
id-token: none
issues: none
discussions: none
packages: none
pages: none
pull-requests: write
repository-projects: none
security-events: none
statuses: write
steps:
- name: リポジトリのチェックアウト
uses: actions/checkout@v3
- name: Javaの環境をセットアップ
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '17'
cache: 'gradle'
- name: '【mainブランチ】'
if: github.ref == 'refs/heads/main'
run: make test
env:
TZ: Asia/Tokyo
- name: '【mainブランチ以外】'
if: github.ref != 'refs/heads/main'
run: make test
env:
TZ: Asia/Tokyo
ci-lint:
runs-on: ubuntu-latest
steps:
- name: リポジトリのチェックアウト
uses: actions/checkout@v3
- name: Javaの環境をセットアップ
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '17'
cache: 'gradle'
- name: make lint
run: make lint
Makefile
上記の CI に必要な Makefile。
detekt については後述。
.PHONY: test
test: ## テスト実行
./gradlew test
.PHONY: fmt
fmt: ## format
./gradlew detekt --auto-correct
.PHONY: lint
lint: ## lint
./gradlew detekt
################################################################################
# Utility-Command help
################################################################################
.DEFAULT_GOAL := help
################################################################################
# マクロ
################################################################################
# Makefileの中身を抽出してhelpとして1行で出す
# $(1): Makefile名
define help
grep -E '^[\.a-zA-Z0-9_-]+:.*?## .*$$' $(1) \
| grep --invert-match "## non-help" \
| awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
endef
################################################################################
# タスク
################################################################################
.PHONY: help
help: ## Make タスク一覧
@echo '######################################################################'
@echo '# Makeタスク一覧'
@echo '# $$ make XXX'
@echo '# or'
@echo '# $$ make XXX --dry-run'
@echo '######################################################################'
@echo $(MAKEFILE_LIST) \
| tr ' ' '\n' \
| xargs -I {included-makefile} $(call help,{included-makefile})
detekt
Kotlin の linter かつ formatter。ktlint よりも選択肢が多い。
build.gradle.kts に以下を追記する。
plugins {
/**
* detekt
*
* URL
* - https://github.com/detekt/detekt
* GradlePlugins(plugins.gradle.org)
* - https://plugins.gradle.org/plugin/io.gitlab.arturbosch.detekt
* Main用途
* - Linter/Formatter
* Sub用途
* - 無し
* 概要
* KotlinのLinter/Formatter
*/
id("io.gitlab.arturbosch.detekt") version "1.23.4"
}
dependencies {
/**
* detektの拡張: detekt-formatting
*
* 概要
* - formattingのルール
* - 基本はktlintと同じ
* - format自動適用オプションの autoCorrect が使えるようになる
*/
detektPlugins("io.gitlab.arturbosch.detekt:detekt-formatting:1.23.4")
}
detekt のバージョンによっては、kotlin("jvm")
と kotlin("plugin.spring")
のバージョンの設定も必要。
plugins {
// Kotlin と Spring Boot で使う Kotlin Plugin のバージョン
kotlin("jvm") version "1.9.21"
kotlin("plugin.spring") version "1.9.21"
}
Makefile を作成して、 lint と format コマンドを登録する。
./gradlew detekt
コマンドは型解決してくれないので、./gradlew detektMain
と ./gradlew detektTest
を設定する。
.PHONY: fmt.main
fmt.main: ## main ソースセットの型解決を使用して format
./gradlew detektMain --auto-correct
.PHONY: lint.main
lint.main: ## main ソースセットの型解決を使用して lint
./gradlew detektMain
.PHONY: fmt.test
fmt.test: ## test ソースセットの型解決を使用して format
./gradlew detektTest --auto-correct
.PHONY: lint.test
lint.test: ## test ソースセットの型解決を使用して lint
./gradlew detektTest
################################################################################
# Utility-Command help
################################################################################
.DEFAULT_GOAL := help
################################################################################
# マクロ
################################################################################
# Makefileの中身を抽出してhelpとして1行で出す
# $(1): Makefile名
define help
grep -E '^[\.a-zA-Z0-9_-]+:.*?## .*$$' $(1) \
| grep --invert-match "## non-help" \
| awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
endef
################################################################################
# タスク
################################################################################
.PHONY: help
help: ## Make タスク一覧
@echo '######################################################################'
@echo '# Makeタスク一覧'
@echo '# $$ make XXX'
@echo '# or'
@echo '# $$ make XXX --dry-run'
@echo '######################################################################'
@echo $(MAKEFILE_LIST) \
| tr ' ' '\n' \
| xargs -I {included-makefile} $(call help,{included-makefile})
Suppress
技術記事のサンプルコードだととりあえず、Suppress で抑制したいときがある。
頻出のものをまとめる。
fun main(args: Array<String>) {
@Suppress("SpreadOperator")
runApplication<OpenapiGeneratorSampleApplication>(*args)
}
@SpringBootTest
class OpenapiGeneratorSampleApplicationTests {
@Test
@Suppress("EmptyFunctionBlock")
fun contextLoads() {
}
}
設定を上書きしたいとき
まず、detekt の設定ファイルを書き出す。
config/detekt/detekt.yml
に書き出される。
./gradlew detektGenerateConfig
続いて、編集したいルールのファイルを作成する。
touch config/detekt/detekt-override.yml
detekt-override.yml で修正したいルールを記入する。コメントで default デフォルト値
を書くとわかりやすい。
以下は記入例
---
# comments のルール
comments:
active: true
#
# UndocumentedPublicClass
#
# 概要
# - コメントがないパブリッククラスを禁止する
#
UndocumentedPublicClass:
active: true # (default: false)
#
# UndocumentedPublicClass
#
# 概要
# - コメントがないパブリックな関数を禁止する
#
UndocumentedPublicFunction:
active: true # (default: false)
#
# UndocumentedPublicClass
#
# 概要
# - コメントがないパブリックなプロパティを禁止する
#
UndocumentedPublicProperty:
active: true # (default: false)
# style のルール
style:
active: true
#
# UnusedImports
#
# 概要
# - 未使用の import を禁止する
#
UnusedImports:
active: true # (default: false)
build.gradle.kts に読み込ませる。
/**
* detektの設定
*
* 基本的に全て `detekt-override.yml` で設定する
*/
detekt {
/**
* ./gradlew detektGenerateConfig でdetekt.ymlが生成される(バージョンが上がる度に再生成する)
*/
config = files(
"$projectDir/config/detekt/detekt.yml",
"$projectDir/config/detekt/detekt-override.yml",
)
}
pre-commit で commit のたびに lint を実行させる
.githooks/pre-commit を作成する
mkdir ./.githooks
touch ./.githooks/pre-commit
以下を記入する。
#!/usr/bin/env bash
echo "Running detekt check..."
OUTPUT="/tmp/detekt-main-$(date +%s)"
make lint.main > $OUTPUT
EXIT_CODE=$?
if [ $EXIT_CODE -ne 0 ]; then
cat $OUTPUT
rm $OUTPUT
echo "**********************************************************"
echo " Detekt failed "
echo " make fmt.main を実行するか、直接修正してからコミットしてください "
echo "**********************************************************"
exit $EXIT_CODE
fi
rm $OUTPUT
OUTPUT="/tmp/detekt-test-$(date +%s)"
make lint.test > $OUTPUT
EXIT_CODE=$?
if [ $EXIT_CODE -ne 0 ]; then
cat $OUTPUT
rm $OUTPUT
echo "**********************************************************"
echo " Detekt failed "
echo " make fmt.test を実行するか、直接修正してからコミットしてください "
echo "**********************************************************"
exit $EXIT_CODE
fi
rm $OUTPUT
実行可能ファイルにして、git hook のパスに指定する。
chmod +x .githooks/pre-commit
git config --local core.hooksPath .githooks
OpenAPI Generator
OpenAPI からコードを自動生成する。
以下の記事に投稿した。
openapi generator の依存関係。
plugins {
/**
* openapi.generator
*
* 公式ページ
* - https://openapi-generator.tech/
* GradlePlugins(plugins.gradle.org)
* - https://plugins.gradle.org/plugin/org.openapi.generator
* GitHub
* - https://github.com/OpenAPITools/openapi-generator/tree/master/modules/openapi-generator-gradle-plugin
* Main用途
* - スキーマファイルからコード生成
* Sub用途
* - スキーマファイルからドキュメント生成
* 概要
* - スキーマ駆動開発するために使う
* - API仕様をスキーマファイル(yaml)に書いて、コード生成し、それを利用するようにする
* - 可能な限りプロダクトコードに依存しないようにする(生成したコードにプロダクトコードを依存させる)
*/
id("org.openapi.generator") version "6.2.0"
}
dependencies {
/**
* Swagger Annotations
* Swagger Models
* Jakarta Annotations API
*
* MavenCentral
* - https://mvnrepository.com/artifact/io.swagger.core.v3/swagger-annotations
* - https://mvnrepository.com/artifact/io.swagger.core.v3/swagger-models
* - https://mvnrepository.com/artifact/jakarta.annotation/jakarta.annotation-api
* Main用途
* - OpenAPI Generatorで作成されるコードで利用
* Sub用途
* - 無し
* 概要
* - OpenAPI Generatorで作成されるコードがimportしている
* - 基本的にプロダクションコードでは使わない想定
*/
compileOnly("io.swagger.core.v3:swagger-annotations:2.2.6")
compileOnly("io.swagger.core.v3:swagger-models:2.2.6")
compileOnly("jakarta.annotation:jakarta.annotation-api:2.1.1")
/**
* Spring Boot Starter Validation
*
* MavenCentral
* - https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-validation
* Main用途
* - OpenAPI Generatorで作成されるコードで利用
* Sub用途
* - 無し
* 概要
* - OpenAPI Generatorで作成されるコードがimportしている
* - javax.validation*を利用するため
* [Spring-Boot-2.3ではjavax.validationを依存関係に追加しなければならない](https://qiita.com/tatetsujitomorrow/items/a397c311a95d66e4f955)
*/
implementation("org.springframework.boot:spring-boot-starter-validation")
}
さらに task コマンドと、build 先のファイルを import できるようにする。
import org.openapitools.generator.gradle.plugin.tasks.GenerateTask // GenerateTask のオプションのために、import が必要。あらかじめ、build して依存ファイルがないと import できない
/**
* OpenAPI Generator を使ってコード生成
*/
task<GenerateTask>("generateApiServer") {
generatorName.set("kotlin-spring")
inputSpec.set("$projectDir/openapi.yaml")
outputDir.set("$buildDir/openapi/server-code/") // .gitignoreされているので注意(わざとここにあります)
apiPackage.set("com.example.yourapp.openapi.generated.controller") // 各自のアプリケーションに合わせてパス名を変更する
modelPackage.set("com.example.yourapp.openapi.generated.model") // 各自のアプリケーションに合わせてパス名を変更する
configOptions.set(
mapOf(
"interfaceOnly" to "true",
)
)
/**
* true にすると tags 準拠で、API の interface を生成する
*/
additionalProperties.set(
mapOf(
"useTags" to "true"
)
)
}
/**
* Kotlinをコンパイルする前に、generateApiServerタスクを実行
* 必ずスキーマファイルから最新のコードが生成され、もし変更があったらコンパイル時に失敗して気付けるため
*/
tasks.compileKotlin {
dependsOn("generateApiServer")
}
/**
* OpenAPI Generator によって生成されたコードを import できるようにする
*/
kotlin.sourceSets.main {
kotlin.srcDir("$buildDir/openapi/server-code/src/main")
}
Database Rider
アノテーションで、DB テストを容易にするライブラリ
以下に記事を投稿した。
build.gradle.kts
dependencies {
// 略
/**
* Database Rider
*
* - Rider Core
* - Rider Spring
* - Rider JUnit 5
*
* URL
* - https://database-rider.github.io/database-rider/
* MavenCentral
* - https://mvnrepository.com/artifact/com.github.database-rider/rider-core
* - https://mvnrepository.com/artifact/com.github.database-rider/rider-spring
* - https://mvnrepository.com/artifact/com.github.database-rider/rider-junit5
* Main用途
* - JUnitでDB周りのテスト時のヘルパー
* 概要
* - テーブルの事前条件、事後条件を簡潔に設定できる
*/
implementation("com.github.database-rider:rider-core:1.35.0")
implementation("com.github.database-rider:rider-spring:1.35.0")
testImplementation("com.github.database-rider:rider-junit5:1.35.0")
}
基本的な書き方
class ClassNameTest {
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@DBRider
@DisplayName("List(コメント一覧を表示)")
class InnterClassNameTest {
@Test
@DBRider
@DataSet("datasets/path/to/input/data.{yml,csv,cml}") // 入力データのファイルを指定
@ExpectedDataSet(
value = ["datasets/path/to/expected/data.{yml,csv,cml}"], // 期待値のデータのファイルを指定
orderBy = ["id"],
ignoreCols = ["createdAt", "updatedAt"], // メタデータを無視する
)
// NOTE: 一度データを書き出したら、コメントアウトする
@ExportDataSet(
format = DataSetFormat.YML, // テスト完了後にデータを書き出す形式を指定
outputName = "src/test/resources/datasets/path/to/export/data.{yml,csv,cml}", // 書き出すファイル先を指定
includeTables = ["customer"] // 含めるカラムを指定
)
fun `テスト`() {
// テストを書く
}
}
}
dataset ファイルのパス
ファイルを配置するのは、src/test/resources
配下に設置する。
dataset ファイルのパスは @DataSet
と @ExpectedDataSet
のときで指定方法が異なる。
アノテーション | 指定先 | 例 |
---|---|---|
@DataSet | resources 配下から指定する。resources は含まない | "datasets/yml/XXX/input-file.yml" |
@ExpectedDataSet | src を含むパスで指定する | "src/test/resources/datasets/yml/XXX/expected-file.yml" |
dokka
Kotlin のドキュメント生成ツール
plugins {
/**
* dokka
*
* URL
* - https://github.com/Kotlin/dokka
* GradlePlugins(plugins.gradle.org)
* - https://plugins.gradle.org/plugin/org.jetbrains.dokka
* Main用途
* - ドキュメント生成
* Sub用途
* - 特になし
* 概要
* - JDocの代替(=KDoc)
*/
id("org.jetbrains.dokka") version "1.7.20"
}
dependencies {
/**
* dokkaHtmlPlugin
*
* dokka Pluginを適用するのに必要
*/
dokkaHtmlPlugin("org.jetbrains.dokka:kotlin-as-java-plugin:1.7.20")
}
Assertj
assertion を簡潔に書くためのライブラリ。
dependencies {
/**
* AssertJ
*
* URL
* - https://assertj.github.io/doc/
* MavenCentral
* - https://mvnrepository.com/artifact/org.assertj/assertj-core
* Main用途
* - JUnitでassertThat(xxx).isEqualTo(yyy)みたいな感じで比較時に使う
* Sub用途
* - 特になし
* 概要
* - JUnit等を直感的に利用するためのライブラリ
*/
testImplementation("org.assertj:assertj-core:3.23.1")
}