Repo created

This commit is contained in:
Fr4nz D13trich 2025-11-24 18:55:42 +01:00
parent a629de6271
commit 3cef7c5092
2161 changed files with 246605 additions and 2 deletions

72
build-plugin/README.md Normal file
View file

@ -0,0 +1,72 @@
# Build plugins
The `build-plugin` folder defines Gradle build plugins, used as single source of truth for the project configuration.
This helps to avoid duplicated build script setups and provides a central location for all common build logic.
## Background
We use Gradle's
[sharing build logic in a multi-repo setup](https://docs.gradle.org/current/samples/sample_publishing_convention_plugins.html)
to create common configuration. It allows usage of `xyz.gradle.kts` files, that are then automatically converted to
Gradle Plugins.
The `build-plugin` is used as included build in the root `settings.gradle.kts` and provides all
included `xyz.gradle.kts` as plugins under their `xyz` name to the whole project.
The plugins should try to accomplish single responsibility and leave one-off configuration to the module's `build.gradle.kts`.
## Convention plugins
- `thunderbird.app.android` - Configures common options for Android apps
- `thunderbird.app.android.compose` - Configures common options for Jetpack Compose, based
on `thunderbird.app.android`
- `thunderbird.library.android` - Configures common options for Android libraries
- `thunderbird.library.android.compose` - Configures common options for Jetpack Compose, based
on `thunderbird.library.android`
- `thunderbird.library.jvm` - Configures common options for JVM libraries
## Supportive plugins
- `thunderbird.dependency.check` - [Gradle Versions: Gradle plugin to discover dependency updates](https://github.com/ben-manes/gradle-versions-plugin)
- Use `./gradlew dependencyUpdates` to generate a dependency update report
- `thunderbird.quality.detekt` - [Detekt - Static code analysis for Kotlin ](https://detekt.dev/)
- Use `./gradlew detekt` to check for any issue and `./gradlew detektBaseline` in case you can't fix the reported
issue.
- `thunderbird.quality.spotless` - [Spotless - Code formatter](https://github.com/diffplug/spotless)
with [Ktlint - Kotlin linter and formatter](https://pinterest.github.io/ktlint/)
- Use `./gradlew spotlessCheck` to check for any issue and `./gradlew spotlessApply` to format your code
## Add new build plugin
Create a `thunderbird.xyz.gradle.kts` file, while `xyz` describes the new plugin.
If you need to access dependencies that are not yet defined in `build-plugin/build.gradle.kts` you have to:
1. Add the dependency to the version catalog `gradle/libs.versions.toml`
2. Then add it to `build-plugin/build.gradle.kts`.
1. In case of a plugin dependency use `implementation(plugin(libs.plugins.YOUR_PLUGIN_DEPENDENCY))`.
1. Otherwise `implementation(libs.YOUR_DEPENDENCY))`.
When done, add the plugin to `build-plugin/src/main/kotlin/ThunderbirdPlugins.kt`
Then apply the plugin to any subproject it should be used with:
```
plugins {
id(ThunderbirdPlugins.xyz)
}
```
If the plugin is meant for the root `build.gradle.kts`, you can't use `ThunderbirdPlugins`, as it's not available to
the `plugins` block. Instead use:
```
plugins {
id("thunderbird.xyz")
}
```
## Acknowledgments
- [Herding Elephants | Square Corner Blog](https://developer.squareup.com/blog/herding-elephants/)
- [Idiomatic Gradle: How do I idiomatically structure a large build with Gradle](https://github.com/jjohannes/idiomatic-gradle#idiomatic-build-logic-structure)

View file

@ -0,0 +1,21 @@
plugins {
`kotlin-dsl`
}
dependencies {
implementation(files(libs.javaClass.superclass.protectionDomain.codeSource.location))
implementation(plugin(libs.plugins.kotlin.jvm))
implementation(plugin(libs.plugins.kotlin.android))
implementation(plugin(libs.plugins.android.application))
implementation(plugin(libs.plugins.android.library))
implementation(plugin(libs.plugins.spotless))
implementation(plugin(libs.plugins.detekt))
implementation(plugin(libs.plugins.dependency.check))
}
fun plugin(provider: Provider<PluginDependency>) = with(provider.get()) {
"$pluginId:$pluginId.gradle.plugin:$version"
}

View file

@ -0,0 +1,21 @@
pluginManagement {
repositories {
gradlePluginPortal()
google()
mavenCentral()
}
}
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
versionCatalogs.create("libs") {
from(files("../gradle/libs.versions.toml"))
}
}
rootProject.name = "build-plugin"

View file

@ -0,0 +1,69 @@
import com.android.build.api.dsl.CommonExtension
import org.gradle.accessors.dm.LibrariesForLibs
import org.gradle.api.artifacts.dsl.DependencyHandler
internal fun CommonExtension<*, *, *, *>.configureSharedConfig() {
compileSdk = ThunderbirdProjectConfig.androidSdkCompile
defaultConfig {
compileSdk = ThunderbirdProjectConfig.androidSdkCompile
minSdk = ThunderbirdProjectConfig.androidSdkMin
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
}
compileOptions {
sourceCompatibility = ThunderbirdProjectConfig.javaVersion
targetCompatibility = ThunderbirdProjectConfig.javaVersion
}
lint {
abortOnError = false
}
testOptions {
unitTests {
isIncludeAndroidResources = true
}
}
}
internal fun CommonExtension<*, *, *, *>.configureSharedComposeConfig(
libs: LibrariesForLibs,
) {
buildFeatures {
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = libs.versions.androidxComposeCompiler.get()
}
lint {
warningsAsErrors = true
abortOnError = true
}
packagingOptions {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
}
}
}
internal fun DependencyHandler.configureSharedComposeDependencies(
libs: LibrariesForLibs,
) {
val composeBom = platform(libs.androidx.compose.bom)
implementation(composeBom)
androidTestImplementation(composeBom)
implementation(libs.bundles.shared.jvm.android.compose)
debugImplementation(libs.bundles.shared.jvm.android.compose.debug)
testImplementation(libs.bundles.shared.jvm.test.compose)
androidTestImplementation(libs.bundles.shared.jvm.androidtest.compose)
}

View file

@ -0,0 +1,14 @@
import org.gradle.api.artifacts.Dependency
import org.gradle.api.artifacts.dsl.DependencyHandler
internal fun DependencyHandler.implementation(dependencyNotation: Any): Dependency? =
add("implementation", dependencyNotation)
internal fun DependencyHandler.debugImplementation(dependencyNotation: Any): Dependency? =
add("debugImplementation", dependencyNotation)
internal fun DependencyHandler.testImplementation(dependencyNotation: Any): Dependency? =
add("testImplementation", dependencyNotation)
internal fun DependencyHandler.androidTestImplementation(dependencyNotation: Any): Dependency? =
add("androidTestImplementation", dependencyNotation)

View file

@ -0,0 +1,10 @@
import org.gradle.accessors.dm.LibrariesForLibs
import org.gradle.api.Project
import org.gradle.kotlin.dsl.DependencyHandlerScope
import org.gradle.kotlin.dsl.getByName
internal val Project.libs: LibrariesForLibs
get() = extensions.getByName<LibrariesForLibs>("libs")
internal fun Project.dependencies(configuration: DependencyHandlerScope.() -> Unit) =
DependencyHandlerScope.of(dependencies).configuration()

View file

@ -0,0 +1,12 @@
object ThunderbirdPlugins {
object App {
const val android = "thunderbird.app.android"
const val androidCompose = "thunderbird.app.android.compose"
}
object Library {
const val jvm = "thunderbird.library.jvm"
const val android = "thunderbird.library.android"
const val androidCompose = "thunderbird.library.android.compose"
}
}

View file

@ -0,0 +1,10 @@
import org.gradle.api.JavaVersion
object ThunderbirdProjectConfig {
val javaVersion = JavaVersion.VERSION_11
const val androidSdkMin = 21
const val androidSdkTarget = 31
const val androidSdkCompile = 33
}

View file

@ -0,0 +1,28 @@
import com.diffplug.gradle.spotless.SpotlessExtension
@Suppress("DSL_SCOPE_VIOLATION")
plugins {
id("com.diffplug.spotless")
}
configure<SpotlessExtension> {
kotlin {
ktlint(libs.versions.ktlint.get())
target("**/*.kt")
targetExclude("**/build/", "**/resources/", "plugins/openpgp-api-lib/")
}
kotlinGradle {
ktlint(libs.versions.ktlint.get())
target("**/*.gradle.kts")
targetExclude("**/build/")
}
format("markdown") {
prettier()
target("**/*.md")
targetExclude("plugins/openpgp-api-lib/")
}
format("misc") {
target("**/*.gradle", "**/.gitignore")
trimTrailingWhitespace()
}
}

View file

@ -0,0 +1,23 @@
plugins {
id("thunderbird.app.android")
}
android {
configureSharedComposeConfig(libs)
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro",
)
}
}
}
dependencies {
configureSharedComposeDependencies(libs)
implementation(libs.androidx.compose.activity)
}

View file

@ -0,0 +1,39 @@
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
}
android {
configureSharedConfig()
defaultConfig {
targetSdk = ThunderbirdProjectConfig.androidSdkTarget
}
buildFeatures {
buildConfig = true
}
compileOptions {
isCoreLibraryDesugaringEnabled = true
}
kotlinOptions {
jvmTarget = ThunderbirdProjectConfig.javaVersion.toString()
}
lint {
lintConfig = file("${rootProject.projectDir}/config/lint/lint.xml")
}
dependenciesInfo {
includeInApk = false
includeInBundle = false
}
}
dependencies {
coreLibraryDesugaring(libs.desugar)
testImplementation(libs.bundles.shared.jvm.test)
}

View file

@ -0,0 +1,40 @@
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
}
android {
configureSharedConfig()
defaultConfig {
targetSdk = ThunderbirdProjectConfig.androidSdkTarget
}
buildFeatures {
buildConfig = true
}
compileOptions {
isCoreLibraryDesugaringEnabled = true
}
kotlinOptions {
jvmTarget = ThunderbirdProjectConfig.javaVersion.toString()
}
lint {
checkDependencies = true
lintConfig = file("${rootProject.projectDir}/config/lint/lint.xml")
}
dependenciesInfo {
includeInApk = false
includeInBundle = false
}
}
dependencies {
coreLibraryDesugaring(libs.desugar)
testImplementation(libs.bundles.shared.jvm.test)
}

