-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbuild.gradle.kts
More file actions
203 lines (180 loc) · 8.85 KB
/
Copy pathbuild.gradle.kts
File metadata and controls
203 lines (180 loc) · 8.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
/*
* Copyright (c) 2026 dexpace and Omar Aljarrah
*
* Licensed under the MIT License. See LICENSE in the project root.
* SPDX-License-Identifier: MIT
*/
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import java.net.URI
plugins {
// The `base` plugin gives the root project the standard lifecycle tasks (notably
// `check` and `build`). Without it the root has no `check` task to hang the aggregate
// coverage verification on, so the documented 80% floor would never run from a plain
// `./gradlew build`. See the `check` wiring near the bottom of this file.
base
// `apply false` brings the Kotlin plugin onto the classpath so each subproject can
// declare `plugins { kotlin("jvm") }` without restating the version — but does not
// apply Kotlin to the root project itself (which has no source).
alias(libs.plugins.kotlin.jvm) apply false
// Kover is applied to the root so it can aggregate coverage across subprojects. Each
// subproject opts in via `plugins { id("org.jetbrains.kotlinx.kover") }`; the root
// collates their `kover.xml` / `kover.html` reports into one combined view.
alias(libs.plugins.kover)
// Binary-compatibility-validator generates .api snapshot files per module so that
// accidental public-API breakage is caught in CI. Run `./gradlew apiDump` to regenerate
// the baseline after intentional API changes.
alias(libs.plugins.binary.compatibility.validator)
// ktlint enforces Kotlin style across all source sets.
alias(libs.plugins.ktlint)
// detekt performs static analysis across all source sets.
alias(libs.plugins.detekt)
}
// `group` and `version` are set once in `gradle.properties` and applied by Gradle to the root
// project and every subproject — see that file.
// Coverage: aggregate every Kover-enabled *library* subproject through this root project's
// reports. `sdk-example` is deliberately absent: it is sample code built around a `main()`, and
// folding it into the aggregate would drag the 80% line-coverage floor down for code that exists
// to be read and run, not unit-tested to the library standard. The example does not apply the
// Kover plugin, so it contributes nothing to these reports; its own smoke test still runs under
// `build` and proves the sample assembles and executes end-to-end.
dependencies {
kover(project(":sdk-core"))
kover(project(":sdk-io-okio3"))
kover(project(":sdk-async-coroutines"))
kover(project(":sdk-async-reactor"))
kover(project(":sdk-async-netty"))
kover(project(":sdk-async-virtualthreads"))
kover(project(":sdk-transport-okhttp"))
kover(project(":sdk-transport-jdkhttp"))
kover(project(":sdk-serde-jackson"))
}
kover {
reports {
filters {
excludes {
// Test fixtures (test-only support code, fully-qualified org.dexpace namespace).
classes("org.dexpace.sdk.core.testing.*")
}
}
verify {
rule {
// Floor on aggregate LINE coverage. Branch coverage is reported but not gated.
// The floor starts loose and is raised in dedicated coverage-PR commits.
minBound(80)
}
}
}
}
// Make the aggregate coverage floor real: hang the root-aggregate `koverVerify` (the only
// task carrying the `minBound(80)` rule above) off the root `check` lifecycle task. Because
// the `base` plugin wires `build` → `check`, a plain `./gradlew build` (or `check`) now runs
// the aggregate verification and fails when the floor is breached. The per-module `koverVerify`
// tasks carry no rule and pass trivially, so the floor is enforced exactly once — no
// double-counting.
tasks.named("check") {
dependsOn(tasks.named("koverVerify"))
}
// Keep the unpublished modules out of the binary-compatibility snapshot. Neither ships a public
// artifact, so neither needs a committed `.api` file; without these exclusions apiCheck would
// demand one (and apiDump would generate a spurious snapshot for an unpublished module). Both are
// also left out of the kover aggregate above.
// - `sdk-shrink-test`: the test-only R8 shrink-survival guard.
// - `sdk-example`: the runnable end-to-end usage sample (an `application` module, no stable ABI).
apiValidation {
ignoredProjects += "sdk-shrink-test"
ignoredProjects += "sdk-example"
}
allprojects {
repositories {
mavenCentral()
maven {
// For maven snapshots
url = URI.create("https://oss.sonatype.org/content/repositories/snapshots/")
}
// Google's Maven repo hosts R8 (com.android.tools:r8), used only by the test-only
// sdk-shrink-test module. Restricted to that group so it is not consulted for anything else.
google {
content {
includeModule("com.android.tools", "r8")
}
}
}
// Plugin application lives in each subproject's own `plugins {}` block — the old
// `apply(plugin = ...)` form is discouraged by the Kotlin DSL. These callbacks fire
// once the relevant plugin is actually applied in a subproject.
plugins.withId("org.jetbrains.kotlin.jvm") {
extensions.configure<org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension> {
jvmToolchain(8)
// Explicit-api strict mode: every public declaration in the main source set must
// specify a visibility modifier and (for non-trivial returns) an explicit return
// type. The DSL form scopes this to the main source set — tests and test
// fixtures keep Kotlin's default-public behaviour for terse `@Test fun foo() {}`.
explicitApi = org.jetbrains.kotlin.gradle.dsl.ExplicitApiMode.Strict
}
// NOTE: Any new module that REQUIRES JDK 21+ APIs (e.g. virtual threads, sequenced
// collections) MUST override both `jvmToolchain(21)` in its kotlin/java extension
// blocks AND set `compilerOptions { jvmTarget.set(JvmTarget.JVM_21) }` in its own
// build script. Failing to do so produces bytecode that references Java 21 stdlib
// symbols but targets JVM_1_8 format — consumers on JDK 8 will see NoSuchMethodError
// at runtime, and Gradle's JVM-target-validation will reject the mismatch.
tasks.withType<KotlinCompile>().configureEach {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_1_8)
allWarningsAsErrors.set(true)
}
}
dependencies {
add("compileOnly", libs.slf4j.api)
// Modern nullability annotations on the compile classpath. kotlin-stdlib pins the ancient
// org.jetbrains:annotations:13.0 transitively, which lacks @UnknownNullability; Kotlin 2.4.0
// tooling materialises inferred platform types with that annotation and reports it as
// inaccessible against 13.0. compileOnly keeps it out of the published POM and ABI.
add("compileOnly", libs.jetbrains.annotations)
}
}
plugins.withId("java") {
extensions.configure<JavaPluginExtension> {
withSourcesJar()
withJavadocJar()
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
toolchain {
languageVersion.set(JavaLanguageVersion.of(8))
}
}
}
// Reproducible archives: strip timestamps and sort archive entries so that
// byte-for-byte identical JARs are produced from the same source across machines.
tasks.withType<AbstractArchiveTask>().configureEach {
isPreserveFileTimestamps = false
isReproducibleFileOrder = true
}
// Stamp the SDK coordinates into every Jar manifest so `SdkInfo.sdkVersion` (which reads
// `Package.getImplementationVersion()`) resolves to the project version instead of the
// "unknown" fallback. Without this the User-Agent emits `dexpace-sdk/unknown`.
tasks.withType<Jar>().configureEach {
manifest {
attributes(
"Implementation-Title" to project.name,
"Implementation-Version" to project.version,
)
}
}
}
// Apply ktlint and detekt to every subproject via the root build. Both run with
// `ignoreFailures = false` — style violations and static-analysis findings break the
// build. Tune `config/detekt.yml` (or add narrow `@Suppress` annotations) when a rule
// needs adjustment rather than re-enabling the legacy report-only mode.
subprojects {
apply(plugin = "org.jlleitschuh.gradle.ktlint")
apply(plugin = "io.gitlab.arturbosch.detekt")
configure<org.jlleitschuh.gradle.ktlint.KtlintExtension> {
ignoreFailures.set(false)
}
detekt {
config.setFrom("$rootDir/config/detekt.yml")
buildUponDefaultConfig = true
ignoreFailures = false
}
}