There was none: no .editorconfig, no static analysis, and CI ran only tests. Style was whatever the IDE happened to do, which is fine until two of them disagree. The split is LibreMail's, because it is the one that avoids arguments between tools: ktlint owns formatting, detekt owns static analysis with its formatting ruleset left off. Neither can contradict the other about the same line. Adapted rather than copied. LibreMail is Gradle 9.6 / JDK 21 with the configuration cache off; this is Gradle 9.5 / JDK 17 with it on, and Kotlin lives under src/main/java rather than src/main/kotlin -- so its plugin versions were evidence, not proof. Verified here before committing: both plugins resolve, ktlint reads src/main/java, and the run stores a configuration cache entry rather than tripping over it. detekt.yml carries only what applies. The Compose relaxations transfer intact -- a @Composable function is legitimately long, PascalCase, and full of dp literals no matter which app it is in. LibreMail's ForbiddenImport guard and its LargeClass exclusions do not: they name an AppLog facade and two test files that exist over there and nowhere here, and config that guards nothing is worse than no config, because the next reader has to work out that it is dead. detekt 2.0 is an alpha. That is not a preference: stable 1.23.x stops at Gradle 8.12 and this project is on 9.5, so there is no other line to be on. Coverage is reported, not gated. A floor needs a measured baseline, and the JVM test stack here is still junit-only -- a number picked before measuring would either fail on day one or mean nothing. Android lint gets warningsAsErrors because the other two tools fail on any finding, and a gate that stays green while its report fills up is not a gate. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
94 lines
5.3 KiB
TOML
94 lines
5.3 KiB
TOML
[versions]
|
|
# Build tooling.
|
|
# NOTE: AGP 9 has BUILT-IN Kotlin support. Applying org.jetbrains.kotlin.android
|
|
# FAILS the build. AGP 9.3.1 brings kotlin-gradle-plugin 2.2.10 transitively, so the
|
|
# Compose compiler plugin below must match that version, not the newest Kotlin release.
|
|
agp = "9.3.1"
|
|
kotlin = "2.2.10"
|
|
ksp = "2.3.11"
|
|
|
|
# AndroidX / Compose
|
|
composeBom = "2026.08.00"
|
|
coreKtx = "1.19.0"
|
|
activityCompose = "1.13.0"
|
|
lifecycle = "2.11.0"
|
|
navigation = "2.9.8"
|
|
work = "2.11.2"
|
|
datastore = "1.2.1"
|
|
media3 = "1.11.0"
|
|
room = "2.8.4"
|
|
documentfile = "1.1.0"
|
|
annotation = "1.10.0"
|
|
|
|
# Test
|
|
junit = "4.13.2"
|
|
androidxJunit = "1.3.0"
|
|
espressoCore = "3.7.0"
|
|
smartException = "0.2.1"
|
|
|
|
# Lint/format. ktlint owns formatting; detekt owns static analysis (its formatting
|
|
# ruleset stays off, so the two can never disagree about the same line).
|
|
# detekt 2.0 is the only line with Gradle 9 support -- stable 1.23.x tops out at
|
|
# Gradle 8.12, and this project is on 9.5.
|
|
ktlint = "14.2.0"
|
|
detekt = "2.0.0-alpha.5"
|
|
# JaCoCo coverage agent. Pinned rather than inheriting whatever Gradle 9.5 bundles,
|
|
# so the agent version that reads Kotlin 2.2.10 bytecode is stated, not implied.
|
|
jacoco = "0.8.13"
|
|
|
|
[libraries]
|
|
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
|
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
|
|
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycle" }
|
|
androidx-lifecycle-runtime-compose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "lifecycle" }
|
|
androidx-lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "lifecycle" }
|
|
|
|
# Compose — versions come from the BOM, so no version.ref here.
|
|
compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
|
|
compose-material3 = { group = "androidx.compose.material3", name = "material3" }
|
|
compose-material3-windowsize = { group = "androidx.compose.material3", name = "material3-window-size-class" }
|
|
compose-material-icons-extended = { group = "androidx.compose.material", name = "material-icons-extended" }
|
|
compose-ui = { group = "androidx.compose.ui", name = "ui" }
|
|
compose-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
|
|
compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
|
|
compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
|
|
compose-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
|
|
compose-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
|
|
|
|
androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigation" }
|
|
androidx-work-runtime-ktx = { group = "androidx.work", name = "work-runtime-ktx", version.ref = "work" }
|
|
androidx-work-testing = { group = "androidx.work", name = "work-testing", version.ref = "work" }
|
|
androidx-datastore-preferences = { group = "androidx.datastore", name = "datastore-preferences", version.ref = "datastore" }
|
|
androidx-documentfile = { group = "androidx.documentfile", name = "documentfile", version.ref = "documentfile" }
|
|
androidx-annotation = { group = "androidx.annotation", name = "annotation", version.ref = "annotation" }
|
|
|
|
# Media3 — the hardware fast path (Apache-2.0).
|
|
media3-transformer = { group = "androidx.media3", name = "media3-transformer", version.ref = "media3" }
|
|
media3-effect = { group = "androidx.media3", name = "media3-effect", version.ref = "media3" }
|
|
media3-common = { group = "androidx.media3", name = "media3-common", version.ref = "media3" }
|
|
media3-exoplayer = { group = "androidx.media3", name = "media3-exoplayer", version.ref = "media3" }
|
|
media3-muxer = { group = "androidx.media3", name = "media3-muxer", version.ref = "media3" }
|
|
|
|
# Room — job history. Added in a later phase; KSP plugin stays unapplied until then.
|
|
androidx-room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" }
|
|
androidx-room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" }
|
|
androidx-room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" }
|
|
|
|
# Required at runtime by the ffmpeg-kit-next wrapper: AbstractSession.fail()
|
|
# references smartexception.java.Exceptions. Debug builds tolerate its absence through
|
|
# lazy class loading, so this only surfaces as a crash on the first FFmpeg error --
|
|
# or, as it did here, as an R8 missing-class error.
|
|
smart-exception-java = { group = "com.arthenica", name = "smart-exception-java", version.ref = "smartException" }
|
|
|
|
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
|
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidxJunit" }
|
|
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
|
|
|
|
[plugins]
|
|
android-application = { id = "com.android.application", version.ref = "agp" }
|
|
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|
|
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
|
|
ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlint" }
|
|
detekt = { id = "dev.detekt", version.ref = "detekt" }
|
|
# DO NOT add org.jetbrains.kotlin.android — AGP 9 built-in Kotlin makes it a build failure.
|