View file

@ -0,0 +1,18 @@
import com.github.benmanes.gradle.versions.updates.DependencyUpdatesTask
plugins {
id("com.github.ben-manes.versions")
}
tasks.withType<DependencyUpdatesTask> {
rejectVersionIf {
isNonStable(candidate.version) && !isNonStable(currentVersion)
}
}
fun isNonStable(version: String): Boolean {
val stableKeyword = listOf("RELEASE", "FINAL", "GA").any { version.toUpperCase().contains(it) }
val regex = "^[\\d,.v-]+(-r)?$".toRegex()
val isStable = stableKeyword || regex.matches(version)
return isStable.not()
}

View file

@ -0,0 +1,18 @@
plugins {
id("thunderbird.library.android")
}
android {
configureSharedComposeConfig(libs)
}
androidComponents {
beforeVariants(selector().withBuildType("release")) { variantBuilder ->
variantBuilder.enableUnitTest = false
variantBuilder.enableAndroidTest = false
}
}
dependencies {
configureSharedComposeDependencies(libs)
}

View file

@ -0,0 +1,32 @@
plugins {
id("com.android.library")
id("org.jetbrains.kotlin.android")
}
android {
configureSharedConfig()
buildFeatures {
buildConfig = false
}
kotlinOptions {
jvmTarget = ThunderbirdProjectConfig.javaVersion.toString()
}
lint {
lintConfig = file("${rootProject.projectDir}/config/lint/lint.xml")
}
testOptions {
unitTests {
isIncludeAndroidResources = true
}
}
}
dependencies {
implementation(libs.bundles.shared.jvm.main)
implementation(libs.bundles.shared.jvm.android)
testImplementation(libs.bundles.shared.jvm.test)
}

