From 3bb9601c12c699d47f36036c88bfc60a1faa20e4 Mon Sep 17 00:00:00 2001 From: takahirom Date: Mon, 18 Mar 2024 20:42:50 +0900 Subject: [PATCH 1/4] Add `reporters` extension function to `MeasureBuildsExtension` --- .../android/measure/BuildTimePlugin.kt | 26 ++- .../android/measure/MeasureBuildsExtension.kt | 11 +- .../lifecycle/BuildFinishedFlowAction.kt | 8 +- .../android/measure/models/BuildData.kt | 1 - .../measure/networking/MetricsReporter.kt | 194 ------------------ .../measure/providers/BuildDataProvider.kt | 2 - .../measure/repoters/LocalMetricsReporter.kt | 42 ++++ .../measure/repoters/MetricsDispatcher.kt | 38 ++++ .../repoters/SlowSlowTasksMetricsReporter.kt | 57 +++++ .../android/measure/BuildTimePluginTest.kt | 48 ----- 10 files changed, 162 insertions(+), 265 deletions(-) delete mode 100644 measure-builds/src/main/java/com/automattic/android/measure/networking/MetricsReporter.kt create mode 100644 measure-builds/src/main/java/com/automattic/android/measure/repoters/LocalMetricsReporter.kt create mode 100644 measure-builds/src/main/java/com/automattic/android/measure/repoters/MetricsDispatcher.kt create mode 100644 measure-builds/src/main/java/com/automattic/android/measure/repoters/SlowSlowTasksMetricsReporter.kt diff --git a/measure-builds/src/main/java/com/automattic/android/measure/BuildTimePlugin.kt b/measure-builds/src/main/java/com/automattic/android/measure/BuildTimePlugin.kt index adb337b..3fabcdb 100644 --- a/measure-builds/src/main/java/com/automattic/android/measure/BuildTimePlugin.kt +++ b/measure-builds/src/main/java/com/automattic/android/measure/BuildTimePlugin.kt @@ -3,9 +3,11 @@ package com.automattic.android.measure import com.automattic.android.measure.lifecycle.BuildFinishedFlowAction import com.automattic.android.measure.lifecycle.BuildTaskService import com.automattic.android.measure.lifecycle.ConfigurationPhaseObserver -import com.automattic.android.measure.networking.MetricsReporter import com.automattic.android.measure.providers.BuildDataProvider import com.automattic.android.measure.providers.UsernameProvider +import com.automattic.android.measure.repoters.LocalMetricsReporter +import com.automattic.android.measure.repoters.MetricsDispatcher +import com.automattic.android.measure.repoters.SlowSlowTasksMetricsReporter import com.gradle.scan.plugin.BuildScanExtension import kotlinx.coroutines.runBlocking import org.gradle.StartParameter @@ -33,8 +35,18 @@ class BuildTimePlugin @Inject constructor( (project.gradle as DefaultGradle).services[BuildStartedTime::class.java].startTime val extension = project.extensions.create("measureBuilds", MeasureBuildsExtension::class.java, project) + val reporters = extension.reporters.convention(listOf(LocalMetricsReporter, SlowSlowTasksMetricsReporter)) - val analyticsReporter = MetricsReporter(project.logger, extension.authToken, project.buildDir) + val metricsDispatcherProvider = project.gradle.sharedServices.registerIfAbsent( + "metricsDispatcher", + MetricsDispatcher::class.java + ) { + it.maxParallelUsages.set(1) + it.parameters.authToken.set(extension.authToken) + it.parameters.buildDir.set(project.layout.buildDirectory) + it.parameters.reporters.set(reporters) + } + registry.onTaskCompletion(metricsDispatcherProvider) val encodedUser: String = UsernameProvider.provide(project, extension) @@ -49,7 +61,6 @@ class BuildTimePlugin @Inject constructor( prepareBuildFinishedAction( project.gradle.startParameter, extension, - analyticsReporter, buildInitiatedTime, configurationProvider ) @@ -57,7 +68,7 @@ class BuildTimePlugin @Inject constructor( } prepareBuildTaskService(project) - prepareBuildScanListener(project, extension, analyticsReporter) + prepareBuildScanListener(project, extension, metricsDispatcherProvider) } private fun prepareBuildData( @@ -68,7 +79,6 @@ class BuildTimePlugin @Inject constructor( InMemoryReport.setBuildData( BuildDataProvider.provide( project, - extension.automatticProject.get(), encodedUser, ) ) @@ -77,13 +87,13 @@ class BuildTimePlugin @Inject constructor( private fun prepareBuildScanListener( project: Project, extension: MeasureBuildsExtension, - analyticsReporter: MetricsReporter, + analyticsReporter: Provider, ) { val buildScanExtension = project.extensions.findByType(BuildScanExtension::class.java) buildScanExtension?.buildScanPublished { runBlocking { if (extension.enable.orNull == true && extension.attachGradleScanId.get()) { - analyticsReporter.report(InMemoryReport, it.buildScanId) + analyticsReporter.get().report(InMemoryReport, it.buildScanId) } } } @@ -92,7 +102,6 @@ class BuildTimePlugin @Inject constructor( private fun prepareBuildFinishedAction( startParameter: StartParameter, extension: MeasureBuildsExtension, - analyticsReporter: MetricsReporter, buildInitiatedTime: Long, configurationPhaseObserver: Provider, ) { @@ -102,7 +111,6 @@ class BuildTimePlugin @Inject constructor( spec.parameters.apply { this.buildWorkResult.set(flowProviders.buildWorkResult) this.attachGradleScanId.set(extension.attachGradleScanId) - this.analyticsReporter.set(analyticsReporter) this.initiationTime.set(buildInitiatedTime) this.configurationPhaseExecuted.set(configurationPhaseObserver) this.startParameter.set(startParameter) diff --git a/measure-builds/src/main/java/com/automattic/android/measure/MeasureBuildsExtension.kt b/measure-builds/src/main/java/com/automattic/android/measure/MeasureBuildsExtension.kt index 056200e..0bfa1b9 100644 --- a/measure-builds/src/main/java/com/automattic/android/measure/MeasureBuildsExtension.kt +++ b/measure-builds/src/main/java/com/automattic/android/measure/MeasureBuildsExtension.kt @@ -1,19 +1,20 @@ package com.automattic.android.measure +import com.automattic.android.measure.repoters.MetricsReporter import org.gradle.api.Project +import org.gradle.api.provider.ListProperty import org.gradle.api.provider.Property abstract class MeasureBuildsExtension(project: Project) { private val objects = project.objects - val automatticProject: Property = - objects.property(AutomatticProject::class.java) - val enable: Property = objects.property(Boolean::class.java) val obfuscateUsername: Property = objects.property(Boolean::class.java) + val reporters: ListProperty = objects.listProperty(MetricsReporter::class.java) + /** * If `true`, then the metrics will be sent at build finish, * orchestrated by Gradle Enterprise plugin, attaching @@ -25,8 +26,4 @@ abstract class MeasureBuildsExtension(project: Project) { val attachGradleScanId: Property = objects.property(Boolean::class.java) val authToken: Property = objects.property(String::class.java) - - enum class AutomatticProject { - WooCommerce, WordPress, DayOne, PocketCasts, Tumblr, FluxC - } } diff --git a/measure-builds/src/main/java/com/automattic/android/measure/lifecycle/BuildFinishedFlowAction.kt b/measure-builds/src/main/java/com/automattic/android/measure/lifecycle/BuildFinishedFlowAction.kt index 6354c2b..ff4d12e 100644 --- a/measure-builds/src/main/java/com/automattic/android/measure/lifecycle/BuildFinishedFlowAction.kt +++ b/measure-builds/src/main/java/com/automattic/android/measure/lifecycle/BuildFinishedFlowAction.kt @@ -4,7 +4,7 @@ package com.automattic.android.measure.lifecycle import com.automattic.android.measure.InMemoryReport import com.automattic.android.measure.models.ExecutionData -import com.automattic.android.measure.networking.MetricsReporter +import com.automattic.android.measure.repoters.MetricsDispatcher import kotlinx.coroutines.runBlocking import org.gradle.StartParameter import org.gradle.api.flow.BuildWorkResult @@ -30,8 +30,8 @@ class BuildFinishedFlowAction : FlowAction { @get:Input val buildWorkResult: Property> - @get:Input - val analyticsReporter: Property + @get:ServiceReference("metricsDispatcher") + val metricsDispatcher: Property @get:Input val attachGradleScanId: Property @@ -71,7 +71,7 @@ class BuildFinishedFlowAction : FlowAction { if (parameters.attachGradleScanId.get() == false) { runBlocking { - parameters.analyticsReporter.get().report( + parameters.metricsDispatcher.get().report( report = InMemoryReport, gradleScanId = null ) diff --git a/measure-builds/src/main/java/com/automattic/android/measure/models/BuildData.kt b/measure-builds/src/main/java/com/automattic/android/measure/models/BuildData.kt index cb29a4e..eb9a010 100644 --- a/measure-builds/src/main/java/com/automattic/android/measure/models/BuildData.kt +++ b/measure-builds/src/main/java/com/automattic/android/measure/models/BuildData.kt @@ -12,7 +12,6 @@ import kotlinx.serialization.Serializable */ @Serializable data class BuildData( - val forProject: MeasureBuildsExtension.AutomatticProject, val user: String, val gradleVersion: String, val operatingSystem: String, diff --git a/measure-builds/src/main/java/com/automattic/android/measure/networking/MetricsReporter.kt b/measure-builds/src/main/java/com/automattic/android/measure/networking/MetricsReporter.kt deleted file mode 100644 index d479776..0000000 --- a/measure-builds/src/main/java/com/automattic/android/measure/networking/MetricsReporter.kt +++ /dev/null @@ -1,194 +0,0 @@ -package com.automattic.android.measure.networking - -import com.automattic.android.measure.InMemoryReport -import com.automattic.android.measure.logging.Emojis.FAILURE_ICON -import com.automattic.android.measure.logging.Emojis.SUCCESS_ICON -import com.automattic.android.measure.logging.Emojis.TURTLE_ICON -import com.automattic.android.measure.logging.Emojis.WAITING_ICON -import com.automattic.android.measure.models.MeasuredTask -import io.ktor.client.HttpClient -import io.ktor.client.engine.cio.CIO -import io.ktor.client.features.HttpTimeout -import io.ktor.client.features.json.JsonFeature -import io.ktor.client.features.json.serializer.KotlinxSerializer -import io.ktor.client.features.logging.LogLevel -import io.ktor.client.features.logging.Logging -import io.ktor.client.request.headers -import io.ktor.client.request.post -import io.ktor.client.statement.HttpResponse -import io.ktor.client.statement.HttpStatement -import io.ktor.http.ContentType -import io.ktor.http.HttpHeaders -import io.ktor.http.HttpHeaders.Authorization -import io.ktor.http.HttpStatusCode -import io.ktor.http.contentType -import kotlinx.serialization.encodeToString -import kotlinx.serialization.json.Json -import org.gradle.api.logging.Logger -import org.gradle.api.provider.Provider -import java.io.File -import java.util.Locale -import java.util.concurrent.TimeUnit.MILLISECONDS -import java.util.concurrent.TimeUnit.MINUTES -import kotlin.io.path.Path -import kotlin.io.path.absolutePathString -import kotlin.io.path.createDirectories -import kotlin.io.path.createFile -import kotlin.io.path.exists -import kotlin.io.path.writeText -import kotlin.time.Duration.Companion.seconds - -class MetricsReporter( - private val logger: Logger, - private val authToken: Provider, - private val buildDir: File, -) { - suspend fun report( - report: InMemoryReport, - gradleScanId: String? - ) { - reportLocally(report) - - val payload = report.toAppsMetricsPayload(gradleScanId) - @Suppress("TooGenericExceptionCaught") - try { - logSlowTasks(report) - if (!authToken.isPresent) { - logger.lifecycle("\nNo authToken provided. Skipping reporting.") - return - } - logger.lifecycle("\n$WAITING_ICON Reporting build data to Apps Metrics...") - - val client = httpClient() - - client.post("https://metrics.a8c-ci.services/api/grouped-metrics") { - headers { - append(HttpHeaders.UserAgent, "Gradle") - append(Authorization, "Bearer ${authToken.get()}") - } - contentType(ContentType.Application.Json) - body = payload - }.execute { response: HttpResponse -> - logger.debug(response.toString()) - - when (response.status) { - HttpStatusCode.Created -> { - val buildTime = report.executionData.buildTime - val timeFormatted = String.format( - Locale.US, - "%dm %ds", - MILLISECONDS.toMinutes(buildTime), - MILLISECONDS.toSeconds(buildTime) - MINUTES.toSeconds( - MILLISECONDS.toMinutes( - buildTime - ) - ) - ) - logger.lifecycle( - "\n$SUCCESS_ICON Build time report of $timeFormatted has been received by Apps Metrics." - ) - } - - else -> { - logger.warn( - "\n$FAILURE_ICON Build time has not been uploaded. Add `debug` property to see more logs." - ) - } - } - } - client.close() - } catch (exception: Exception) { - logger.warn( - "\n$FAILURE_ICON Build time has not been uploaded. Add `debug` property to see more logs." - ) - logger.debug(exception.stackTraceToString()) - } - } - - private fun reportLocally(report: InMemoryReport) { - Path("${buildDir.path}/reports/measure_builds") - .apply { - if (!exists()) { - createDirectories() - } - resolve("build_data.json").apply { - logger.info("Writing build data to ${absolutePathString()}") - if (!exists()) { - createFile() - } - writeText(Json.encodeToString(report.buildData)) - } - resolve("execution_data.json").apply { - logger.info("Writing execution data to ${absolutePathString()}") - if (!exists()) { - createFile() - } - writeText(Json.encodeToString(report.executionData)) - } - } - } - - private fun httpClient(): HttpClient { - val client = HttpClient(CIO) { - install(Logging) { - this.logger = object : io.ktor.client.features.logging.Logger { - override fun log(message: String) { - this@MetricsReporter.logger.debug(message) - } - } - level = LogLevel.ALL - } - install(JsonFeature) { - serializer = KotlinxSerializer() - } - install(HttpTimeout) { - requestTimeoutMillis = 5.seconds.inWholeMilliseconds - } - } - return client - } - - private fun logSlowTasks(report: InMemoryReport) { - if (report.executionData.buildTime == 0L) { - return - } - val slowTasks = - report.executionData.executedTasks.sortedByDescending { it.duration } - .chunked(atMostLoggedTasks).firstOrNull() - ?: return - - logger.lifecycle("\n$TURTLE_ICON ${slowTasks.size} slowest tasks were: ") - - logger.lifecycle( - String.format( - Locale.US, - "%-15s %-15s %s", - "Duration", - "% of build", - "Task" - ) - ) - slowTasks.forEach { - @Suppress("MagicNumber") - logger.lifecycle( - "%-15s %-15s %s".format( - Locale.US, - readableDuration(it), - "${(it.duration.inWholeMilliseconds * 100 / report.executionData.buildTime).toInt()}%", - it.name, - ) - ) - } - } - - private fun readableDuration(it: MeasuredTask) = - if (it.duration < 1.seconds) { - "${it.duration.inWholeMilliseconds}ms" - } else { - "${it.duration.inWholeSeconds}s" - } - - companion object { - private const val atMostLoggedTasks = 5 - } -} diff --git a/measure-builds/src/main/java/com/automattic/android/measure/providers/BuildDataProvider.kt b/measure-builds/src/main/java/com/automattic/android/measure/providers/BuildDataProvider.kt index 311a458..f826e57 100644 --- a/measure-builds/src/main/java/com/automattic/android/measure/providers/BuildDataProvider.kt +++ b/measure-builds/src/main/java/com/automattic/android/measure/providers/BuildDataProvider.kt @@ -10,7 +10,6 @@ object BuildDataProvider { fun provide( project: Project, - automatticProject: MeasureBuildsExtension.AutomatticProject, username: String, ): BuildData { val gradle = project.gradle @@ -18,7 +17,6 @@ object BuildDataProvider { @Suppress("UnstableApiUsage") return BuildData( - forProject = automatticProject, environment = gradle.environment(), gradleVersion = gradle.gradleVersion, operatingSystem = System.getProperty("os.name").lowercase(), diff --git a/measure-builds/src/main/java/com/automattic/android/measure/repoters/LocalMetricsReporter.kt b/measure-builds/src/main/java/com/automattic/android/measure/repoters/LocalMetricsReporter.kt new file mode 100644 index 0000000..e56229b --- /dev/null +++ b/measure-builds/src/main/java/com/automattic/android/measure/repoters/LocalMetricsReporter.kt @@ -0,0 +1,42 @@ +package com.automattic.android.measure.repoters + +import com.automattic.android.measure.InMemoryReport +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import org.gradle.api.logging.Logging +import kotlin.io.path.Path +import kotlin.io.path.absolutePathString +import kotlin.io.path.createDirectories +import kotlin.io.path.createFile +import kotlin.io.path.exists +import kotlin.io.path.writeText + +object LocalMetricsReporter : MetricsReporter { + private val logger = Logging.getLogger(LocalMetricsReporter::class.java) + override suspend fun report( + report: InMemoryReport, + gradleScanId: String?, + parameters: MetricsDispatcher.Parameters + ) { + Path("${parameters.buildDir.get().asFile.get().path}/reports/measure_builds") + .apply { + if (!exists()) { + createDirectories() + } + resolve("build_data.json").apply { + logger.info("Writing build data to ${absolutePathString()}") + if (!exists()) { + createFile() + } + writeText(Json.encodeToString(report.buildData)) + } + resolve("execution_data.json").apply { + logger.info("Writing execution data to ${absolutePathString()}") + if (!exists()) { + createFile() + } + writeText(Json.encodeToString(report.executionData)) + } + } + } +} \ No newline at end of file diff --git a/measure-builds/src/main/java/com/automattic/android/measure/repoters/MetricsDispatcher.kt b/measure-builds/src/main/java/com/automattic/android/measure/repoters/MetricsDispatcher.kt new file mode 100644 index 0000000..77e8100 --- /dev/null +++ b/measure-builds/src/main/java/com/automattic/android/measure/repoters/MetricsDispatcher.kt @@ -0,0 +1,38 @@ +package com.automattic.android.measure.repoters + +import com.automattic.android.measure.InMemoryReport +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.Property +import org.gradle.api.services.BuildService +import org.gradle.api.services.BuildServiceParameters +import org.gradle.tooling.events.FinishEvent +import org.gradle.tooling.events.OperationCompletionListener + +fun interface MetricsReporter: java.io.Serializable { + suspend fun report(report: InMemoryReport, gradleScanId: String?, parameters: MetricsDispatcher.Parameters) +} + +abstract class MetricsDispatcher : BuildService, OperationCompletionListener { + interface Parameters : BuildServiceParameters { + val authToken: Property + val buildDir: Property + val reporters: ListProperty + } + + suspend fun report( + report: InMemoryReport, + gradleScanId: String? + ) { + parameters.reporters.get().forEach { it.report(report, gradleScanId, parameters) } + } + + + override fun onFinish(event: FinishEvent?) { + // Do nothing + + // Without OperationCompletionListener, this service would not be created. + // https://docs.gradle.org/current/userguide/build_services.html#registering_a_build_service_and_connecting_it_to_tasks + // This happens on demand when a task first uses the service. If no task uses the service during a build, the service instance will not be created. + } +} diff --git a/measure-builds/src/main/java/com/automattic/android/measure/repoters/SlowSlowTasksMetricsReporter.kt b/measure-builds/src/main/java/com/automattic/android/measure/repoters/SlowSlowTasksMetricsReporter.kt new file mode 100644 index 0000000..08aa4e1 --- /dev/null +++ b/measure-builds/src/main/java/com/automattic/android/measure/repoters/SlowSlowTasksMetricsReporter.kt @@ -0,0 +1,57 @@ +package com.automattic.android.measure.repoters + +import com.automattic.android.measure.InMemoryReport +import com.automattic.android.measure.logging.Emojis +import com.automattic.android.measure.models.MeasuredTask +import org.gradle.api.logging.Logging +import java.util.Locale +import kotlin.time.Duration.Companion.seconds + +object SlowSlowTasksMetricsReporter : MetricsReporter { + private val logger = Logging.getLogger(SlowSlowTasksMetricsReporter::class.java) + override suspend fun report( + report: InMemoryReport, + gradleScanId: String?, + parameters: MetricsDispatcher.Parameters + ) { + if (report.executionData.buildTime == 0L) { + return + } + val slowTasks = + report.executionData.executedTasks.sortedByDescending { it.duration } + .chunked(atMostLoggedTasks).firstOrNull() + ?: return + + logger.lifecycle("\n${Emojis.TURTLE_ICON} ${slowTasks.size} slowest tasks were: ") + + logger.lifecycle( + String.format( + Locale.US, + "%-15s %-15s %s", + "Duration", + "% of build", + "Task" + ) + ) + slowTasks.forEach { + @Suppress("MagicNumber") + logger.lifecycle( + "%-15s %-15s %s".format( + Locale.US, + readableDuration(it), + "${(it.duration.inWholeMilliseconds * 100 / report.executionData.buildTime).toInt()}%", + it.name, + ) + ) + } + } + + private fun readableDuration(it: MeasuredTask) = + if (it.duration < 1.seconds) { + "${it.duration.inWholeMilliseconds}ms" + } else { + "${it.duration.inWholeSeconds}s" + } + + private const val atMostLoggedTasks = 5 +} \ No newline at end of file diff --git a/measure-builds/src/test/java/com/automattic/android/measure/BuildTimePluginTest.kt b/measure-builds/src/test/java/com/automattic/android/measure/BuildTimePluginTest.kt index 397e881..f314bd1 100644 --- a/measure-builds/src/test/java/com/automattic/android/measure/BuildTimePluginTest.kt +++ b/measure-builds/src/test/java/com/automattic/android/measure/BuildTimePluginTest.kt @@ -11,37 +11,6 @@ import java.io.File @Suppress("MaximumLineLength", "MaxLineLength") class BuildTimePluginTest { - - @Test - fun `given a project that attaches gradle scan id, when executing a task with configuration from cache, then send the report with attached gradle scan id`() { - // given - val runner = functionalTestRunner( - enable = true, - attachGradleScanId = true, - projectWithSendingScans = true - ) - - // when - val prepareConfigurationCache = - runner.withArguments("--configuration-cache", "help").build() - - // then - assertThat( - prepareConfigurationCache.output - ).contains("Calculating task graph as no configuration cache is available for tasks") - .contains("Configuration cache entry stored") - - // when - val buildUsingConfigurationCache = - runner.withArguments("--configuration-cache", "help", "--debug").build() - - // then - assertThat(buildUsingConfigurationCache.output).contains("Reusing configuration cache") - .contains("Reporting build data to Apps Metrics...") - .contains("{\"name\":\"woocommerce-gradle-scan-id\",\"value\":") - .doesNotContain("{\"name\":\"woocommerce-gradle-scan-id\",\"value\":\"null\"}") - } - @Test fun `given a project that disabled build measurements and does not attach Gradle Scan Id, when executing a task, then build metrics are not sent`() { // given @@ -102,23 +71,6 @@ class BuildTimePluginTest { assertThat(run.output).doesNotContain("Reporting build data to Apps Metrics...") } - @Test - fun `given a project without apps metrics token, when executing a task, then build does not fail`() { - // given - val runner = functionalTestRunner( - enable = true, - attachGradleScanId = false, - applyAppsMetricsToken = false, - ) - - // when - val run = runner.withArguments("help").build() - - // then - assertThat(run.output).contains("No authToken provided. Skipping reporting.") - .contains("BUILD SUCCESSFUL") - } - @Test fun `given a help task to execute, when finishing the build, the help task is present in report file`() { // given From f126cfa64098ddae0f790a125cd6cddf5be32270 Mon Sep 17 00:00:00 2001 From: takahirom Date: Tue, 19 Mar 2024 21:37:38 +0900 Subject: [PATCH 2/4] Use Property for report callback --- .../android/measure/BuildTimePlugin.kt | 36 ++--- .../android/measure/MeasureBuildsExtension.kt | 13 +- .../lifecycle/BuildFinishedFlowAction.kt | 16 +-- .../measure/networking/ToAppsInfraPayload.kt | 3 +- .../reporters/InMemoryMetricsReporter.kt | 23 ++++ .../InternalWooCommerceA8cCiReporter.kt | 125 ++++++++++++++++++ .../LocalMetricsReporter.kt | 15 +-- .../measure/reporters/MetricsReport.kt | 8 ++ .../SlowSlowTasksMetricsReporter.kt | 11 +- .../measure/repoters/MetricsDispatcher.kt | 38 ------ .../android/measure/BuildTimePluginTest.kt | 63 ++++++++- 11 files changed, 256 insertions(+), 95 deletions(-) create mode 100644 measure-builds/src/main/java/com/automattic/android/measure/reporters/InMemoryMetricsReporter.kt create mode 100644 measure-builds/src/main/java/com/automattic/android/measure/reporters/InternalWooCommerceA8cCiReporter.kt rename measure-builds/src/main/java/com/automattic/android/measure/{repoters => reporters}/LocalMetricsReporter.kt (75%) create mode 100644 measure-builds/src/main/java/com/automattic/android/measure/reporters/MetricsReport.kt rename measure-builds/src/main/java/com/automattic/android/measure/{repoters => reporters}/SlowSlowTasksMetricsReporter.kt (86%) delete mode 100644 measure-builds/src/main/java/com/automattic/android/measure/repoters/MetricsDispatcher.kt diff --git a/measure-builds/src/main/java/com/automattic/android/measure/BuildTimePlugin.kt b/measure-builds/src/main/java/com/automattic/android/measure/BuildTimePlugin.kt index 3fabcdb..41049dd 100644 --- a/measure-builds/src/main/java/com/automattic/android/measure/BuildTimePlugin.kt +++ b/measure-builds/src/main/java/com/automattic/android/measure/BuildTimePlugin.kt @@ -5,9 +5,7 @@ import com.automattic.android.measure.lifecycle.BuildTaskService import com.automattic.android.measure.lifecycle.ConfigurationPhaseObserver import com.automattic.android.measure.providers.BuildDataProvider import com.automattic.android.measure.providers.UsernameProvider -import com.automattic.android.measure.repoters.LocalMetricsReporter -import com.automattic.android.measure.repoters.MetricsDispatcher -import com.automattic.android.measure.repoters.SlowSlowTasksMetricsReporter +import com.automattic.android.measure.reporters.InMemoryMetricsReporter import com.gradle.scan.plugin.BuildScanExtension import kotlinx.coroutines.runBlocking import org.gradle.StartParameter @@ -35,30 +33,21 @@ class BuildTimePlugin @Inject constructor( (project.gradle as DefaultGradle).services[BuildStartedTime::class.java].startTime val extension = project.extensions.create("measureBuilds", MeasureBuildsExtension::class.java, project) - val reporters = extension.reporters.convention(listOf(LocalMetricsReporter, SlowSlowTasksMetricsReporter)) - val metricsDispatcherProvider = project.gradle.sharedServices.registerIfAbsent( - "metricsDispatcher", - MetricsDispatcher::class.java - ) { - it.maxParallelUsages.set(1) - it.parameters.authToken.set(extension.authToken) - it.parameters.buildDir.set(project.layout.buildDirectory) - it.parameters.reporters.set(reporters) - } - registry.onTaskCompletion(metricsDispatcherProvider) + val metricsDispatcher = InMemoryMetricsReporter + metricsDispatcher.reportProperty = extension.reporterProperty val encodedUser: String = UsernameProvider.provide(project, extension) project.afterEvaluate { - if (extension.enable.orNull == true) { + if (extension.enable.convention(false).get() == true) { val configurationProvider: Provider = project.providers.of( ConfigurationPhaseObserver::class.java ) { } ConfigurationPhaseObserver.init() - prepareBuildData(project, extension, encodedUser) - prepareBuildFinishedAction( + prepareBuildData(project, encodedUser) + prepareBuildFinishedAction( project.gradle.startParameter, extension, buildInitiatedTime, @@ -68,12 +57,11 @@ class BuildTimePlugin @Inject constructor( } prepareBuildTaskService(project) - prepareBuildScanListener(project, extension, metricsDispatcherProvider) + prepareBuildScanListener(project, extension, metricsDispatcher) } private fun prepareBuildData( project: Project, - extension: MeasureBuildsExtension, encodedUser: String, ) { InMemoryReport.setBuildData( @@ -87,13 +75,15 @@ class BuildTimePlugin @Inject constructor( private fun prepareBuildScanListener( project: Project, extension: MeasureBuildsExtension, - analyticsReporter: Provider, + analyticsReporter: InMemoryMetricsReporter, ) { val buildScanExtension = project.extensions.findByType(BuildScanExtension::class.java) - buildScanExtension?.buildScanPublished { + val extensionEnable = extension.enable + val attachGradleScanId = extension.attachGradleScanId + buildScanExtension?.buildScanPublished { runBlocking { - if (extension.enable.orNull == true && extension.attachGradleScanId.get()) { - analyticsReporter.get().report(InMemoryReport, it.buildScanId) + if (extensionEnable.get() == true && attachGradleScanId.get()) { + analyticsReporter.report(InMemoryReport, it.buildScanId) } } } diff --git a/measure-builds/src/main/java/com/automattic/android/measure/MeasureBuildsExtension.kt b/measure-builds/src/main/java/com/automattic/android/measure/MeasureBuildsExtension.kt index 0bfa1b9..094a3c5 100644 --- a/measure-builds/src/main/java/com/automattic/android/measure/MeasureBuildsExtension.kt +++ b/measure-builds/src/main/java/com/automattic/android/measure/MeasureBuildsExtension.kt @@ -1,8 +1,8 @@ package com.automattic.android.measure -import com.automattic.android.measure.repoters.MetricsReporter +import com.automattic.android.measure.reporters.MetricsReport +import org.gradle.api.Action import org.gradle.api.Project -import org.gradle.api.provider.ListProperty import org.gradle.api.provider.Property abstract class MeasureBuildsExtension(project: Project) { @@ -13,7 +13,12 @@ abstract class MeasureBuildsExtension(project: Project) { val obfuscateUsername: Property = objects.property(Boolean::class.java) - val reporters: ListProperty = objects.listProperty(MetricsReporter::class.java) + @Suppress("UNCHECKED_CAST") + val reporterProperty: Property> = objects.property(Action::class.java) as Property> + + fun buildMetricsReported(action: Action) { + reporterProperty.set(action) + } /** * If `true`, then the metrics will be sent at build finish, @@ -24,6 +29,4 @@ abstract class MeasureBuildsExtension(project: Project) { * @see io.github.wzieba.tracks.plugin.analytics.BuildFinishedFlowAction */ val attachGradleScanId: Property = objects.property(Boolean::class.java) - - val authToken: Property = objects.property(String::class.java) } diff --git a/measure-builds/src/main/java/com/automattic/android/measure/lifecycle/BuildFinishedFlowAction.kt b/measure-builds/src/main/java/com/automattic/android/measure/lifecycle/BuildFinishedFlowAction.kt index ff4d12e..d9ef6eb 100644 --- a/measure-builds/src/main/java/com/automattic/android/measure/lifecycle/BuildFinishedFlowAction.kt +++ b/measure-builds/src/main/java/com/automattic/android/measure/lifecycle/BuildFinishedFlowAction.kt @@ -4,8 +4,7 @@ package com.automattic.android.measure.lifecycle import com.automattic.android.measure.InMemoryReport import com.automattic.android.measure.models.ExecutionData -import com.automattic.android.measure.repoters.MetricsDispatcher -import kotlinx.coroutines.runBlocking +import com.automattic.android.measure.reporters.InMemoryMetricsReporter import org.gradle.StartParameter import org.gradle.api.flow.BuildWorkResult import org.gradle.api.flow.FlowAction @@ -30,9 +29,6 @@ class BuildFinishedFlowAction : FlowAction { @get:Input val buildWorkResult: Property> - @get:ServiceReference("metricsDispatcher") - val metricsDispatcher: Property - @get:Input val attachGradleScanId: Property @@ -70,12 +66,10 @@ class BuildFinishedFlowAction : FlowAction { InMemoryReport.setExecutionData(executionData) if (parameters.attachGradleScanId.get() == false) { - runBlocking { - parameters.metricsDispatcher.get().report( - report = InMemoryReport, - gradleScanId = null - ) - } + InMemoryMetricsReporter.report( + report = InMemoryReport, + gradleScanId = null + ) } } } diff --git a/measure-builds/src/main/java/com/automattic/android/measure/networking/ToAppsInfraPayload.kt b/measure-builds/src/main/java/com/automattic/android/measure/networking/ToAppsInfraPayload.kt index d88b479..4f414c6 100644 --- a/measure-builds/src/main/java/com/automattic/android/measure/networking/ToAppsInfraPayload.kt +++ b/measure-builds/src/main/java/com/automattic/android/measure/networking/ToAppsInfraPayload.kt @@ -5,8 +5,7 @@ import com.automattic.android.measure.models.MeasuredTask.State.EXECUTED import com.automattic.android.measure.models.MeasuredTask.State.IS_FROM_CACHE import com.automattic.android.measure.models.MeasuredTask.State.UP_TO_DATE -fun InMemoryReport.toAppsMetricsPayload(gradleScanId: String?): GroupedAppsMetrics { - val projectKey = buildData.forProject.name.lowercase() +fun InMemoryReport.toAppsMetricsPayload(projectKey:String, gradleScanId: String?): GroupedAppsMetrics { val meta = mapOf( "user" to buildData.user, diff --git a/measure-builds/src/main/java/com/automattic/android/measure/reporters/InMemoryMetricsReporter.kt b/measure-builds/src/main/java/com/automattic/android/measure/reporters/InMemoryMetricsReporter.kt new file mode 100644 index 0000000..ab33f93 --- /dev/null +++ b/measure-builds/src/main/java/com/automattic/android/measure/reporters/InMemoryMetricsReporter.kt @@ -0,0 +1,23 @@ +package com.automattic.android.measure.reporters + +import com.automattic.android.measure.InMemoryReport +import org.gradle.api.Action +import org.gradle.api.provider.Property + +object InMemoryMetricsReporter { + + lateinit var reportProperty: Property> + + fun report( + report: InMemoryReport, + gradleScanId: String? + ) { + val result = object : MetricsReport { + override val report: InMemoryReport + get() = report + override val gradleScanId: String? + get() = gradleScanId + } + reportProperty.get().execute(result) + } +} diff --git a/measure-builds/src/main/java/com/automattic/android/measure/reporters/InternalWooCommerceA8cCiReporter.kt b/measure-builds/src/main/java/com/automattic/android/measure/reporters/InternalWooCommerceA8cCiReporter.kt new file mode 100644 index 0000000..6f766a5 --- /dev/null +++ b/measure-builds/src/main/java/com/automattic/android/measure/reporters/InternalWooCommerceA8cCiReporter.kt @@ -0,0 +1,125 @@ +package com.automattic.android.measure.reporters + +import com.automattic.android.measure.logging.Emojis.FAILURE_ICON +import com.automattic.android.measure.logging.Emojis.SUCCESS_ICON +import com.automattic.android.measure.logging.Emojis.WAITING_ICON +import com.automattic.android.measure.networking.toAppsMetricsPayload +import io.ktor.client.HttpClient +import io.ktor.client.engine.cio.CIO +import io.ktor.client.features.HttpTimeout +import io.ktor.client.features.json.JsonFeature +import io.ktor.client.features.json.serializer.KotlinxSerializer +import io.ktor.client.features.logging.LogLevel +import io.ktor.client.features.logging.Logging +import io.ktor.client.request.headers +import io.ktor.client.request.post +import io.ktor.client.statement.HttpResponse +import io.ktor.client.statement.HttpStatement +import io.ktor.http.ContentType +import io.ktor.http.HttpHeaders +import io.ktor.http.HttpHeaders.Authorization +import io.ktor.http.HttpStatusCode +import io.ktor.http.contentType +import kotlinx.coroutines.runBlocking +import java.io.File +import java.util.Locale +import java.util.concurrent.TimeUnit.MILLISECONDS +import java.util.concurrent.TimeUnit.MINUTES +import kotlin.time.Duration.Companion.seconds + + +object InternalA8cCiReporter { + private val logger = + org.gradle.api.logging.Logging.getLogger(InternalA8cCiReporter::class.java) + + fun reportBlocking( + metricsReport: MetricsReport, + projectName: String, + authToken: String?, + ) { + runBlocking { + report(metricsReport, projectName, authToken) + } + } + + suspend fun report( + metricsReport: MetricsReport, + projectName: String, + authToken: String?, + ) { + val report = metricsReport.report + val payload = report.toAppsMetricsPayload(projectName, metricsReport.gradleScanId) + @Suppress("TooGenericExceptionCaught") + try { + if (authToken.isNullOrBlank()) { + logger.lifecycle("\nNo authToken provided. Skipping reporting.") + return + } + logger.lifecycle("\n$WAITING_ICON Reporting build data to Apps Metrics...") + + val client = httpClient() + + client.post("https://metrics.a8c-ci.services/api/grouped-metrics") { + headers { + append(HttpHeaders.UserAgent, "Gradle") + append(Authorization, "Bearer $authToken") + } + contentType(ContentType.Application.Json) + body = payload + }.execute { response: HttpResponse -> + logger.debug(response.toString()) + + when (response.status) { + HttpStatusCode.Created -> { + val buildTime = report.executionData.buildTime + val timeFormatted = String.format( + Locale.US, + "%dm %ds", + MILLISECONDS.toMinutes(buildTime), + MILLISECONDS.toSeconds(buildTime) - MINUTES.toSeconds( + MILLISECONDS.toMinutes( + buildTime + ) + ) + ) + logger.lifecycle( + "\n$SUCCESS_ICON Build time report of $timeFormatted has been received by Apps Metrics." + ) + } + + else -> { + logger.warn( + "\n$FAILURE_ICON Build time has not been uploaded. Add `debug` property to see more logs." + ) + } + } + } + client.close() + } catch (exception: Exception) { + logger.warn( + "\n$FAILURE_ICON Build time has not been uploaded. Add `debug` property to see more logs." + ) + logger.debug(exception.stackTraceToString()) + } + } + + private fun httpClient(): HttpClient { + val client = HttpClient(CIO) { + install(Logging) { + this.logger = object : io.ktor.client.features.logging.Logger { + override fun log(message: String) { + this@InternalA8cCiReporter.logger.debug(message) + } + } + level = LogLevel.ALL + } + install(JsonFeature) { + serializer = KotlinxSerializer() + } + install(HttpTimeout) { + requestTimeoutMillis = 5.seconds.inWholeMilliseconds + } + } + return client + } +} \ No newline at end of file diff --git a/measure-builds/src/main/java/com/automattic/android/measure/repoters/LocalMetricsReporter.kt b/measure-builds/src/main/java/com/automattic/android/measure/reporters/LocalMetricsReporter.kt similarity index 75% rename from measure-builds/src/main/java/com/automattic/android/measure/repoters/LocalMetricsReporter.kt rename to measure-builds/src/main/java/com/automattic/android/measure/reporters/LocalMetricsReporter.kt index e56229b..a6cf76d 100644 --- a/measure-builds/src/main/java/com/automattic/android/measure/repoters/LocalMetricsReporter.kt +++ b/measure-builds/src/main/java/com/automattic/android/measure/reporters/LocalMetricsReporter.kt @@ -1,6 +1,5 @@ -package com.automattic.android.measure.repoters +package com.automattic.android.measure.reporters -import com.automattic.android.measure.InMemoryReport import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import org.gradle.api.logging.Logging @@ -11,14 +10,14 @@ import kotlin.io.path.createFile import kotlin.io.path.exists import kotlin.io.path.writeText -object LocalMetricsReporter : MetricsReporter { +object LocalMetricsReporter { private val logger = Logging.getLogger(LocalMetricsReporter::class.java) - override suspend fun report( - report: InMemoryReport, - gradleScanId: String?, - parameters: MetricsDispatcher.Parameters + fun report( + metricsReport: MetricsReport, + buildDirPath: String, ) { - Path("${parameters.buildDir.get().asFile.get().path}/reports/measure_builds") + val report = metricsReport.report + Path("${buildDirPath}/reports/measure_builds") .apply { if (!exists()) { createDirectories() diff --git a/measure-builds/src/main/java/com/automattic/android/measure/reporters/MetricsReport.kt b/measure-builds/src/main/java/com/automattic/android/measure/reporters/MetricsReport.kt new file mode 100644 index 0000000..f435b4f --- /dev/null +++ b/measure-builds/src/main/java/com/automattic/android/measure/reporters/MetricsReport.kt @@ -0,0 +1,8 @@ +package com.automattic.android.measure.reporters + +import com.automattic.android.measure.InMemoryReport + +interface MetricsReport { + val report: InMemoryReport + val gradleScanId: String? +} diff --git a/measure-builds/src/main/java/com/automattic/android/measure/repoters/SlowSlowTasksMetricsReporter.kt b/measure-builds/src/main/java/com/automattic/android/measure/reporters/SlowSlowTasksMetricsReporter.kt similarity index 86% rename from measure-builds/src/main/java/com/automattic/android/measure/repoters/SlowSlowTasksMetricsReporter.kt rename to measure-builds/src/main/java/com/automattic/android/measure/reporters/SlowSlowTasksMetricsReporter.kt index 08aa4e1..4ea6e9d 100644 --- a/measure-builds/src/main/java/com/automattic/android/measure/repoters/SlowSlowTasksMetricsReporter.kt +++ b/measure-builds/src/main/java/com/automattic/android/measure/reporters/SlowSlowTasksMetricsReporter.kt @@ -1,4 +1,4 @@ -package com.automattic.android.measure.repoters +package com.automattic.android.measure.reporters import com.automattic.android.measure.InMemoryReport import com.automattic.android.measure.logging.Emojis @@ -7,13 +7,12 @@ import org.gradle.api.logging.Logging import java.util.Locale import kotlin.time.Duration.Companion.seconds -object SlowSlowTasksMetricsReporter : MetricsReporter { +object SlowSlowTasksMetricsReporter { private val logger = Logging.getLogger(SlowSlowTasksMetricsReporter::class.java) - override suspend fun report( - report: InMemoryReport, - gradleScanId: String?, - parameters: MetricsDispatcher.Parameters + fun report( + metricsReport: MetricsReport ) { + val report = metricsReport.report if (report.executionData.buildTime == 0L) { return } diff --git a/measure-builds/src/main/java/com/automattic/android/measure/repoters/MetricsDispatcher.kt b/measure-builds/src/main/java/com/automattic/android/measure/repoters/MetricsDispatcher.kt deleted file mode 100644 index 77e8100..0000000 --- a/measure-builds/src/main/java/com/automattic/android/measure/repoters/MetricsDispatcher.kt +++ /dev/null @@ -1,38 +0,0 @@ -package com.automattic.android.measure.repoters - -import com.automattic.android.measure.InMemoryReport -import org.gradle.api.file.DirectoryProperty -import org.gradle.api.provider.ListProperty -import org.gradle.api.provider.Property -import org.gradle.api.services.BuildService -import org.gradle.api.services.BuildServiceParameters -import org.gradle.tooling.events.FinishEvent -import org.gradle.tooling.events.OperationCompletionListener - -fun interface MetricsReporter: java.io.Serializable { - suspend fun report(report: InMemoryReport, gradleScanId: String?, parameters: MetricsDispatcher.Parameters) -} - -abstract class MetricsDispatcher : BuildService, OperationCompletionListener { - interface Parameters : BuildServiceParameters { - val authToken: Property - val buildDir: Property - val reporters: ListProperty - } - - suspend fun report( - report: InMemoryReport, - gradleScanId: String? - ) { - parameters.reporters.get().forEach { it.report(report, gradleScanId, parameters) } - } - - - override fun onFinish(event: FinishEvent?) { - // Do nothing - - // Without OperationCompletionListener, this service would not be created. - // https://docs.gradle.org/current/userguide/build_services.html#registering_a_build_service_and_connecting_it_to_tasks - // This happens on demand when a task first uses the service. If no task uses the service during a build, the service instance will not be created. - } -} diff --git a/measure-builds/src/test/java/com/automattic/android/measure/BuildTimePluginTest.kt b/measure-builds/src/test/java/com/automattic/android/measure/BuildTimePluginTest.kt index f314bd1..863ee40 100644 --- a/measure-builds/src/test/java/com/automattic/android/measure/BuildTimePluginTest.kt +++ b/measure-builds/src/test/java/com/automattic/android/measure/BuildTimePluginTest.kt @@ -11,6 +11,37 @@ import java.io.File @Suppress("MaximumLineLength", "MaxLineLength") class BuildTimePluginTest { + + @Test + fun `given a project that attaches gradle scan id, when executing a task with configuration from cache, then send the report with attached gradle scan id`() { + // given + val runner = functionalTestRunner( + enable = true, + attachGradleScanId = true, + projectWithSendingScans = true + ) + + // when + val prepareConfigurationCache = + runner.withArguments("--configuration-cache", "help").build() + + // then + assertThat( + prepareConfigurationCache.output + ).contains("Calculating task graph as no configuration cache is available for tasks") + .contains("Configuration cache entry stored") + + // when + val buildUsingConfigurationCache = + runner.withArguments("--configuration-cache", "help", "--debug").build() + + // then + assertThat(buildUsingConfigurationCache.output).contains("Reusing configuration cache") + .contains("Reporting build data to Apps Metrics...") + .contains("{\"name\":\"woocommerce-gradle-scan-id\",\"value\":") + .doesNotContain("{\"name\":\"woocommerce-gradle-scan-id\",\"value\":\"null\"}") + } + @Test fun `given a project that disabled build measurements and does not attach Gradle Scan Id, when executing a task, then build metrics are not sent`() { // given @@ -71,6 +102,23 @@ class BuildTimePluginTest { assertThat(run.output).doesNotContain("Reporting build data to Apps Metrics...") } + @Test + fun `given a project without apps metrics token, when executing a task, then build does not fail`() { + // given + val runner = functionalTestRunner( + enable = true, + attachGradleScanId = false, + applyAppsMetricsToken = false, + ) + + // when + val run = runner.withArguments("help").build() + + // then + assertThat(run.output).contains("No authToken provided. Skipping reporting.") + .contains("BUILD SUCCESSFUL") + } + @Test fun `given a help task to execute, when finishing the build, the help task is present in report file`() { // given @@ -131,11 +179,22 @@ class BuildTimePluginTest { plugins { id("com.automattic.android.measure-builds") } + val buildPathProperty = project.layout.buildDirectory.map { it.asFile.path } measureBuilds { ${if (enable != null) "enable.set($enable)" else ""} attachGradleScanId.set($attachGradleScanId) - automatticProject.set(com.automattic.android.measure.MeasureBuildsExtension.AutomatticProject.WooCommerce) - ${if (applyAppsMetricsToken) "authToken.set(\"token\")" else ""} + +// reporters.set(listOf( +// com.automattic.android.measure.reporters.LocalMetricsReporter, +// com.automattic.android.measure.reporters.SlowSlowTasksMetricsReporter, +// com.automattic.android.measure.reporters.internal.InternalWooCommerceA8cCiReporter, +// )) + buildMetricsReported{ + val buildPath = buildPathProperty.get() + com.automattic.android.measure.reporters.LocalMetricsReporter.report(this, buildPath) + com.automattic.android.measure.reporters.SlowSlowTasksMetricsReporter.report(this) + com.automattic.android.measure.reporters.InternalA8cCiReporter.reportBlocking(this, "woocommerce", ${if (applyAppsMetricsToken) "\"token\"" else "\"\""}) + } } """.trimIndent() ) From 1110d34bc25ae34c58d60abf962e3765bc0e421d Mon Sep 17 00:00:00 2001 From: takahirom Date: Tue, 19 Mar 2024 21:56:18 +0900 Subject: [PATCH 3/4] Fix format and fix tests --- .../android/measure/BuildTimePlugin.kt | 8 ++++---- .../android/measure/MeasureBuildsExtension.kt | 7 ++++--- .../measure/networking/ToAppsInfraPayload.kt | 5 ++++- .../reporters/InMemoryMetricsReporter.kt | 6 +++--- .../InternalWooCommerceA8cCiReporter.kt | 4 ++-- .../BuildTimePluginConfigurationCacheTests.kt | 7 ++++++- .../android/measure/BuildTimePluginTest.kt | 20 +++++++------------ 7 files changed, 30 insertions(+), 27 deletions(-) diff --git a/measure-builds/src/main/java/com/automattic/android/measure/BuildTimePlugin.kt b/measure-builds/src/main/java/com/automattic/android/measure/BuildTimePlugin.kt index 41049dd..a6a2ab5 100644 --- a/measure-builds/src/main/java/com/automattic/android/measure/BuildTimePlugin.kt +++ b/measure-builds/src/main/java/com/automattic/android/measure/BuildTimePlugin.kt @@ -35,7 +35,7 @@ class BuildTimePlugin @Inject constructor( project.extensions.create("measureBuilds", MeasureBuildsExtension::class.java, project) val metricsDispatcher = InMemoryMetricsReporter - metricsDispatcher.reportProperty = extension.reporterProperty + metricsDispatcher.buildMetricsPreparedAction = extension.buildMetricsPreparedAction val encodedUser: String = UsernameProvider.provide(project, extension) @@ -47,7 +47,7 @@ class BuildTimePlugin @Inject constructor( ConfigurationPhaseObserver.init() prepareBuildData(project, encodedUser) - prepareBuildFinishedAction( + prepareBuildFinishedAction( project.gradle.startParameter, extension, buildInitiatedTime, @@ -80,10 +80,10 @@ class BuildTimePlugin @Inject constructor( val buildScanExtension = project.extensions.findByType(BuildScanExtension::class.java) val extensionEnable = extension.enable val attachGradleScanId = extension.attachGradleScanId - buildScanExtension?.buildScanPublished { + buildScanExtension?.buildScanPublished { runBlocking { if (extensionEnable.get() == true && attachGradleScanId.get()) { - analyticsReporter.report(InMemoryReport, it.buildScanId) + analyticsReporter.report(InMemoryReport, it.buildScanId) } } } diff --git a/measure-builds/src/main/java/com/automattic/android/measure/MeasureBuildsExtension.kt b/measure-builds/src/main/java/com/automattic/android/measure/MeasureBuildsExtension.kt index 094a3c5..000587c 100644 --- a/measure-builds/src/main/java/com/automattic/android/measure/MeasureBuildsExtension.kt +++ b/measure-builds/src/main/java/com/automattic/android/measure/MeasureBuildsExtension.kt @@ -14,10 +14,11 @@ abstract class MeasureBuildsExtension(project: Project) { val obfuscateUsername: Property = objects.property(Boolean::class.java) @Suppress("UNCHECKED_CAST") - val reporterProperty: Property> = objects.property(Action::class.java) as Property> + internal val buildMetricsPreparedAction: Property> = + objects.property(Action::class.java) as Property> - fun buildMetricsReported(action: Action) { - reporterProperty.set(action) + fun buildMetricsPrepared(action: Action) { + buildMetricsPreparedAction.set(action) } /** diff --git a/measure-builds/src/main/java/com/automattic/android/measure/networking/ToAppsInfraPayload.kt b/measure-builds/src/main/java/com/automattic/android/measure/networking/ToAppsInfraPayload.kt index 4f414c6..ffc6684 100644 --- a/measure-builds/src/main/java/com/automattic/android/measure/networking/ToAppsInfraPayload.kt +++ b/measure-builds/src/main/java/com/automattic/android/measure/networking/ToAppsInfraPayload.kt @@ -5,7 +5,10 @@ import com.automattic.android.measure.models.MeasuredTask.State.EXECUTED import com.automattic.android.measure.models.MeasuredTask.State.IS_FROM_CACHE import com.automattic.android.measure.models.MeasuredTask.State.UP_TO_DATE -fun InMemoryReport.toAppsMetricsPayload(projectKey:String, gradleScanId: String?): GroupedAppsMetrics { +fun InMemoryReport.toAppsMetricsPayload( + projectKey: String, + gradleScanId: String? +): GroupedAppsMetrics { val meta = mapOf( "user" to buildData.user, diff --git a/measure-builds/src/main/java/com/automattic/android/measure/reporters/InMemoryMetricsReporter.kt b/measure-builds/src/main/java/com/automattic/android/measure/reporters/InMemoryMetricsReporter.kt index ab33f93..323fd7d 100644 --- a/measure-builds/src/main/java/com/automattic/android/measure/reporters/InMemoryMetricsReporter.kt +++ b/measure-builds/src/main/java/com/automattic/android/measure/reporters/InMemoryMetricsReporter.kt @@ -6,18 +6,18 @@ import org.gradle.api.provider.Property object InMemoryMetricsReporter { - lateinit var reportProperty: Property> + lateinit var buildMetricsPreparedAction: Property> fun report( report: InMemoryReport, gradleScanId: String? ) { - val result = object : MetricsReport { + val result = object : MetricsReport { override val report: InMemoryReport get() = report override val gradleScanId: String? get() = gradleScanId } - reportProperty.get().execute(result) + buildMetricsPreparedAction.get().execute(result) } } diff --git a/measure-builds/src/main/java/com/automattic/android/measure/reporters/InternalWooCommerceA8cCiReporter.kt b/measure-builds/src/main/java/com/automattic/android/measure/reporters/InternalWooCommerceA8cCiReporter.kt index 6f766a5..aaafbc6 100644 --- a/measure-builds/src/main/java/com/automattic/android/measure/reporters/InternalWooCommerceA8cCiReporter.kt +++ b/measure-builds/src/main/java/com/automattic/android/measure/reporters/InternalWooCommerceA8cCiReporter.kt @@ -37,7 +37,7 @@ object InternalA8cCiReporter { projectName: String, authToken: String?, ) { - runBlocking { + runBlocking { report(metricsReport, projectName, authToken) } } @@ -47,7 +47,7 @@ object InternalA8cCiReporter { projectName: String, authToken: String?, ) { - val report = metricsReport.report + val report = metricsReport.report val payload = report.toAppsMetricsPayload(projectName, metricsReport.gradleScanId) @Suppress("TooGenericExceptionCaught") try { diff --git a/measure-builds/src/test/java/com/automattic/android/measure/BuildTimePluginConfigurationCacheTests.kt b/measure-builds/src/test/java/com/automattic/android/measure/BuildTimePluginConfigurationCacheTests.kt index e41e0f2..259e253 100644 --- a/measure-builds/src/test/java/com/automattic/android/measure/BuildTimePluginConfigurationCacheTests.kt +++ b/measure-builds/src/test/java/com/automattic/android/measure/BuildTimePluginConfigurationCacheTests.kt @@ -96,10 +96,15 @@ class BuildTimePluginConfigurationCacheTests { plugins { id("com.automattic.android.measure-builds") } + val buildPathProperty = project.layout.buildDirectory.map { it.asFile.path } measureBuilds { enable.set(true) attachGradleScanId.set(false) - automatticProject.set(com.automattic.android.measure.MeasureBuildsExtension.AutomatticProject.WooCommerce) + buildMetricsPrepared{ + val buildPath = buildPathProperty.get() + com.automattic.android.measure.reporters.LocalMetricsReporter.report(this, buildPath) + com.automattic.android.measure.reporters.SlowSlowTasksMetricsReporter.report(this) + } } """.trimIndent() ) diff --git a/measure-builds/src/test/java/com/automattic/android/measure/BuildTimePluginTest.kt b/measure-builds/src/test/java/com/automattic/android/measure/BuildTimePluginTest.kt index 863ee40..9a57c77 100644 --- a/measure-builds/src/test/java/com/automattic/android/measure/BuildTimePluginTest.kt +++ b/measure-builds/src/test/java/com/automattic/android/measure/BuildTimePluginTest.kt @@ -179,22 +179,16 @@ class BuildTimePluginTest { plugins { id("com.automattic.android.measure-builds") } - val buildPathProperty = project.layout.buildDirectory.map { it.asFile.path } + val buildPathProperty = project.layout.buildDirectory.map { it.asFile.path } measureBuilds { ${if (enable != null) "enable.set($enable)" else ""} attachGradleScanId.set($attachGradleScanId) - -// reporters.set(listOf( -// com.automattic.android.measure.reporters.LocalMetricsReporter, -// com.automattic.android.measure.reporters.SlowSlowTasksMetricsReporter, -// com.automattic.android.measure.reporters.internal.InternalWooCommerceA8cCiReporter, -// )) - buildMetricsReported{ - val buildPath = buildPathProperty.get() - com.automattic.android.measure.reporters.LocalMetricsReporter.report(this, buildPath) - com.automattic.android.measure.reporters.SlowSlowTasksMetricsReporter.report(this) - com.automattic.android.measure.reporters.InternalA8cCiReporter.reportBlocking(this, "woocommerce", ${if (applyAppsMetricsToken) "\"token\"" else "\"\""}) - } + buildMetricsPrepared{ + val buildPath = buildPathProperty.get() + com.automattic.android.measure.reporters.LocalMetricsReporter.report(this, buildPath) + com.automattic.android.measure.reporters.SlowSlowTasksMetricsReporter.report(this) + com.automattic.android.measure.reporters.InternalA8cCiReporter.reportBlocking(this, "woocommerce", ${if (applyAppsMetricsToken) "\"token\"" else "\"\""}) + } } """.trimIndent() ) From 1accd072d3d243f9fab57033780d2ab0d53d3692 Mon Sep 17 00:00:00 2001 From: takahirom Date: Thu, 21 Mar 2024 19:28:51 +0900 Subject: [PATCH 4/4] Fix format issues --- .../com/automattic/android/measure/models/BuildData.kt | 1 - .../android/measure/networking/ToAppsInfraPayload.kt | 1 - .../android/measure/providers/BuildDataProvider.kt | 1 - ...ooCommerceA8cCiReporter.kt => InternalA8cCiReporter.kt} | 7 +++---- .../android/measure/reporters/LocalMetricsReporter.kt | 4 ++-- .../measure/reporters/SlowSlowTasksMetricsReporter.kt | 3 +-- 6 files changed, 6 insertions(+), 11 deletions(-) rename measure-builds/src/main/java/com/automattic/android/measure/reporters/{InternalWooCommerceA8cCiReporter.kt => InternalA8cCiReporter.kt} (97%) diff --git a/measure-builds/src/main/java/com/automattic/android/measure/models/BuildData.kt b/measure-builds/src/main/java/com/automattic/android/measure/models/BuildData.kt index eb9a010..0d9c100 100644 --- a/measure-builds/src/main/java/com/automattic/android/measure/models/BuildData.kt +++ b/measure-builds/src/main/java/com/automattic/android/measure/models/BuildData.kt @@ -1,6 +1,5 @@ package com.automattic.android.measure.models -import com.automattic.android.measure.MeasureBuildsExtension import kotlinx.serialization.Serializable /** diff --git a/measure-builds/src/main/java/com/automattic/android/measure/networking/ToAppsInfraPayload.kt b/measure-builds/src/main/java/com/automattic/android/measure/networking/ToAppsInfraPayload.kt index ffc6684..f3841d0 100644 --- a/measure-builds/src/main/java/com/automattic/android/measure/networking/ToAppsInfraPayload.kt +++ b/measure-builds/src/main/java/com/automattic/android/measure/networking/ToAppsInfraPayload.kt @@ -9,7 +9,6 @@ fun InMemoryReport.toAppsMetricsPayload( projectKey: String, gradleScanId: String? ): GroupedAppsMetrics { - val meta = mapOf( "user" to buildData.user, "project" to projectKey, diff --git a/measure-builds/src/main/java/com/automattic/android/measure/providers/BuildDataProvider.kt b/measure-builds/src/main/java/com/automattic/android/measure/providers/BuildDataProvider.kt index f826e57..f298ebe 100644 --- a/measure-builds/src/main/java/com/automattic/android/measure/providers/BuildDataProvider.kt +++ b/measure-builds/src/main/java/com/automattic/android/measure/providers/BuildDataProvider.kt @@ -1,6 +1,5 @@ package com.automattic.android.measure.providers -import com.automattic.android.measure.MeasureBuildsExtension import com.automattic.android.measure.models.BuildData import com.automattic.android.measure.models.Environment import org.gradle.api.Project diff --git a/measure-builds/src/main/java/com/automattic/android/measure/reporters/InternalWooCommerceA8cCiReporter.kt b/measure-builds/src/main/java/com/automattic/android/measure/reporters/InternalA8cCiReporter.kt similarity index 97% rename from measure-builds/src/main/java/com/automattic/android/measure/reporters/InternalWooCommerceA8cCiReporter.kt rename to measure-builds/src/main/java/com/automattic/android/measure/reporters/InternalA8cCiReporter.kt index aaafbc6..99b4f42 100644 --- a/measure-builds/src/main/java/com/automattic/android/measure/reporters/InternalWooCommerceA8cCiReporter.kt +++ b/measure-builds/src/main/java/com/automattic/android/measure/reporters/InternalA8cCiReporter.kt @@ -21,16 +21,15 @@ import io.ktor.http.HttpHeaders.Authorization import io.ktor.http.HttpStatusCode import io.ktor.http.contentType import kotlinx.coroutines.runBlocking -import java.io.File import java.util.Locale import java.util.concurrent.TimeUnit.MILLISECONDS import java.util.concurrent.TimeUnit.MINUTES import kotlin.time.Duration.Companion.seconds - +import org.gradle.api.logging.Logging as GradleLogging object InternalA8cCiReporter { private val logger = - org.gradle.api.logging.Logging.getLogger(InternalA8cCiReporter::class.java) + GradleLogging.getLogger(InternalA8cCiReporter::class.java) fun reportBlocking( metricsReport: MetricsReport, @@ -122,4 +121,4 @@ object InternalA8cCiReporter { } return client } -} \ No newline at end of file +} diff --git a/measure-builds/src/main/java/com/automattic/android/measure/reporters/LocalMetricsReporter.kt b/measure-builds/src/main/java/com/automattic/android/measure/reporters/LocalMetricsReporter.kt index a6cf76d..b7f46a8 100644 --- a/measure-builds/src/main/java/com/automattic/android/measure/reporters/LocalMetricsReporter.kt +++ b/measure-builds/src/main/java/com/automattic/android/measure/reporters/LocalMetricsReporter.kt @@ -17,7 +17,7 @@ object LocalMetricsReporter { buildDirPath: String, ) { val report = metricsReport.report - Path("${buildDirPath}/reports/measure_builds") + Path("$buildDirPath/reports/measure_builds") .apply { if (!exists()) { createDirectories() @@ -38,4 +38,4 @@ object LocalMetricsReporter { } } } -} \ No newline at end of file +} diff --git a/measure-builds/src/main/java/com/automattic/android/measure/reporters/SlowSlowTasksMetricsReporter.kt b/measure-builds/src/main/java/com/automattic/android/measure/reporters/SlowSlowTasksMetricsReporter.kt index 4ea6e9d..723b693 100644 --- a/measure-builds/src/main/java/com/automattic/android/measure/reporters/SlowSlowTasksMetricsReporter.kt +++ b/measure-builds/src/main/java/com/automattic/android/measure/reporters/SlowSlowTasksMetricsReporter.kt @@ -1,6 +1,5 @@ package com.automattic.android.measure.reporters -import com.automattic.android.measure.InMemoryReport import com.automattic.android.measure.logging.Emojis import com.automattic.android.measure.models.MeasuredTask import org.gradle.api.logging.Logging @@ -53,4 +52,4 @@ object SlowSlowTasksMetricsReporter { } private const val atMostLoggedTasks = 5 -} \ No newline at end of file +}