diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index 930c8a2..0000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,134 +0,0 @@ - -# Contributor Covenant Code of Conduct - -## Our Pledge - -We as members, contributors, and leaders pledge to make participation in our -community a harassment-free experience for everyone, regardless of age, body -size, visible or invisible disability, ethnicity, sex characteristics, gender -identity and expression, level of experience, education, socio-economic status, -nationality, personal appearance, race, caste, color, religion, or sexual -identity and orientation. - -We pledge to act and interact in ways that contribute to an open, welcoming, -diverse, inclusive, and healthy community. - -## Our Standards - -Examples of behavior that contributes to a positive environment for our -community include: - -* Demonstrating empathy and kindness toward other people -* Being respectful of differing opinions, viewpoints, and experiences -* Giving and gracefully accepting constructive feedback -* Accepting responsibility and apologizing to those affected by our mistakes, - and learning from the experience -* Focusing on what is best not just for us as individuals, but for the overall - community - -Examples of unacceptable behavior include: - -* The use of sexualized language or imagery, and sexual attention or advances of - any kind -* Trolling, insulting or derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or email address, - without their explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting - -## Enforcement Responsibilities - -Community leaders are responsible for clarifying and enforcing our standards of -acceptable behavior and will take appropriate and fair corrective action in -response to any behavior that they deem inappropriate, threatening, offensive, -or harmful. - -Community leaders have the right and responsibility to remove, edit, or reject -comments, commits, code, wiki edits, issues, and other contributions that are -not aligned to this Code of Conduct, and will communicate reasons for moderation -decisions when appropriate. - -## Scope - -This Code of Conduct applies within all community spaces, and also applies when -an individual is officially representing the community in public spaces. -Examples of representing our community include using an official e-mail address, -posting via an official social media account, or acting as an appointed -representative at an online or offline event. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported to the community leaders responsible for enforcement at -[contact@asarkar.com](mailto:contact@asarkar.com). -All complaints will be reviewed and investigated promptly and fairly. - -All community leaders are obligated to respect the privacy and security of the -reporter of any incident. - -## Enforcement Guidelines - -Community leaders will follow these Community Impact Guidelines in determining -the consequences for any action they deem in violation of this Code of Conduct: - -### 1. Correction - -**Community Impact**: Use of inappropriate language or other behavior deemed -unprofessional or unwelcome in the community. - -**Consequence**: A private, written warning from community leaders, providing -clarity around the nature of the violation and an explanation of why the -behavior was inappropriate. A public apology may be requested. - -### 2. Warning - -**Community Impact**: A violation through a single incident or series of -actions. - -**Consequence**: A warning with consequences for continued behavior. No -interaction with the people involved, including unsolicited interaction with -those enforcing the Code of Conduct, for a specified period of time. This -includes avoiding interactions in community spaces as well as external channels -like social media. Violating these terms may lead to a temporary or permanent -ban. - -### 3. Temporary Ban - -**Community Impact**: A serious violation of community standards, including -sustained inappropriate behavior. - -**Consequence**: A temporary ban from any sort of interaction or public -communication with the community for a specified period of time. No public or -private interaction with the people involved, including unsolicited interaction -with those enforcing the Code of Conduct, is allowed during this period. -Violating these terms may lead to a permanent ban. - -### 4. Permanent Ban - -**Community Impact**: Demonstrating a pattern of violation of community -standards, including sustained inappropriate behavior, harassment of an -individual, or aggression toward or disparagement of classes of individuals. - -**Consequence**: A permanent ban from any sort of public interaction within the -community. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], -version 2.1, available at -[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. - -Community Impact Guidelines were inspired by -[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. - -For answers to common questions about this code of conduct, see the FAQ at -[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at -[https://www.contributor-covenant.org/translations][translations]. - -[homepage]: https://www.contributor-covenant.org -[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html -[Mozilla CoC]: https://github.com/mozilla/diversity -[FAQ]: https://www.contributor-covenant.org/faq -[translations]: https://www.contributor-covenant.org/translations - diff --git a/README.md b/README.md index 64c8bd2..f9a649e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # build-time-tracker -Gradle plugin that prints the time taken by the tasks in a build. +Gradle plugin that prints the time taken by the tasks in a build. If you like it, consider becoming a +![GitHub Sponsor](https://img.shields.io/github/sponsors/asarkar?label=Sponsor&logo=GitHub). [![](https://github.com/asarkar/build-time-tracker/workflows/CI%20Pipeline/badge.svg)](https://github.com/asarkar/build-time-tracker/actions?query=workflow%3A%22CI+Pipeline%22) @@ -36,9 +37,10 @@ buildTimeTracker { :information_source: Due to a [Gradle limitation](https://docs.gradle.org/6.5.1/userguide/upgrading_version_5.html#apis_buildlistener_buildstarted_and_gradle_buildstarted_have_been_deprecated) -, the build duration can't be calculated precisely. The bars and percentages are rounded off such that the output -provides a good indication of how long individual tasks took to complete relative to the build, but are not meant to be -correct up to the milliseconds. +and the ill-thought-out [Configuration Cache](https://github.com/gradle/gradle/issues/18520) design, the build duration +can't be calculated precisely. The bars and percentages are rounded off such that the output provides a good indication +of how long individual tasks took to complete relative to the build, but are not meant to be correct up to the +milliseconds. :information_source: It is sufficient to apply the plugin to the root project; applying to subprojects will result in duplication of the report. @@ -55,14 +57,14 @@ is one way. - Java 11 - Gradle 6.1 -## Contribute +## Contribution -This project is a volunteer effort. You are welcome to send pull requests, ask questions, or create issues. If you like -it, you can help by spreading the word and "Starring" the GitHub repo! +This project is a volunteer effort. You are welcome to send pull requests, ask questions, or create issues. ## Code of Conduct -This project adheres to the Contributor Covenant [code of conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. +This project adheres to the Contributor Covenant [code of conduct](https://github.com/asarkar/.github/blob/main/CODE_OF_CONDUCT.md). +By participating, you are expected to uphold this code. ## License diff --git a/gradle.properties b/gradle.properties index 721b9a7..60885bf 100644 --- a/gradle.properties +++ b/gradle.properties @@ -11,7 +11,7 @@ pluginImplementationClass = com.asarkar.gradle.buildtimetracker.BuildTimeTracker pluginDeclarationName = buildTimeTrackerPlugin projectGroup = com.asarkar.gradle -projectVersion = 4.0.0 +projectVersion = 4.1.0 junitVersion = latest.release assertjVersion = latest.release diff --git a/src/main/kotlin/com/asarkar/gradle/buildtimetracker/BuildTimeTrackerPlugin.kt b/src/main/kotlin/com/asarkar/gradle/buildtimetracker/BuildTimeTrackerPlugin.kt index 4fe91ba..8cd3116 100644 --- a/src/main/kotlin/com/asarkar/gradle/buildtimetracker/BuildTimeTrackerPlugin.kt +++ b/src/main/kotlin/com/asarkar/gradle/buildtimetracker/BuildTimeTrackerPlugin.kt @@ -1,26 +1,40 @@ package com.asarkar.gradle.buildtimetracker -import com.asarkar.gradle.buildtimetracker.Constants.EXTRA_EXTENSION_NAME -import com.asarkar.gradle.buildtimetracker.Constants.LOGGER_KEY import com.asarkar.gradle.buildtimetracker.Constants.PLUGIN_EXTENSION_NAME import org.gradle.api.Plugin import org.gradle.api.Project -import org.gradle.api.plugins.ExtensionAware import org.gradle.api.plugins.ReportingBasePlugin -import org.gradle.api.reflect.TypeOf +import org.gradle.build.event.BuildEventsListenerRegistry +import javax.inject.Inject -class BuildTimeTrackerPlugin : Plugin { +@Suppress("UnstableApiUsage") +class BuildTimeTrackerPlugin @Inject constructor(private val registry: BuildEventsListenerRegistry) : Plugin { override fun apply(project: Project) { project.pluginManager.apply(ReportingBasePlugin::class.java) val ext = project.extensions.create( PLUGIN_EXTENSION_NAME, BuildTimeTrackerPluginExtension::class.java, project ) - (ext as ExtensionAware).extensions.add( - object : TypeOf>() {}, - EXTRA_EXTENSION_NAME, - mapOf(LOGGER_KEY to project.logger) - ) - val timingRecorder = TimingRecorder(ext) - project.gradle.addListener(timingRecorder) + val clazz = TimingRecorder::class.java + val timingRecorder = + project.gradle.sharedServices.registerIfAbsent(clazz.simpleName, clazz) { spec -> + val params = BuildTimeTrackerPluginParams(ext.reportsDir.get().asFile) + spec.parameters.getParams().set(params) + } + + project.gradle.projectsEvaluated { + copyParams(ext, timingRecorder.get().parameters.getParams().get()) + } + + registry.onTaskCompletion(timingRecorder) + } + + private fun copyParams(src: BuildTimeTrackerPluginExtension, dest: BuildTimeTrackerPluginParams) { + dest.barPosition = src.barPosition.get() + dest.sort = src.sort.get() + dest.output = src.output.get() + dest.maxWidth = src.maxWidth.get() + dest.minTaskDuration = src.minTaskDuration.get() + dest.showBars = src.showBars.get() + dest.reportsDir = src.reportsDir.get().asFile } } diff --git a/src/main/kotlin/com/asarkar/gradle/buildtimetracker/BuildTimeTrackerPluginExtension.kt b/src/main/kotlin/com/asarkar/gradle/buildtimetracker/BuildTimeTrackerPluginHelper.kt similarity index 78% rename from src/main/kotlin/com/asarkar/gradle/buildtimetracker/BuildTimeTrackerPluginExtension.kt rename to src/main/kotlin/com/asarkar/gradle/buildtimetracker/BuildTimeTrackerPluginHelper.kt index cb5c488..5831433 100644 --- a/src/main/kotlin/com/asarkar/gradle/buildtimetracker/BuildTimeTrackerPluginExtension.kt +++ b/src/main/kotlin/com/asarkar/gradle/buildtimetracker/BuildTimeTrackerPluginHelper.kt @@ -4,6 +4,7 @@ import org.gradle.api.Project import org.gradle.api.file.DirectoryProperty import org.gradle.api.provider.Property import org.gradle.api.reporting.ReportingExtension +import java.io.File import java.time.Duration enum class BarPosition { @@ -34,3 +35,12 @@ open class BuildTimeTrackerPluginExtension(private val project: Project) { get() = project.extensions.getByType(ReportingExtension::class.java) .baseDirectory } + +open class BuildTimeTrackerPluginParams(var reportsDir: File) : java.io.Serializable { + var barPosition = Constants.DEFAULT_BAR_POSITION + var sort = Constants.DEFAULT_SORT + var output = Constants.DEFAULT_OUTPUT + var maxWidth = Constants.DEFAULT_MAX_WIDTH + var minTaskDuration: Duration = Duration.ofSeconds(Constants.DEFAULT_MIN_TASK_DURATION) + var showBars = Constants.DEFAULT_SHOW_BARS +} diff --git a/src/main/kotlin/com/asarkar/gradle/buildtimetracker/Printer.kt b/src/main/kotlin/com/asarkar/gradle/buildtimetracker/Printer.kt index 7f4d32d..6ea352d 100644 --- a/src/main/kotlin/com/asarkar/gradle/buildtimetracker/Printer.kt +++ b/src/main/kotlin/com/asarkar/gradle/buildtimetracker/Printer.kt @@ -70,13 +70,12 @@ interface Printer : Closeable { private fun Int.format(): String = String.format("%d%%", this) - fun newInstance(ext: BuildTimeTrackerPluginExtension): Printer { - return when (ext.output.get()) { + fun newInstance(params: BuildTimeTrackerPluginParams): Printer { + return when (params.output) { Output.CONSOLE -> ConsolePrinter() Output.CSV -> { - val csvFile = ext.reportsDir.get() - .file(Constants.CSV_FILENAME) - .asFile + val csvFile = params.reportsDir + .resolve(Constants.CSV_FILENAME) CsvPrinter(newOutputStream(csvFile)) } } diff --git a/src/main/kotlin/com/asarkar/gradle/buildtimetracker/TimingRecorder.kt b/src/main/kotlin/com/asarkar/gradle/buildtimetracker/TimingRecorder.kt index f6609ee..c554fa1 100644 --- a/src/main/kotlin/com/asarkar/gradle/buildtimetracker/TimingRecorder.kt +++ b/src/main/kotlin/com/asarkar/gradle/buildtimetracker/TimingRecorder.kt @@ -1,62 +1,50 @@ package com.asarkar.gradle.buildtimetracker -import org.gradle.BuildAdapter -import org.gradle.BuildResult -import org.gradle.api.Task -import org.gradle.api.execution.TaskExecutionListener -import org.gradle.api.invocation.Gradle -import org.gradle.api.logging.Logger -import org.gradle.api.plugins.ExtensionAware -import org.gradle.api.reflect.TypeOf -import org.gradle.api.tasks.TaskState +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 +import org.gradle.tooling.events.task.TaskFinishEvent import java.time.Duration import java.time.Instant -import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentLinkedQueue -class TimingRecorder(private val ext: BuildTimeTrackerPluginExtension) : TaskExecutionListener, BuildAdapter() { - private val taskStartTimings: MutableMap = ConcurrentHashMap() - private val taskDurations: MutableCollection> = ConcurrentLinkedQueue() - private lateinit var buildStarted: Instant - - override fun beforeExecute(task: Task) { - taskStartTimings[task.path] = Instant.now() +@Suppress("UnstableApiUsage") +abstract class TimingRecorder : BuildService, OperationCompletionListener, AutoCloseable { + interface Params : BuildServiceParameters { + fun getParams(): Property } - override fun afterExecute(task: Task, state: TaskState) { - check(taskStartTimings.contains(task.path)) { "No start timing for task ${task.path}" } - val duration = Duration.between(taskStartTimings[task.path], Instant.now()).seconds - if (duration >= ext.minTaskDuration.get().seconds) { - taskDurations.add(task.path to duration) + private val taskDurations: MutableCollection> = ConcurrentLinkedQueue() + private val buildStarted: Instant = Instant.now() + + override fun onFinish(event: FinishEvent) { + if (event is TaskFinishEvent) { + val params = parameters.getParams().get() + val duration = Duration.ofMillis(event.result.endTime - event.result.startTime).seconds + if (duration >= params.minTaskDuration.seconds) { + taskDurations.add(event.descriptor.taskPath to duration) + } } } - override fun buildFinished(result: BuildResult) { + override fun close() { if (taskDurations.isEmpty()) { - val extra = (ext as ExtensionAware).extensions.getByType( - object : TypeOf>() {} - ) - (extra[Constants.LOGGER_KEY] as Logger).lifecycle( - "All tasks completed within the minimum threshold: {}s, no build summary to show", - ext.minTaskDuration.get().seconds - ) return } + val params = parameters.getParams().get() val buildDuration = Duration.between(buildStarted, Instant.now()).seconds - Printer.newInstance(ext) + Printer.newInstance(params) .use { printer -> val input = PrinterInput( buildDuration, - if (ext.sort.get()) taskDurations.sortedBy { -it.second } else taskDurations, - ext.maxWidth.get(), - ext.showBars.get(), - ext.barPosition.get() + if (params.sort) taskDurations.sortedBy { -it.second } else taskDurations, + params.maxWidth, + params.showBars, + params.barPosition ) printer.print(input) } } - - override fun projectsEvaluated(gradle: Gradle) { - buildStarted = Instant.now() - } }