View file

@ -0,0 +1,14 @@
plugins {
`java-library`
id("org.jetbrains.kotlin.jvm")
}
java {
sourceCompatibility = ThunderbirdProjectConfig.javaVersion
targetCompatibility = ThunderbirdProjectConfig.javaVersion
}
dependencies {
implementation(libs.bundles.shared.jvm.main)
testImplementation(libs.bundles.shared.jvm.test)
}

View file

@ -0,0 +1,48 @@
import io.gitlab.arturbosch.detekt.Detekt
import io.gitlab.arturbosch.detekt.DetektCreateBaselineTask
import io.gitlab.arturbosch.detekt.extensions.DetektExtension
plugins {
id("io.gitlab.arturbosch.detekt")
}
configure<DetektExtension> {
source = project.files(
project.file(project.rootDir),
)
config = project.rootProject.files("config/detekt/detekt.yml")
baseline = project.rootProject.file("config/detekt/baseline.xml")
}
tasks.withType<Detekt>().configureEach {
jvmTarget = ThunderbirdProjectConfig.javaVersion.toString()
exclude(
"**/.gradle/**",
"**/.idea/**",
"**/build/**",
".github/**",
"gradle/**",
)
reports {
html.required.set(true)
sarif.required.set(true)
xml.required.set(true)
}
}
tasks.withType<DetektCreateBaselineTask>().configureEach {
exclude(
"**/.gradle/**",
"**/.idea/**",
"**/build/**",
".github/**",
"gradle/**",
)
}
dependencies {
detektPlugins(libs.detekt.plugin.compose)
}

View file

@ -0,0 +1,27 @@
import com.diffplug.gradle.spotless.SpotlessExtension
plugins {
id("com.diffplug.spotless")
}
configure<SpotlessExtension> {
kotlin {
ktlint(libs.versions.ktlint.get())
target("**/*.kt")
targetExclude("**/build/", "**/resources/", "plugins/openpgp-api-lib/")
}
kotlinGradle {
ktlint(libs.versions.ktlint.get())
target("**/*.gradle.kts")
targetExclude("**/build/")
}
format("markdown") {
prettier()
target("**/*.md")
targetExclude("plugins/openpgp-api-lib/")
}
format("misc") {
target("**/*.gradle", "**/.gitignore")
trimTrailingWhitespace()
}
}