Skip to content

Commit

Permalink
feat: add static code analysis, linting and code coverage reporting
Browse files Browse the repository at this point in the history
  • Loading branch information
christian-draeger committed Jan 31, 2025
1 parent a684dd1 commit 462198c
Show file tree
Hide file tree
Showing 12 changed files with 173 additions and 48 deletions.
40 changes: 40 additions & 0 deletions .github/workflows/build-pr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: CI

on:
pull_request:
branches: [ "main" ]
paths-ignore:
- '.github/workflows/release.yml'
- '**/*.md'
- 'LICENSE'

jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read

steps:
- uses: actions/checkout@v4
- name: Set up JDK
uses: actions/setup-java@v4
with:
java-version: '23'
distribution: 'liberica'

- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4

- name: Build with Gradle Wrapper
run: ./gradlew build --s

- name: Add coverage report to PR
id: kover
uses: mi-kas/kover-report@v1.9
with:
path: ${{ github.workspace }}/build/reports/kover/report.xml
title: Code Coverage
update-comment: true
min-coverage-overall: 80
min-coverage-changed-files: 80
coverage-counter-type: LINE
8 changes: 0 additions & 8 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,6 @@ on:
- '.github/workflows/release.yml'
- '**/*.md'
- 'LICENSE'
pull_request:
branches: [ "main" ]
paths-ignore:
- '.github/workflows/release.yml'
- '**/*.md'
- 'LICENSE'

jobs:
build:
Expand All @@ -27,8 +21,6 @@ jobs:
java-version: '23'
distribution: 'liberica'

# Configure Gradle for optimal use in GitHub Actions, including caching of downloaded dependencies.
# See: https://github.com/gradle/actions/blob/main/setup-gradle/README.md
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4

Expand Down
55 changes: 55 additions & 0 deletions INITIAL_SETUP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# 🛠️ Installation and Usage

1. **Use the Template:**
- Fork the repository or use it directly as a GitHub Template.
- Customize `settings.gradle.kts` and `build.gradle.kts` to suit your project.
- change package name
- add or remove targets to the kotlin multiplatform extension in the [build.gradle.kts](build.gradle.kts)
- Give Github Actions required permissions to be able to open pull requests and commit tags to your repository.
- [x] Repository > Settings > Actions > General > Workflow permissions > Read and write permissions
- [x] Repository > Settings > Actions > General > Workflow permissions > Allow GitHub Actions to create and approve pull requests

2. **Adjust Modules:**
- Modify the `core` and `example` modules, or add new ones to expand functionality.

