Skip to content

Commit

Permalink
MBS-10126 Add posibility to user defined custom task verdict producers (
Browse files Browse the repository at this point in the history
  • Loading branch information
sboishtyan authored Dec 14, 2020
1 parent 246b6f3 commit 3d5219e
Show file tree
Hide file tree
Showing 19 changed files with 246 additions and 120 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,16 @@ class BuildVerdictPlugin : Plugin<ProjectInternal> {

if (project.pluginIsEnabled) {
val extension = project.extensions.create<BuildVerdictPluginExtension>("buildVerdict")
val outputDir = extension.outputDir
val services = BuildVerdictPluginServices()
val services = BuildVerdictPluginServices(extension, project.ciLogger)
project.gradle.addListener(services.gradleTaskExecutionListener())
project.gradle.addLogEventListener(services.gradleLogEventListener())
val configurationListener = services.gradleConfigurationListener(outputDir, project.ciLogger)
val configurationListener = services.gradleConfigurationListener()
project.gradle.addBuildListener(configurationListener)
project.gradle.taskGraph.whenReady { graph ->
project.gradle.removeListener(configurationListener)
project.gradle.addBuildListener(
services.gradleBuildFinishedListener(
graph,
outputDir,
project.ciLogger
graph
)
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.avito.android.build_verdict

import org.gradle.api.Task
import org.gradle.api.file.Directory
import org.gradle.api.file.ProjectLayout
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.Property
import org.gradle.kotlin.dsl.listProperty

@Suppress("UnstableApiUsage")
abstract class BuildVerdictPluginExtension(
Expand All @@ -12,4 +14,32 @@ abstract class BuildVerdictPluginExtension(
) {
val outputDir: Property<Directory> =
objects.directoryProperty().convention(layout.projectDirectory.dir("outputs/build-verdict"))

internal val taskVerdictProviders = objects.listProperty<UserDefinedTaskVerdictProducer>()

fun onTaskFailure(name: String, verdictProducer: TaskVerdictProducer) {
taskVerdictProviders.add(
UserDefinedTaskVerdictProducer(
predicate = TaskPredicate.ByName(name),
producer = verdictProducer
)
)
}

fun onTaskFailure(acceptedType: Class<in Task>, verdictProducer: TaskVerdictProducer) {
taskVerdictProviders.add(
UserDefinedTaskVerdictProducer(
predicate = TaskPredicate.ByType(acceptedType),
producer = verdictProducer
)
)
}

fun onTaskFailure(name: String, producer: (Task) -> String) {
onTaskFailure(name, TaskVerdictProducer.create(producer))
}

fun onTaskFailure(acceptedClass: Class<in Task>, producer: (Task) -> String) {
onTaskFailure(acceptedClass, TaskVerdictProducer.create(producer))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.avito.android.build_verdict

import org.gradle.api.Task

class UserDefinedTaskVerdictProducer(
predicate: TaskPredicate,
producer: TaskVerdictProducer
) : TaskPredicate by predicate, TaskVerdictProducer by producer

interface TaskPredicate {
fun accept(task: Task): Boolean

class ByName(private val name: String) : TaskPredicate {
override fun accept(task: Task) = task.name == name
}

class ByType(private val acceptedClass: Class<in Task>) : TaskPredicate {
override fun accept(task: Task) = acceptedClass.isInstance(task)
}
}

interface TaskVerdictProducer {
fun produce(task: Task): String

companion object {
internal inline fun create(crossinline producer: (Task) -> String): TaskVerdictProducer {
return object : TaskVerdictProducer {
override fun produce(task: Task) = producer(task)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import org.gradle.util.Path
internal class BuildExecutionFailureListener(
private val graph: TaskExecutionGraph,
private val logs: Map<Path, LogsTextBuilder>,
private val verdicts: Map<Path, LogsTextBuilder>,
private val writer: BuildVerdictWriter
) : BaseBuildListener() {

Expand All @@ -29,7 +30,8 @@ internal class BuildExecutionFailureListener(
FailedTask(
name = task.name,
projectPath = task.project.path,
errorOutput = logs[Path.path(task.path)]?.build() ?: "No error logs",
errorLogs = logs[Path.path(task.path)]?.build() ?: "No error logs",
verdict = verdicts[Path.path(task.path)]?.build(),
error = task.state.failure!!.let { error -> Error.from(error) }
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ internal sealed class BuildVerdict {
internal data class FailedTask(
val name: String,
val projectPath: String,
val errorOutput: String,
val error: Error
val errorLogs: String,
val error: Error,
val verdict: String? = null
)
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package com.avito.android.build_verdict.internal

import com.avito.android.build_verdict.BuildVerdictPluginExtension
import com.avito.android.build_verdict.internal.task.lifecycle.BuildVerdictTaskLifecycleListener
import com.avito.android.build_verdict.internal.task.lifecycle.DefaultTaskLifecycleListener
import com.avito.android.build_verdict.internal.task.lifecycle.TaskExecutionListenerBridge
import com.avito.android.build_verdict.internal.task.lifecycle.TestTaskLifecycleListener
import com.avito.android.build_verdict.internal.task.lifecycle.UserDefinedVerdictProducerTaskLifecycleListener
import com.avito.android.build_verdict.internal.writer.CompositeBuildVerdictWriter
import com.avito.android.build_verdict.internal.writer.PlainTextBuildVerdictWriter
import com.avito.android.build_verdict.internal.writer.RawBuildVerdictWriter
Expand All @@ -18,10 +21,14 @@ import org.gradle.internal.operations.OperationIdentifier
import org.gradle.util.Path
import java.util.concurrent.ConcurrentHashMap

internal class BuildVerdictPluginServices {
internal class BuildVerdictPluginServices(
private val extension: BuildVerdictPluginExtension,
private val logger: CILogger
) {

private val listeners = ConcurrentHashMap<OperationIdentifier, LogMessageListener>()
private val logs = ConcurrentHashMap<Path, LogsTextBuilder>()
private val verdicts = ConcurrentHashMap<Path, LogsTextBuilder>()
private val gson by lazy {
GsonBuilder()
.disableHtmlEscaping()
Expand All @@ -36,33 +43,39 @@ internal class BuildVerdictPluginServices {
}

fun gradleTaskExecutionListener(): TaskExecutionListener {
return TaskExecutionErrorsCapture(
testLifecycle = TestTaskLifecycleListener(logs),
buildVerdictLifecycle = BuildVerdictTaskLifecycleListener(logs),
defaultLifecycle = DefaultTaskLifecycleListener(logs, listeners)
return TaskExecutionListenerBridge(
listeners = listOf(
TestTaskLifecycleListener(verdicts),
BuildVerdictTaskLifecycleListener(verdicts),
DefaultTaskLifecycleListener(logs, listeners),
UserDefinedVerdictProducerTaskLifecycleListener(
taskVerdictProducers = lazy {
extension.taskVerdictProviders.getOrElse(
emptyList()
)
},
verdicts = verdicts
)
)
)
}

fun gradleConfigurationListener(
outputDir: Provider<Directory>,
logger: CILogger
): BuildListener {
fun gradleConfigurationListener(): BuildListener {
return BuildConfigurationFailureListener(
createWriter(
outputDir = outputDir,
outputDir = extension.outputDir,
logger = logger
)
)
}

fun gradleBuildFinishedListener(
graph: TaskExecutionGraph,
outputDir: Provider<Directory>,
logger: CILogger
graph: TaskExecutionGraph
): BuildListener = BuildExecutionFailureListener(
graph = graph,
logs = logs,
writer = createWriter(outputDir, logger)
verdicts = verdicts,
writer = createWriter(extension.outputDir, logger)
)

private fun createWriter(
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,14 @@ import com.avito.android.build_verdict.internal.LogsTextBuilder
import org.gradle.api.Task
import org.gradle.util.Path

class BuildVerdictTaskLifecycleListener(
override val logs: MutableMap<Path, LogsTextBuilder>
internal class BuildVerdictTaskLifecycleListener(
private val verdicts: MutableMap<Path, LogsTextBuilder>
) : TaskLifecycleListener<BuildVerdictTask>() {

override fun beforeExecute(task: BuildVerdictTask) {
// empty
}
override val acceptedTask: Class<in BuildVerdictTask> = BuildVerdictTask::class.java

override fun afterFailedExecute(task: BuildVerdictTask) {
logs.getOrPut(Path.path((task as Task).path), { LogsTextBuilder() })
override fun afterFailedExecute(task: BuildVerdictTask, error: Throwable) {
verdicts.getOrPut(Path.path((task as Task).path), { LogsTextBuilder() })
.addLine(task.verdict)
}

override fun afterSucceedExecute(task: BuildVerdictTask) {
// empty
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ import org.gradle.internal.operations.CurrentBuildOperationRef
import org.gradle.internal.operations.OperationIdentifier
import org.gradle.util.Path

class DefaultTaskLifecycleListener(
override val logs: MutableMap<Path, LogsTextBuilder>,
internal class DefaultTaskLifecycleListener(
private val logs: MutableMap<Path, LogsTextBuilder>,
private val listeners: MutableMap<OperationIdentifier, LogMessageListener>
) : TaskLifecycleListener<Task>() {

override fun beforeExecute(task: Task) {
override val acceptedTask: Class<in Task> = Task::class.java

override fun beforeExecuteTyped(task: Task) {
val id = CurrentBuildOperationRef.instance().id
if (id != null) {
val path = Path.path(task.path)
Expand All @@ -27,9 +29,10 @@ class DefaultTaskLifecycleListener(

override fun afterSucceedExecute(task: Task) {
deleteListener()
logs.remove(Path.path(task.path))
}

override fun afterFailedExecute(task: Task) {
override fun afterFailedExecute(task: Task, error: Throwable) {
deleteListener()
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.avito.android.build_verdict.internal.task.lifecycle

import org.gradle.BuildAdapter
import org.gradle.BuildResult
import org.gradle.api.Task
import org.gradle.api.execution.TaskExecutionListener
import org.gradle.api.tasks.TaskState

internal class TaskExecutionListenerBridge(
private val listeners: List<TaskLifecycleListener<*>>
) : TaskExecutionListener, BuildAdapter() {

override fun beforeExecute(task: Task) {
listeners.forEach { it.beforeExecute(task) }
}

override fun afterExecute(task: Task, state: TaskState) {
listeners.forEach { it.afterExecute(task, state) }
}

override fun buildFinished(result: BuildResult) {
result.gradle?.removeListener(this)
}
}
Original file line number Diff line number Diff line change
@@ -1,25 +1,40 @@
package com.avito.android.build_verdict.internal.task.lifecycle

import com.avito.android.build_verdict.internal.LogsTextBuilder
import org.gradle.api.Task
import org.gradle.util.Path
import org.gradle.api.tasks.TaskState

abstract class TaskLifecycleListener<in T> {
internal abstract class TaskLifecycleListener<in T> {

protected abstract val logs: MutableMap<Path, LogsTextBuilder>
abstract val acceptedTask: Class<in T>

abstract fun beforeExecute(task: T)
@Suppress("UNCHECKED_CAST")
fun beforeExecute(task: Task) {
if (acceptedTask.isInstance(task)) {
beforeExecuteTyped(task as T)
}
}

protected open fun beforeExecuteTyped(task: T) {
// empty
}

protected abstract fun afterSucceedExecute(task: T)
protected open fun afterSucceedExecute(task: T) {
// empty
}

protected abstract fun afterFailedExecute(task: T)
protected open fun afterFailedExecute(task: T, error: Throwable) {
// empty
}

fun afterExecute(task: T, failure: Throwable?) {
if (failure == null) {
logs.remove(Path.path((task as Task).path))
afterSucceedExecute(task)
} else {
afterFailedExecute(task)
@Suppress("UNCHECKED_CAST")
fun afterExecute(task: Task, state: TaskState) {
if (acceptedTask.isInstance(task)) {
val failure = state.failure
if (failure != null) {
afterFailedExecute(task as T, failure)
} else {
afterSucceedExecute(task as T)
}
}
}
}
Loading

0 comments on commit 3d5219e

Please sign in to comment.