Skip to content

Commit 2cdbfb3

Browse files
authored
Add example of library usage in a Gradle build (#452)
Add a tested example to support the use case of a Gradle build. Related to #404 aiming to prevent regressions around this use case.
1 parent c40bb74 commit 2cdbfb3

File tree

16 files changed

+581
-3
lines changed

16 files changed

+581
-3
lines changed

.gitignore

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ profile-out*
88

99
build
1010
!.github/actions/build
11-
!*/src/**/build
12-
!*/*/src/**/build
11+
!**/src/**/build
1312
.ipynb_checkpoints
1413

1514
.venv

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ A Kotlin library to access the [Develocity API][1], easy to use from:
1010
- [Jupyter notebooks with the Kotlin kernel][29]
1111
- [Kotlin scripts (`kts`)][27]
1212
- [Kotlin projects][28]
13+
- [Gradle tasks][36]
1314

1415
```kotlin
1516
val api = DevelocityApi.newInstance()
@@ -232,3 +233,4 @@ For general discussions or questions, feel free to reach out to maintainers on t
232233
[33]: https://github.com/gradle/develocity-api-samples
233234
[34]: https://github.com/gabrielfeo/develocity-api-kotlin/blob/main/build-logic/src/functionalTest/kotlin/com/gabrielfeo/task/PostProcessGeneratedApiTest.kt#L21
234235
[35]: https://community.gradle.org/#community-channels
236+
[36]: ./examples/example-gradle-task/

build-logic/src/main/kotlin/com/gabrielfeo/examples-test-suite.gradle.kts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,11 @@ kotlin {
2424

2525
val examples = fileTree(rootDir) {
2626
include("examples/**")
27-
exclude("**/build", "**/.*")
27+
exclude {
28+
it.isDirectory
29+
&& (it.name == "build" || it.name.startsWith("."))
30+
&& !it.path.endsWith("build-logic/src/main/kotlin/build")
31+
}
2832
}
2933

3034
tasks.named("processExamplesTestResources", ProcessResources::class) {
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
2+
3+
# Example usage in build logic
4+
5+
This example shows how to create a reusable Gradle plugin that adds a `userBuildPerformanceMetrics` task to fetch and print build performance metrics for a specific user, using the Develocity API Kotlin client.
6+
7+
## Core files
8+
9+
- [`build-logic/src/main/kotlin/build/logic/PerformanceMetricsTask.kt`](./build-logic/src/main/kotlin/build/logic/PerformanceMetricsTask.kt): Implements a custom Gradle task that fetches and prints build performance metrics of a given user.
10+
- [`build-logic/src/main/kotlin/build/logic/DevelocityApiService.kt`](./build-logic/src/main/kotlin/build/logic/DevelocityApiService.kt): Defines a shared build service containing the Develocity API client, which could be used by multiple tasks while ensuring a singleton client per build.
11+
- [`build-logic/performance-metrics-plugin.gradle.kts`](./build-logic/performance-metrics-plugin.gradle.kts): A plugin which registers the task where it's applied.
12+
- [`build.gradle.kts`](./build.gradle.kts): Applies `performance-metrics-plugin`, making the task available for this build.
13+
14+
## Usage
15+
16+
Run:
17+
18+
```sh
19+
./gradlew userBuildPerformanceMetrics [--user=foo] --period=[-1d|-7d|-14d|-30d|...]
20+
```
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
plugins {
2+
`kotlin-dsl`
3+
}
4+
5+
dependencies {
6+
implementation("com.gabrielfeo:develocity-api-kotlin:2024.3.0")
7+
implementation("org.apache.commons:commons-math3:3.6.1")
8+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
rootProject.name = "dak-example-gradle-task-build-logic"
2+
3+
dependencyResolutionManagement {
4+
repositories {
5+
mavenCentral()
6+
gradlePluginPortal()
7+
}
8+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package build.logic
2+
3+
import com.gabrielfeo.develocity.api.Config
4+
import com.gabrielfeo.develocity.api.DevelocityApi
5+
import org.gradle.api.services.BuildService
6+
import org.gradle.api.services.BuildServiceParameters
7+
import okhttp3.OkHttpClient
8+
9+
abstract class DevelocityApiService
10+
: DevelocityApi by DevelocityApi.newInstance(config()),
11+
BuildService<BuildServiceParameters.None>,
12+
AutoCloseable {
13+
14+
override fun close() {
15+
shutdown()
16+
}
17+
}
18+
19+
private fun config() = Config(
20+
// Necessary to accomodate Gradle's build service lifecycle because library
21+
// uses a singleton OkHttpClient.Builder unless one is provided.
22+
// See https://github.com/gabrielfeo/develocity-api-kotlin/issues/451
23+
clientBuilder = OkHttpClient.Builder(),
24+
)
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package build.logic
2+
3+
import com.gabrielfeo.develocity.api.DevelocityApi
4+
import kotlinx.coroutines.runBlocking
5+
import com.gabrielfeo.develocity.api.model.BuildModelName
6+
import com.gabrielfeo.develocity.api.model.Build
7+
import com.gabrielfeo.develocity.api.model.GradleBuildCachePerformance
8+
import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics
9+
import org.gradle.api.DefaultTask
10+
import org.gradle.api.provider.Property
11+
import org.gradle.api.tasks.Input
12+
import org.gradle.api.tasks.Optional
13+
import org.gradle.api.tasks.TaskAction
14+
import org.gradle.api.tasks.options.Option
15+
import org.gradle.api.services.ServiceReference
16+
import java.time.Duration
17+
18+
19+
abstract class PerformanceMetricsTask(
20+
21+
) : DefaultTask() {
22+
23+
@get:Optional
24+
@get:Input
25+
@get:Option(
26+
option = "user",
27+
description = "The user to query builds for. Defaults to the current OS username."
28+
)
29+
abstract val user: Property<String>
30+
31+
@get:Optional
32+
@get:Input
33+
@get:Option(
34+
option = "period",
35+
description = "The period to query builds for (e.g. -14d, -30d, etc). Default: -14d."
36+
)
37+
abstract val period: Property<String>
38+
39+
@get:ServiceReference
40+
abstract val api: Property<DevelocityApiService>
41+
42+
@TaskAction
43+
fun run() {
44+
val user = user.getOrElse(System.getProperty("user.name"))
45+
val startTime = period.getOrElse("-14d")
46+
val metrics = runBlocking {
47+
getUserBuildsPerformanceMetrics(api.get(), user, startTime)
48+
}
49+
logger.quiet(metrics)
50+
}
51+
52+
suspend fun getUserBuildsPerformanceMetrics(
53+
api: DevelocityApi,
54+
user: String,
55+
startTime: String,
56+
): String {
57+
val query = """user:"$user" buildStartTime>$startTime"""
58+
val buildsPerformanceData = fetchBuildsPerformanceData(api, query)
59+
val serializationFactors = buildsPerformanceData
60+
.map { it.serializationFactor }
61+
.let { DescriptiveStatistics(it.toDoubleArray()) }
62+
val avoidanceSavings = buildsPerformanceData
63+
.map { it.workUnitAvoidanceSavingsSummary.ratio }
64+
.let { DescriptiveStatistics(it.toDoubleArray()) }
65+
val heading = "Build performance overview for $user since $startTime (powered by Develocity®)"
66+
return """
67+
|
68+
|${"\u001B[1;36m"}$heading${"\u001B[0m"}
69+
| ▶︎ Serialization factor: %.1fx
70+
| (Gradle's parallel execution)
71+
| ⏩︎ Avoidance savings: %.1f%% (mean) ~ %.1f%% (p95)
72+
| (Gradle and Develocity's mechanisms, incl. incremental build and remote cache)
73+
""".trimMargin().format(
74+
serializationFactors.mean,
75+
avoidanceSavings.mean,
76+
avoidanceSavings.getPercentile(95.0),
77+
)
78+
}
79+
80+
private suspend fun fetchBuildsPerformanceData(
81+
api: DevelocityApi,
82+
query: String,
83+
): List<GradleBuildCachePerformance> {
84+
return api.buildsApi.getBuilds(
85+
fromInstant = 0,
86+
query = query,
87+
models = listOf(BuildModelName.gradleBuildCachePerformance),
88+
).mapNotNull { build ->
89+
build.models?.gradleBuildCachePerformance?.model
90+
}
91+
}
92+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package build.logic
2+
3+
gradle.sharedServices.registerIfAbsent("develocityApiService", DevelocityApiService::class)
4+
5+
tasks.register<PerformanceMetricsTask>("userBuildPerformanceMetrics") {
6+
group = "Develocity"
7+
description = "Retrieves performance metrics for the user's builds from Develocity API."
8+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
plugins {
2+
id("build.logic.performance-metrics-plugin")
3+
}

0 commit comments

Comments
 (0)