3. **Automated Releases:**
- Use GitHub Actions for automated CI/CD workflows.
- The GitHub action requires the following secrets to be set in your [project settings](settings/secrets/actions) to be able to publish to Maven Central:
- `MAVEN_CENTRAL_USERNAME`
- `MAVEN_CENTRAL_PASSWORD`
- `SIGNING_IN_MEMORY_KEY`
- `SIGNING_IN_MEMORY_KEY_ID`
- `SIGNING_IN_MEMORY_KEY_PASSWORD`
- You can find more information about how to get the required maven central credentials [here](https://medium.com/@iRYO400/how-to-upload-your-android-library-to-maven-central-central-portal-in-2024-af7348742247) or [here](https://medium.com/@efthymiou.dimitrios1/how-to-publish-your-library-to-maven-central-3923139967e1) or in the [official documentation](https://central.sonatype.org/register/central-portal/).
- Publishing To Maven Local:
- ```bash
./gradlew publishToMavenLocal
```

---

## 🤖 Preconfigured CI/CD Workflows

This template includes ready-to-use GitHub Actions workflows for:
- **Build & Test:** Ensures your library is reliable and bug-free.
- **Release:** Automatically publishes new versions when a release tag is created.
- **Linting:** Keeps your code clean and adherent to Kotlin standards.
- **Maintenance:** Automatic updates of your dependencies via Pull Request.
---

## ⚡️ Troubleshooting
### How to get the signing key
Please change the `<key id>` placeholder in the following command to your signing key id.
```bash
gpg --export-secret-keys --armor <key id> | grep -v '\-\-' | grep -v '^=.' | tr -d '\n'
```

---

## 📄 License

This repository is licensed under the [MIT License](LICENSE).

---

With this template, you can focus on writing great code while the boilerplate tasks are taken care of. Happy coding! 🎉
37 changes: 9 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
# Kotlin Library Template [![CI](https://github.com/christian-draeger/kotlin-library-template/actions/workflows/build.yml/badge.svg)](https://github.com/christian-draeger/kotlin-library-template/actions/workflows/build.yml) ![Maven Central Version](https://img.shields.io/maven-central/v/codes.draeger/kotlin-library-template-example?logo=sonatype&label=Release)

A **universal Kotlin library template** designed to kickstart your Kotlin library projects with ease. This template provides a robust and scalable foundation for library development, with built-in automation tools to guide you from initial setup to publishing your library effortlessly.
An **opinionated Kotlin library template** designed to jumpstart your Kotlin library projects with ease. This template provides a robust and scalable foundation for library development, with built-in automation tools to guide you from initial setup to publishing and maintaining your library effortlessly.

## 🚀 Features

- **⏩️ Quick Start:** No complex setup required. Simply fork this template, adjust the settings, and start coding.
- **🧱 Modular Structure:** Designed from the ground up to support splitting your library into multiple smaller libraries, enabling more granular access and flexibility for users of your project.
- **🌈 Powered by Kotlin Multiplatform:** Offers full flexibility in choosing your target platforms
- **📦 Automated Publishing:** Fully configured Gradle setup for publishing to Maven Central or other package repositories.
- **🗽 CI independent:** Every crucial task like build, test, release and version catalog update can be executed manually via Gradle tasks.
- **🔋 Best Practices Included:**
- **🗽 CI independent:** Every crucial task like build, test, release, lining, coverage measurement and version catalog update can be executed manually via Gradle tasks.
- **🔋 Ready to go:** Everything you need to get started with your library included:
- [x] [Explicit API mode](https://kotlinlang.org/docs/whatsnew14.html#explicit-api-mode-for-library-authors) to enforce visibility to be specifically declared.
- [x] Automatic versioning following [Semantic Versioning](https://semver.org/) principles.
- [x] [Gradle version catalog](https://docs.gradle.org/current/userguide/version_catalogs.html) for easy and clean cross module dependency management.
- [ ] [Gradle convention plugins](https://docs.gradle.org/current/samples/sample_convention_plugins.html) for consistent and reusable project configuration.
- [x] Automatic dependency version checks and updates via [Gradle Versions Plugin](https://github.com/littlerobots/version-catalog-update-plugin).
- [ ] static code analysis with [detekt](https://detekt.dev/) and [ktlint](https://ktlint.github.io/).
- [x] static code analysis and linting via [detekt](https://detekt.dev/) and [ktlint](https://ktlint.github.io/).
- [ ] [OSS Review Toolkit](https://oss-review-toolkit.org/ort/docs/intro) integration for automated license compliance checks.
- [ ] Testing setup and coverage reporting out of the box.
- **🟢 CI/CD Ready:** Preconfigured GitHub Actions workflows for seamless builds, releases and maintenance.
- [x] Build, test and release snapshot version on every push to main.
- [x] On-Click publish to Maven Central with automatic versioning.
- [x] One-Click publish to Maven Central with automatic versioning.
- [x] Scheduled automatic version catalog update updates via PR by using gradle task.

---
Expand All @@ -41,29 +41,10 @@ This template comes with a powerful **automated publishing system**:

## 🛠️ Installation and Usage

1. **Use the Template:**
- Fork the repository or use it directly as a GitHub Template.
- Customize `settings.gradle.kts` and `build.gradle.kts` to suit your project.
- Give Github Actions required permissions to be able to open pull requests and commit tags to your repository.
- [x] Repository > Settings > Actions > General > Workflow permissions > Read and write permissions
- [x] Repository > Settings > Actions > General > Workflow permissions > Allow GitHub Actions to create and approve pull requests

2. **Adjust Modules:**
- Modify the `core` and `example` modules, or add new ones to expand functionality.

3. **Automated Releases:**
- Use GitHub Actions for automated CI/CD workflows.
- The GitHub action requires the following secrets to be set in your [project settings](settings/secrets/actions) to be able to publish to Maven Central:
- `MAVEN_CENTRAL_USERNAME`
- `MAVEN_CENTRAL_PASSWORD`
- `SIGNING_IN_MEMORY_KEY`
- `SIGNING_IN_MEMORY_KEY_ID`
- `SIGNING_IN_MEMORY_KEY_PASSWORD`
- You can find more information about how to get the required maven central credentials [here](https://medium.com/@iRYO400/how-to-upload-your-android-library-to-maven-central-central-portal-in-2024-af7348742247) or [here](https://medium.com/@efthymiou.dimitrios1/how-to-publish-your-library-to-maven-central-3923139967e1) or in the [official documentation](https://central.sonatype.org/register/central-portal/).
- Publishing To Maven Local:
- ```bash
./gradlew publishToMavenLocal
```
Even though this template tries to reduce necessary manual steps as much as possible,
there are still some things you need to do, since you can not inherit some settings from github template repository.

Please follow the [initial setup guide](INITIAL_SETUP.md) to get started. It won't take more than a few minutes.

---

Expand Down
35 changes: 31 additions & 4 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import com.adarshr.gradle.testlogger.theme.ThemeType.MOCHA_PARALLEL
import io.gitlab.arturbosch.detekt.Detekt

plugins {
alias(libs.plugins.mavenPublishPlugin)
Expand All @@ -8,6 +9,8 @@ plugins {
alias(libs.plugins.versionsPlugin)
alias(libs.plugins.versionsFilterPlugin)
alias(libs.plugins.versionCatalogUpdate)

alias(libs.plugins.detekt)
}

// aggregate dependents to an all-in-one library
Expand All @@ -20,11 +23,21 @@ kotlin {
}
}

allprojects {
apply(plugin = "org.jetbrains.kotlin.multiplatform")
apply(plugin = "com.vanniktech.maven.publish")
apply(plugin = "com.adarshr.test-logger")
tasks {
// detektAll is a task that runs detekt on all src sets
val detektAll by registering {
allprojects {
this@registering.dependsOn(tasks.withType<Detekt>())
}
}
build {
dependsOn(detektAll)
finalizedBy(koverXmlReport, koverHtmlReport)
}
}

allprojects {
apply(plugin = rootProject.libs.plugins.kotlinMultiplatform.get().pluginId)
kotlin {
explicitApi()
jvm()
Expand All @@ -34,6 +47,7 @@ allprojects {
}
}

apply(plugin = rootProject.libs.plugins.mavenPublishPlugin.get().pluginId)
mavenPublishing {
val artifactId = if (project.name == rootProject.name) project.name else "${rootProject.name}-${project.name}"
coordinates(artifactId = artifactId)
Expand All @@ -42,6 +56,19 @@ allprojects {
}
}

apply(plugin = rootProject.libs.plugins.detekt.get().pluginId)
detekt {
toolVersion = rootProject.libs.versions.detekt.get()
autoCorrect = true
config.setFrom(file("$rootDir/config/detekt/detekt.yml"))
buildUponDefaultConfig = true
// ignoreFailures = true
}
dependencies {
detektPlugins(rootProject.libs.detekt.formatting)
}

apply(plugin = rootProject.libs.plugins.testLogger.get().pluginId)
testlogger {
theme = MOCHA_PARALLEL
showFullStackTraces = false
Expand Down
8 changes: 8 additions & 0 deletions config/detekt/detekt.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
build:
maxIssues: 0
excludeCorrectable: false
weights:
# complexity: 2
# LongParameterList: 1
# style: 1
# comments: 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package codes.draeger.example.core

import kotlin.test.Test
import kotlin.test.assertEquals

internal class ExampleTest {

@Test
fun exampleTest() {
assertEquals(Example().foo(), "bar")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ package codes.draeger.example.core

import kotlin.test.Test

internal class PrinterTest {
internal class LoggerUtilsKtTest {

@Test
fun testMessage() {
fun exampleTest() {
logger.info { "dfd" }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,4 @@ package codes.draeger.example.extensions.example

import codes.draeger.example.core.Example

public fun foobar() {
println(Example().foo())
}
public fun foobar(): String = Example().foo().also(::println)
7 changes: 4 additions & 3 deletions extensions/example/src/commonTest/kotlin/UtilitiesTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ package codes.draeger.utils

import codes.draeger.example.extensions.example.foobar
import kotlin.test.Test
import kotlin.test.assertEquals

internal class PrinterTest {
internal class UtilitiesKtTest {

@Test
fun testMessage() {
foobar()
fun exampleTest() {
assertEquals(foobar(), "bar")
}
}
5 changes: 5 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ testLogger = "4.0.0"
versionCatalogUpdate = "0.8.5"
versionsFilterPlugin = "0.1.16"
versionsPlugin = "0.52.0"
detekt = "1.23.7"
kover = "0.9.1"

[plugins]
kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
Expand All @@ -20,13 +22,16 @@ testLogger = { id = "com.adarshr.test-logger", version.ref = "testLogger" }
versionCatalogUpdate = { id = "nl.littlerobots.version-catalog-update", version.ref = "versionCatalogUpdate" }
versionsFilterPlugin = { id = "se.ascp.gradle.gradle-versions-filter", version.ref = "versionsFilterPlugin" }
versionsPlugin = { id = "com.github.ben-manes.versions", version.ref = "versionsPlugin" }
detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" }
kover = { id = "org.jetbrains.kotlinx.kover.aggregation", version.ref = "kover" }

[libraries]
kotlinLogging = { module = "io.github.oshai:kotlin-logging", version.ref = "kotlinLogging" }
kotlinxCoroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinxCoroutines" }
kotlinxDatetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "kotlinxDatetime" }
kotlinxSerialization = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJSON" }
slf4jSimple = { module = "org.slf4j:slf4j-simple", version.ref = "slf4j" }
detekt-formatting = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", version.ref = "detekt" }

[bundles]
kotlinxEcosystem = [
Expand Down
6 changes: 6 additions & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import kotlinx.kover.gradle.aggregation.settings.dsl.minBound

rootProject.name = "kotlin-library-template"
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")

Expand All @@ -20,4 +22,8 @@ project(":example").projectDir = file("extensions/example")

plugins {
id("org.gradle.toolchains.foojay-resolver-convention") version "0.9.0"
id("org.jetbrains.kotlinx.kover.aggregation") version "0.9.1"
}
kover {
enableCoverage()
}

0 comments on commit 462198c

Please sign in to comment.