Skip to content

Commit 4718484

Browse files
authored
Switch to using Gradle Configurations for sharing data between projects (#21)
Fixes #18
1 parent 51266ee commit 4718484

10 files changed

+247
-112
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ extensions.configure<MetricsForDevelocityExtension> {
4747
// value is configured for this property then the Develocity key will be searched
4848
// for in the standard locations for manual key provisioning, documented here:
4949
// https://docs.gradle.com/develocity/gradle-plugin/current/#manual_access_key_configuration
50-
develocityAccessKey.set("https://custom-develocity-server.com")
50+
develocityAccessKey.set("your_base64_encoded_access_key")
5151

5252
// Optional: Configure the query filter to use when querying the Develocity server for
5353
// builds. This filter is expressed using the Develocity's advanced search syntax:

docs/Design.md

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,30 @@ Time window tasks (e.g., last 7 days)
3535
- Loads the hourly summary files
3636
- reduces the summaries together, persisting them to disk by ID
3737

38-
## Data consumer/reporting tasks
38+
All data gathering tasks are performed only in the root project.
39+
40+
## Consumable configurations
41+
42+
In order to expose the root project's data to the consuming projects the root project defines
43+
consumable configurations which export the data produced by the data gathering tasks to the
44+
consuming projects. These configurations use the same naming conventions as the data gathering
45+
tasks.
46+
47+
To simplify the chore of wiring up the consuming projects, the
48+
[TaskProviderExtensions](../src/main/kotlin/com/ebay/plugins/metrics/develocity/TaskProviderExtensions.kt)
49+
file provides a set of extension functions that can be used to wire up an individual sumarizer's output of the data
50+
gathering task to a consuming project's `TaskProvider<out MetricSummarizerTask>` instance.
51+
52+
For more advanced use cases, the consuming project can create a resolvable Gradle `Configuration` to refer to
53+
the root project's configuration by name. When this approach is used, the resolvable configuration should specify the
54+
[SUMMARIZER_ATTRIBUTE](../src/main/kotlin/com/ebay/plugins/metrics/develocity/MetricsForDevelocityConstants.kt)
55+
with a value of [SUMMARIZER_ALL], which will result in a directory of all summarizer outputs. To select an
56+
individual summarizer's output, the consuming project can leverage the
57+
[SummarizerSelectTransform](../src/main/kotlin/com/ebay/plugins/metrics/develocity/SummarizerSelectTransform.kt)
58+
artifact transform to get at the desired individual output file.
59+
60+
## Data consumer / reporting tasks
3961

4062
Data consumer / reporting tasks are configured to consume the aggregated data from the first
4163
stage of the pipeline and produce data or reports in their final form. These tasks are
4264
to be implemented as needed to satisfy the requirements of the project.
43-
44-
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package com.ebay.plugins.metrics.develocity
2+
3+
import org.gradle.api.attributes.Attribute
4+
5+
/**
6+
* Constants defined by the plugin which may be used by consuming plugins or build scripts.
7+
*/
8+
object MetricsForDevelocityConstants {
9+
/**
10+
* The name that the [MetricsForDevelocityExtension] is registered under.
11+
*/
12+
const val EXTENSION_NAME = "metricsForDevelocity"
13+
14+
/**
15+
* The gradle property name used to configure the query filter.
16+
*/
17+
const val QUERY_FILTER_PROPERTY = "metricsForDevelocityQueryFilter"
18+
19+
/**
20+
* The variant attribute used to identify what summarizer data is being exported or resolved. The special
21+
* value of [SUMMARIZER_ALL] is used will result in a directory contianing all summarizer data. Consumers
22+
* can start with this and apply a [SummarizerSelectTransform] to filter down to a single summarizer output.
23+
*/
24+
val SUMMARIZER_ATTRIBUTE = Attribute.of("com.ebay.metrics-for-develocity.summarizer", String::class.java)
25+
26+
/**
27+
* The attribute value used to identify the configuration used to export all summarizer data to
28+
* consuming projects.
29+
*/
30+
val SUMMARIZER_ALL = "_all_"
31+
}

src/main/kotlin/com/ebay/plugins/metrics/develocity/MetricsForDevelocityInternalExtension.kt

Lines changed: 0 additions & 28 deletions
This file was deleted.

src/main/kotlin/com/ebay/plugins/metrics/develocity/MetricsForDevelocityPlugin.kt

Lines changed: 72 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
package com.ebay.plugins.metrics.develocity
22

3+
import com.ebay.plugins.metrics.develocity.MetricsForDevelocityConstants.EXTENSION_NAME
4+
import com.ebay.plugins.metrics.develocity.MetricsForDevelocityConstants.QUERY_FILTER_PROPERTY
5+
import com.ebay.plugins.metrics.develocity.MetricsForDevelocityConstants.SUMMARIZER_ALL
6+
import com.ebay.plugins.metrics.develocity.MetricsForDevelocityConstants.SUMMARIZER_ATTRIBUTE
7+
import com.ebay.plugins.metrics.develocity.NameUtil.DATETIME_TASK_PATTERN
8+
import com.ebay.plugins.metrics.develocity.NameUtil.DURATION_TASK_PATTERN
39
import com.ebay.plugins.metrics.develocity.projectcost.ProjectCostPlugin
410
import com.ebay.plugins.metrics.develocity.service.DevelocityBuildService
511
import com.ebay.plugins.metrics.develocity.userquery.UserQueryPlugin
@@ -20,7 +26,7 @@ import javax.inject.Inject
2026
* Plugin implementation which defines tasks and configurations artifacts which are used to
2127
* generate aggregate metric data based upon Develocity build scans.
2228
*/
23-
@Suppress("unused") // False positive
29+
@Suppress("unused", "UnstableApiUsage") // False positive
2430
internal class MetricsForDevelocityPlugin @Inject constructor(
2531
private val providerFactory: ProviderFactory
2632
) : Plugin<Any> {
@@ -70,6 +76,8 @@ internal class MetricsForDevelocityPlugin @Inject constructor(
7076
}
7177

7278
private fun applyProject(project: Project) {
79+
project.dependencies.attributesSchema.attribute(SUMMARIZER_ATTRIBUTE)
80+
7381
if (project.parent == null) {
7482
applyRootProject(project)
7583
}
@@ -135,11 +143,68 @@ internal class MetricsForDevelocityPlugin @Inject constructor(
135143
ext,
136144
)
137145

146+
/*
147+
* Creates a rule for generating consumable configurations for date time requests.
148+
* These can be scoped to daily or hourly time specifications.
149+
*/
150+
project.configurations.addRule(
151+
"Pattern: ${NameUtil.dateTime("<YYYY>-<MM>-<DD>[T<HH>]")} " +
152+
"Gathers Develocity metrics for the date (and optionally hour) specified."
153+
) { configurationName ->
154+
val matcher = DATETIME_TASK_PATTERN.matcher(configurationName)
155+
if (!matcher.matches()) return@addRule
156+
157+
val date = matcher.group(2)
158+
val timeSpec: String = matcher.group(1)
159+
val hour: String? = matcher.group(3)
160+
161+
project.configurations.register(configurationName) { config ->
162+
with(config) {
163+
isCanBeConsumed = true
164+
isCanBeResolved = false
165+
isTransitive = false
166+
attributes.attribute(SUMMARIZER_ATTRIBUTE, SUMMARIZER_ALL)
167+
}
168+
}
169+
170+
val artifactTaskProvider = if (hour == null) {
171+
pluginContext.registerDaily(date)
172+
} else {
173+
pluginContext.registerHourly(timeSpec)
174+
}
175+
project.artifacts.add(configurationName, artifactTaskProvider)
176+
}
177+
178+
/*
179+
* Creates a rule for generating consumable configurations for duration-based requests.
180+
* These can be scoped to specific time duration specifications.
181+
*/
182+
project.configurations.addRule(
183+
"Pattern: ${NameUtil.duration("<Java Duration String>")} " +
184+
"Aggregates Develocity metrics for the current date back in time for the " +
185+
"specified duration."
186+
) { configurationName ->
187+
val matcher = DURATION_TASK_PATTERN.matcher(configurationName)
188+
if (!matcher.matches()) return@addRule
189+
190+
val durationStr: String = matcher.group(1)
191+
val consumableConfig = project.configurations.register(configurationName)
192+
consumableConfig.configure { config ->
193+
with(config) {
194+
isCanBeConsumed = true
195+
isCanBeResolved = false
196+
isTransitive = false
197+
attributes.attribute(SUMMARIZER_ATTRIBUTE, SUMMARIZER_ALL)
198+
}
199+
}
200+
project.artifacts.add(configurationName, pluginContext.registerDurationAggregation(durationStr))
201+
}
202+
138203
/*
139204
* Rule to register a task for a specific date and optionally hour.
140205
*/
141206
project.tasks.addRule(
142-
"Pattern: $TASK_PREFIX-<YYYY>-<MM>-<DD>T[<HH>] " +
207+
"Pattern: ${NameUtil.dateTime("<YYYY>-<MM>-<DD>[T<HH>]")} " +
143208
"Gathers Develocity metrics for the date (and optionally hour) specified."
144209
) { taskName ->
145210
val matcher = DATETIME_TASK_PATTERN.matcher(taskName)
@@ -162,7 +227,7 @@ internal class MetricsForDevelocityPlugin @Inject constructor(
162227
* Rule to register a task for a time window / duration, relative to the current day and hour.
163228
*/
164229
project.tasks.addRule(
165-
"Pattern: $TASK_PREFIX-last-<Java Duration String> " +
230+
"Pattern: ${NameUtil.duration("<Java Duration String>")} " +
166231
"Aggregates Develocity metrics for the current date back in time for the " +
167232
"specified duration."
168233
) { taskName ->
@@ -172,22 +237,14 @@ internal class MetricsForDevelocityPlugin @Inject constructor(
172237
val durationStr: String = matcher.group(1)
173238
pluginContext.registerDurationAggregation(durationStr)
174239
}
175-
176-
ext.extensions.create(
177-
INTERNAL_EXTENSION_NAME,
178-
MetricsForDevelocityInternalExtension::class.java,
179-
{ timeSpec: String -> pluginContext.registerHourly(timeSpec) },
180-
{ dateStr: String -> pluginContext.registerDaily(dateStr) },
181-
{ durationStr: String -> pluginContext.registerDurationAggregation(durationStr) },
182-
)
183240
}
184241

185242
/*
186243
* Function to register an hourly gather task. This function must be re-entrant and allow for
187244
* the same task to be registered multiple times without error.
188245
*/
189246
private fun PluginContext.registerHourly(timeSpec: String): TaskProvider<GatherHourlyTask> {
190-
val taskName = "$TASK_PREFIX-$timeSpec"
247+
val taskName = NameUtil.dateTime(timeSpec)
191248
return if (project.tasks.names.contains(taskName)) {
192249
// task already exists. Return its TaskProvider.
193250
project.tasks.named(taskName, GatherHourlyTask::class.java)
@@ -227,7 +284,7 @@ internal class MetricsForDevelocityPlugin @Inject constructor(
227284
* the same task to be registered multiple times without error.
228285
*/
229286
private fun PluginContext.registerDaily(dateString: String): TaskProvider<GatherAggregateTask> {
230-
val taskName = "$TASK_PREFIX-$dateString"
287+
val taskName = NameUtil.dateTime(dateString)
231288
return if (project.tasks.names.contains(taskName)) {
232289
// task already exists. Return its TaskProvider.
233290
project.tasks.named(taskName, GatherAggregateTask::class.java)
@@ -281,7 +338,7 @@ internal class MetricsForDevelocityPlugin @Inject constructor(
281338
}
282339
}.toList()
283340

284-
val taskName = "$TASK_PREFIX-last-$durationStr"
341+
val taskName = NameUtil.duration(durationStr)
285342
return if (project.tasks.names.contains(taskName)) {
286343
// task already exists. Return its TaskProvider.
287344
project.tasks.named(taskName, GatherAggregateTask::class.java)
@@ -357,25 +414,6 @@ internal class MetricsForDevelocityPlugin @Inject constructor(
357414
}
358415

359416
companion object {
360-
const val EXTENSION_NAME = "metricsForDevelocity"
361-
const val INTERNAL_EXTENSION_NAME = "metricsForDevelocityInternal"
362-
const val TASK_PREFIX = "metricsForDevelocity"
363-
const val TASK_GROUP = "develocity metrics"
364-
const val QUERY_FILTER_PROPERTY = "metricsForDevelocityQueryFilter"
365-
366-
private val DATETIME_TASK_PATTERN = Regex(
367-
// Examples:
368-
// metricsForDevelocity-2024-06-01
369-
// metricsForDevelocity-2024-06-01T05
370-
"^\\Q$TASK_PREFIX-\\E((\\d{4}-\\d{2}-\\d{2})(?:T(\\d{2}))?)$"
371-
).toPattern()
372-
373-
private val DURATION_TASK_PATTERN = Regex(
374-
// Examples:
375-
// metricsForDevelocity-last-P7D
376-
// metricsForDevelocity-last-PT8H
377-
// metricsForDevelocity-last-P2DT8H
378-
"^\\Q$TASK_PREFIX-last-\\E(\\w+)$"
379-
).toPattern()
417+
internal const val TASK_GROUP = "develocity metrics"
380418
}
381419
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.ebay.plugins.metrics.develocity
2+
3+
/**
4+
* Utility functions for generating task names, to keep it all in one place.
5+
*/
6+
internal object NameUtil {
7+
const val TASK_PREFIX = "metricsForDevelocity"
8+
9+
val DATETIME_TASK_PATTERN = Regex(
10+
// Examples:
11+
// metricsForDevelocity-2024-06-01
12+
// metricsForDevelocity-2024-06-01T05
13+
"^\\Q$TASK_PREFIX-\\E((\\d{4}-\\d{2}-\\d{2})(?:T(\\d{2}))?)$"
14+
).toPattern()
15+
16+
val DURATION_TASK_PATTERN = Regex(
17+
// Examples:
18+
// metricsForDevelocity-last-P7D
19+
// metricsForDevelocity-last-PT8H
20+
// metricsForDevelocity-last-P2DT8H
21+
"^\\Q$TASK_PREFIX-last-\\E(\\w+)$"
22+
).toPattern()
23+
24+
fun dateTime(timeSpec: String): String = "$TASK_PREFIX-$timeSpec"
25+
26+
fun duration(durationStr: String): String = "$TASK_PREFIX-last-$durationStr"
27+
}

src/main/kotlin/com/ebay/plugins/metrics/develocity/PathUtil.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import org.gradle.api.provider.Provider
99
/**
1010
* Utility to help consolidate all path-related logic for the develocity metrics plugin.
1111
*/
12-
object PathUtil {
12+
internal object PathUtil {
1313
private fun summarizerFileName(summarizer: MetricSummarizer<*>) = summarizer.id
1414

1515
/**
@@ -26,8 +26,8 @@ object PathUtil {
2626
projectLayout: ProjectLayout,
2727
dateStr: String,
2828
): Provider<Directory> = dailyBaseDir(projectLayout).map { dailyBaseDir ->
29-
dailyBaseDir.dir(dateStr)
30-
}
29+
dailyBaseDir.dir(dateStr)
30+
}
3131

3232
/**
3333
* The base directory for all hourly outputs.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.ebay.plugins.metrics.develocity
2+
3+
import org.gradle.api.artifacts.transform.InputArtifact
4+
import org.gradle.api.artifacts.transform.TransformAction
5+
import org.gradle.api.artifacts.transform.TransformOutputs
6+
import org.gradle.api.file.FileSystemLocation
7+
import org.gradle.api.provider.Provider
8+
import org.gradle.api.tasks.PathSensitive
9+
import org.gradle.api.tasks.PathSensitivity
10+
import org.gradle.work.DisableCachingByDefault
11+
12+
/**
13+
* Artifact transform which extracts an individual summarizer's result from the output of the summary
14+
* computation task.
15+
*/
16+
@DisableCachingByDefault(because = "Copying files does not benefit from caching")
17+
abstract class SummarizerSelectTransform : TransformAction<SummarizerSelectTransformParameters> {
18+
@get:InputArtifact
19+
@get:PathSensitive(PathSensitivity.NONE)
20+
abstract val inputArtifact: Provider<FileSystemLocation>
21+
22+
override fun transform(outputs: TransformOutputs) {
23+
val input = inputArtifact.get().asFile
24+
require(input.isDirectory) {
25+
"Input artifact must be a directory, but was: $input"
26+
}
27+
outputs.file(input.resolve(parameters.summarizerId.get()))
28+
}
29+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.ebay.plugins.metrics.develocity
2+
3+
import org.gradle.api.artifacts.transform.TransformParameters
4+
import org.gradle.api.provider.Property
5+
import org.gradle.api.tasks.Input
6+
7+
/**
8+
* Parameters for the summarizer selection transform.
9+
*/
10+
interface SummarizerSelectTransformParameters : TransformParameters {
11+
/**
12+
* The [MetricSummarizer.id] of the summarizer data to extract.
13+
*/
14+
@get:Input
15+
val summarizerId: Property<String>
16+
}

0 commit comments

Comments
 (0)