diff --git a/subprojects/gradle/build-verdict/src/main/kotlin/com/avito/android/build_verdict/BuildVerdictPlugin.kt b/subprojects/gradle/build-verdict/src/main/kotlin/com/avito/android/build_verdict/BuildVerdictPlugin.kt index ce81394b68..19fb25ae70 100644 --- a/subprojects/gradle/build-verdict/src/main/kotlin/com/avito/android/build_verdict/BuildVerdictPlugin.kt +++ b/subprojects/gradle/build-verdict/src/main/kotlin/com/avito/android/build_verdict/BuildVerdictPlugin.kt @@ -29,19 +29,16 @@ class BuildVerdictPlugin : Plugin { if (project.pluginIsEnabled) { val extension = project.extensions.create("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 ) ) } diff --git a/subprojects/gradle/build-verdict/src/main/kotlin/com/avito/android/build_verdict/BuildVerdictPluginExtension.kt b/subprojects/gradle/build-verdict/src/main/kotlin/com/avito/android/build_verdict/BuildVerdictPluginExtension.kt index 020bbb2abc..ecde0296d3 100644 --- a/subprojects/gradle/build-verdict/src/main/kotlin/com/avito/android/build_verdict/BuildVerdictPluginExtension.kt +++ b/subprojects/gradle/build-verdict/src/main/kotlin/com/avito/android/build_verdict/BuildVerdictPluginExtension.kt @@ -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( @@ -12,4 +14,32 @@ abstract class BuildVerdictPluginExtension( ) { val outputDir: Property = objects.directoryProperty().convention(layout.projectDirectory.dir("outputs/build-verdict")) + + internal val taskVerdictProviders = objects.listProperty() + + fun onTaskFailure(name: String, verdictProducer: TaskVerdictProducer) { + taskVerdictProviders.add( + UserDefinedTaskVerdictProducer( + predicate = TaskPredicate.ByName(name), + producer = verdictProducer + ) + ) + } + + fun onTaskFailure(acceptedType: Class, 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, producer: (Task) -> String) { + onTaskFailure(acceptedClass, TaskVerdictProducer.create(producer)) + } } diff --git a/subprojects/gradle/build-verdict/src/main/kotlin/com/avito/android/build_verdict/UserDefinedTaskVerdictProducer.kt b/subprojects/gradle/build-verdict/src/main/kotlin/com/avito/android/build_verdict/UserDefinedTaskVerdictProducer.kt new file mode 100644 index 0000000000..b58484b686 --- /dev/null +++ b/subprojects/gradle/build-verdict/src/main/kotlin/com/avito/android/build_verdict/UserDefinedTaskVerdictProducer.kt @@ -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) : 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) + } + } + } +} diff --git a/subprojects/gradle/build-verdict/src/main/kotlin/com/avito/android/build_verdict/internal/BuildExecutionFailureListener.kt b/subprojects/gradle/build-verdict/src/main/kotlin/com/avito/android/build_verdict/internal/BuildExecutionFailureListener.kt index 51a7d90d38..dcb5e83b43 100644 --- a/subprojects/gradle/build-verdict/src/main/kotlin/com/avito/android/build_verdict/internal/BuildExecutionFailureListener.kt +++ b/subprojects/gradle/build-verdict/src/main/kotlin/com/avito/android/build_verdict/internal/BuildExecutionFailureListener.kt @@ -8,6 +8,7 @@ import org.gradle.util.Path internal class BuildExecutionFailureListener( private val graph: TaskExecutionGraph, private val logs: Map, + private val verdicts: Map, private val writer: BuildVerdictWriter ) : BaseBuildListener() { @@ -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) } ) } diff --git a/subprojects/gradle/build-verdict/src/main/kotlin/com/avito/android/build_verdict/internal/BuildVerdict.kt b/subprojects/gradle/build-verdict/src/main/kotlin/com/avito/android/build_verdict/internal/BuildVerdict.kt index 9960c42ae8..e76942dc55 100644 --- a/subprojects/gradle/build-verdict/src/main/kotlin/com/avito/android/build_verdict/internal/BuildVerdict.kt +++ b/subprojects/gradle/build-verdict/src/main/kotlin/com/avito/android/build_verdict/internal/BuildVerdict.kt @@ -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 ) diff --git a/subprojects/gradle/build-verdict/src/main/kotlin/com/avito/android/build_verdict/internal/BuildVerdictPluginServices.kt b/subprojects/gradle/build-verdict/src/main/kotlin/com/avito/android/build_verdict/internal/BuildVerdictPluginServices.kt index 5b7bc7b728..e1d8b05fd7 100644 --- a/subprojects/gradle/build-verdict/src/main/kotlin/com/avito/android/build_verdict/internal/BuildVerdictPluginServices.kt +++ b/subprojects/gradle/build-verdict/src/main/kotlin/com/avito/android/build_verdict/internal/BuildVerdictPluginServices.kt @@ -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 @@ -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() private val logs = ConcurrentHashMap() + private val verdicts = ConcurrentHashMap() private val gson by lazy { GsonBuilder() .disableHtmlEscaping() @@ -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, - logger: CILogger - ): BuildListener { + fun gradleConfigurationListener(): BuildListener { return BuildConfigurationFailureListener( createWriter( - outputDir = outputDir, + outputDir = extension.outputDir, logger = logger ) ) } fun gradleBuildFinishedListener( - graph: TaskExecutionGraph, - outputDir: Provider, - logger: CILogger + graph: TaskExecutionGraph ): BuildListener = BuildExecutionFailureListener( graph = graph, logs = logs, - writer = createWriter(outputDir, logger) + verdicts = verdicts, + writer = createWriter(extension.outputDir, logger) ) private fun createWriter( diff --git a/subprojects/gradle/build-verdict/src/main/kotlin/com/avito/android/build_verdict/internal/TaskExecutionErrorsCapture.kt b/subprojects/gradle/build-verdict/src/main/kotlin/com/avito/android/build_verdict/internal/TaskExecutionErrorsCapture.kt deleted file mode 100644 index 053fc64b7a..0000000000 --- a/subprojects/gradle/build-verdict/src/main/kotlin/com/avito/android/build_verdict/internal/TaskExecutionErrorsCapture.kt +++ /dev/null @@ -1,37 +0,0 @@ -package com.avito.android.build_verdict.internal - -import com.avito.android.build_verdict.BuildVerdictTask -import com.avito.android.build_verdict.internal.task.lifecycle.TaskLifecycleListener -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 -import org.gradle.api.tasks.testing.Test - -internal class TaskExecutionErrorsCapture( - private val testLifecycle: TaskLifecycleListener, - private val buildVerdictLifecycle: TaskLifecycleListener, - private val defaultLifecycle: TaskLifecycleListener -) : TaskExecutionListener, BuildAdapter() { - - override fun beforeExecute(task: Task) { - when (task) { - is Test -> testLifecycle.beforeExecute(task) - is BuildVerdictTask -> buildVerdictLifecycle.beforeExecute(task) - else -> defaultLifecycle.beforeExecute(task) - } - } - - override fun afterExecute(task: Task, state: TaskState) { - when (task) { - is Test -> testLifecycle.afterExecute(task, state.failure) - is BuildVerdictTask -> buildVerdictLifecycle.afterExecute(task, state.failure) - else -> defaultLifecycle.afterExecute(task, state.failure) - } - } - - override fun buildFinished(result: BuildResult) { - result.gradle?.removeListener(this) - } -} diff --git a/subprojects/gradle/build-verdict/src/main/kotlin/com/avito/android/build_verdict/internal/task/lifecycle/BuildVerdictTaskLifecycleListener.kt b/subprojects/gradle/build-verdict/src/main/kotlin/com/avito/android/build_verdict/internal/task/lifecycle/BuildVerdictTaskLifecycleListener.kt index b66a4ed985..da0b4a8e58 100644 --- a/subprojects/gradle/build-verdict/src/main/kotlin/com/avito/android/build_verdict/internal/task/lifecycle/BuildVerdictTaskLifecycleListener.kt +++ b/subprojects/gradle/build-verdict/src/main/kotlin/com/avito/android/build_verdict/internal/task/lifecycle/BuildVerdictTaskLifecycleListener.kt @@ -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 +internal class BuildVerdictTaskLifecycleListener( + private val verdicts: MutableMap ) : TaskLifecycleListener() { - override fun beforeExecute(task: BuildVerdictTask) { - // empty - } + override val acceptedTask: Class = 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 - } } diff --git a/subprojects/gradle/build-verdict/src/main/kotlin/com/avito/android/build_verdict/internal/task/lifecycle/DefaultTaskLifecycleListener.kt b/subprojects/gradle/build-verdict/src/main/kotlin/com/avito/android/build_verdict/internal/task/lifecycle/DefaultTaskLifecycleListener.kt index 14de3ea3f0..5a4a390bbb 100644 --- a/subprojects/gradle/build-verdict/src/main/kotlin/com/avito/android/build_verdict/internal/task/lifecycle/DefaultTaskLifecycleListener.kt +++ b/subprojects/gradle/build-verdict/src/main/kotlin/com/avito/android/build_verdict/internal/task/lifecycle/DefaultTaskLifecycleListener.kt @@ -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, +internal class DefaultTaskLifecycleListener( + private val logs: MutableMap, private val listeners: MutableMap ) : TaskLifecycleListener() { - override fun beforeExecute(task: Task) { + override val acceptedTask: Class = Task::class.java + + override fun beforeExecuteTyped(task: Task) { val id = CurrentBuildOperationRef.instance().id if (id != null) { val path = Path.path(task.path) @@ -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() } diff --git a/subprojects/gradle/build-verdict/src/main/kotlin/com/avito/android/build_verdict/internal/task/lifecycle/TaskExecutionListenerBridge.kt b/subprojects/gradle/build-verdict/src/main/kotlin/com/avito/android/build_verdict/internal/task/lifecycle/TaskExecutionListenerBridge.kt new file mode 100644 index 0000000000..a5b87e4941 --- /dev/null +++ b/subprojects/gradle/build-verdict/src/main/kotlin/com/avito/android/build_verdict/internal/task/lifecycle/TaskExecutionListenerBridge.kt @@ -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> +) : 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) + } +} diff --git a/subprojects/gradle/build-verdict/src/main/kotlin/com/avito/android/build_verdict/internal/task/lifecycle/TaskLifecycleListener.kt b/subprojects/gradle/build-verdict/src/main/kotlin/com/avito/android/build_verdict/internal/task/lifecycle/TaskLifecycleListener.kt index b6b5f3332a..69c30d9be0 100644 --- a/subprojects/gradle/build-verdict/src/main/kotlin/com/avito/android/build_verdict/internal/task/lifecycle/TaskLifecycleListener.kt +++ b/subprojects/gradle/build-verdict/src/main/kotlin/com/avito/android/build_verdict/internal/task/lifecycle/TaskLifecycleListener.kt @@ -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 { +internal abstract class TaskLifecycleListener { - protected abstract val logs: MutableMap + abstract val acceptedTask: Class - 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) + } } } } diff --git a/subprojects/gradle/build-verdict/src/main/kotlin/com/avito/android/build_verdict/internal/task/lifecycle/TestTaskLifecycleListener.kt b/subprojects/gradle/build-verdict/src/main/kotlin/com/avito/android/build_verdict/internal/task/lifecycle/TestTaskLifecycleListener.kt index 4ad36818ce..2cc06853cb 100644 --- a/subprojects/gradle/build-verdict/src/main/kotlin/com/avito/android/build_verdict/internal/task/lifecycle/TestTaskLifecycleListener.kt +++ b/subprojects/gradle/build-verdict/src/main/kotlin/com/avito/android/build_verdict/internal/task/lifecycle/TestTaskLifecycleListener.kt @@ -7,28 +7,22 @@ import org.gradle.api.tasks.testing.TestDescriptor import org.gradle.api.tasks.testing.TestResult import org.gradle.util.Path -class TestTaskLifecycleListener( - override val logs: MutableMap +internal class TestTaskLifecycleListener( + private val verdicts: MutableMap ) : TaskLifecycleListener() { - override fun beforeExecute(task: Test) { + override val acceptedTask: Class = Test::class.java + + override fun beforeExecuteTyped(task: Test) { task.addTestListener( object : DefaultTestListener() { override fun afterTest(testDescriptor: TestDescriptor, result: TestResult) { if (result.resultType == TestResult.ResultType.FAILURE) { - logs.getOrPut(Path.path(task.path), { LogsTextBuilder("FAILED tests:") }) + verdicts.getOrPut(Path.path(task.path), { LogsTextBuilder("FAILED tests:") }) .addLine("\t${testDescriptor.className}.${testDescriptor.displayName}") } } } ) } - - override fun afterSucceedExecute(task: Test) { - // empty - } - - override fun afterFailedExecute(task: Test) { - // empty - } } diff --git a/subprojects/gradle/build-verdict/src/main/kotlin/com/avito/android/build_verdict/internal/task/lifecycle/UserDefinedVerdictProducerTaskLifecycleListener.kt b/subprojects/gradle/build-verdict/src/main/kotlin/com/avito/android/build_verdict/internal/task/lifecycle/UserDefinedVerdictProducerTaskLifecycleListener.kt new file mode 100644 index 0000000000..1aca074814 --- /dev/null +++ b/subprojects/gradle/build-verdict/src/main/kotlin/com/avito/android/build_verdict/internal/task/lifecycle/UserDefinedVerdictProducerTaskLifecycleListener.kt @@ -0,0 +1,24 @@ +package com.avito.android.build_verdict.internal.task.lifecycle + +import com.avito.android.build_verdict.UserDefinedTaskVerdictProducer +import com.avito.android.build_verdict.internal.LogsTextBuilder +import org.gradle.api.Task +import org.gradle.util.Path + +internal class UserDefinedVerdictProducerTaskLifecycleListener( + private val taskVerdictProducers: Lazy>, + private val verdicts: MutableMap +) : TaskLifecycleListener() { + override val acceptedTask: Class = Task::class.java + + override fun afterFailedExecute(task: Task, error: Throwable) { + taskVerdictProducers + .value + .filter { it.accept(task) } + .map { it.produce(task) } + .forEach { verdict -> + verdicts.getOrPut(Path.path(task.path)) { LogsTextBuilder() } + .addLine(verdict) + } + } +} diff --git a/subprojects/gradle/build-verdict/src/main/kotlin/com/avito/android/build_verdict/internal/writer/BuildVerdictExecutionPlainText.kt b/subprojects/gradle/build-verdict/src/main/kotlin/com/avito/android/build_verdict/internal/writer/BuildVerdictExecutionPlainText.kt index 2424ab5482..a6f0864d74 100644 --- a/subprojects/gradle/build-verdict/src/main/kotlin/com/avito/android/build_verdict/internal/writer/BuildVerdictExecutionPlainText.kt +++ b/subprojects/gradle/build-verdict/src/main/kotlin/com/avito/android/build_verdict/internal/writer/BuildVerdictExecutionPlainText.kt @@ -24,8 +24,13 @@ internal fun BuildVerdict.Execution.plainText(): String { private fun FailedTask.plainText() = buildString { appendln(error.plainText().trimIndent()) appendln() + if (verdict != null) { + appendln("* Task result:") + appendln(verdict.trimIndent()) + appendln() + } appendln("* Error logs:") - appendln(errorOutput.trimIndent()) + appendln(errorLogs.trimIndent()) } private fun Error.plainText() = buildString { diff --git a/subprojects/gradle/build-verdict/src/test/kotlin/com/avito/android/build_verdict/BaseBuildVerdictTest.kt b/subprojects/gradle/build-verdict/src/test/kotlin/com/avito/android/build_verdict/BaseBuildVerdictTest.kt index 5b1ca6fb50..9fdc6a287f 100644 --- a/subprojects/gradle/build-verdict/src/test/kotlin/com/avito/android/build_verdict/BaseBuildVerdictTest.kt +++ b/subprojects/gradle/build-verdict/src/test/kotlin/com/avito/android/build_verdict/BaseBuildVerdictTest.kt @@ -49,11 +49,13 @@ abstract class BaseBuildVerdictTest { protected fun generateProject( module: Module = AndroidAppModule( name = appName - ) + ), + buildGradleExtra: String = "" ) { TestProjectGenerator( plugins = listOf("com.avito.android.build-verdict"), - modules = listOf(module) + modules = listOf(module), + buildGradleExtra = buildGradleExtra ).generateIn(temp) } diff --git a/subprojects/gradle/build-verdict/src/test/kotlin/com/avito/android/build_verdict/BuildVerdictPluginExecutionPhaseTest.kt b/subprojects/gradle/build-verdict/src/test/kotlin/com/avito/android/build_verdict/BuildVerdictPluginExecutionPhaseTest.kt index cdfbc0a878..2950d66584 100644 --- a/subprojects/gradle/build-verdict/src/test/kotlin/com/avito/android/build_verdict/BuildVerdictPluginExecutionPhaseTest.kt +++ b/subprojects/gradle/build-verdict/src/test/kotlin/com/avito/android/build_verdict/BuildVerdictPluginExecutionPhaseTest.kt @@ -28,7 +28,7 @@ class BuildVerdictPluginExecutionPhaseTest : BaseBuildVerdictTest() { assertBuildVerdict( failedTask = "compileDebugKotlin", plainTextVerdict = compileFails(temp), - expectedErrorLines = listOf( + expectedErrorLogs = listOf( "$temp/app/src/main/kotlin/Uncompiled.kt: (1, 1): Expecting a top level declaration", "$temp/app/src/main/kotlin/Uncompiled.kt: (1, 11): Expecting a top level declaration" ) @@ -56,7 +56,7 @@ class BuildVerdictPluginExecutionPhaseTest : BaseBuildVerdictTest() { assertBuildVerdict( failedTask = "kaptGenerateStubsDebugKotlin", plainTextVerdict = kaptStubGeneratingFails(temp), - expectedErrorLines = listOf( + expectedErrorLogs = listOf( "$temp/app/src/main/kotlin/Uncompiled.kt: (1, 1): Expecting a top level declaration", "$temp/app/src/main/kotlin/Uncompiled.kt: (1, 11): Expecting a top level declaration" ) @@ -99,7 +99,7 @@ class BuildVerdictPluginExecutionPhaseTest : BaseBuildVerdictTest() { assertBuildVerdict( failedTask = "kaptDebugKotlin", plainTextVerdict = kaptFails(temp), - expectedErrorLines = listOf( + expectedErrorLogs = listOf( "$temp/app/build/tmp/kapt3/stubs/debug/DaggerComponent.java:6: error: [Dagger/MissingBinding] CoffeeMaker cannot be provided without an @Inject constructor or an @Provides-annotated method.", "public abstract interface DaggerComponent {", " ^", @@ -151,10 +151,8 @@ class BuildVerdictPluginExecutionPhaseTest : BaseBuildVerdictTest() { assertBuildVerdict( failedTask = "testDebugUnitTest", plainTextVerdict = unitTestsFails(temp), - expectedErrorLines = listOf( - "FAILED tests:", - "\tAppTest.test assert true", - "\tAppTest.test runtime exception" + expectedErrorLogs = listOf( + "No error logs" ) ) } @@ -202,7 +200,14 @@ class BuildVerdictPluginExecutionPhaseTest : BaseBuildVerdictTest() { tasks.register("customTask", CustomTask) """.trimIndent() - ) + ), + buildGradleExtra = """ +import com.avito.android.build_verdict.TaskVerdictProducer +import org.gradle.api.Task +buildVerdict { + onTaskFailure("customTask", { "User added verdict" } as TaskVerdictProducer) +} +""".trimIndent() ) val result = gradlew( @@ -218,8 +223,8 @@ class BuildVerdictPluginExecutionPhaseTest : BaseBuildVerdictTest() { assertBuildVerdict( failedTask = "customTask", plainTextVerdict = customTaskFails, - expectedErrorLines = listOf( - "Custom verdict" + expectedErrorLogs = listOf( + "No error logs" ) ) } @@ -228,7 +233,7 @@ class BuildVerdictPluginExecutionPhaseTest : BaseBuildVerdictTest() { failedApp: String = appName, failedTask: String, plainTextVerdict: String, - expectedErrorLines: List + expectedErrorLogs: List ) { assertBuildVerdictFileExist(true) assertThat(plainTextBuildVerdict.readText()).isEqualTo(plainTextVerdict) @@ -246,14 +251,14 @@ class BuildVerdictPluginExecutionPhaseTest : BaseBuildVerdictTest() { assertThat(task.projectPath) .isEqualTo(":$failedApp") - val actualErrorLines = task.errorOutput.lines() + val actualErrorLines = task.errorLogs.lines() assertThat(actualErrorLines) - .hasSize(expectedErrorLines.size) + .hasSize(expectedErrorLogs.size) actualErrorLines.forEachIndexed { index, actualErrorLine -> assertThat(actualErrorLine) - .contains(expectedErrorLines[index]) + .contains(expectedErrorLogs[index]) } } } diff --git a/subprojects/gradle/build-verdict/src/test/kotlin/com/avito/android/build_verdict/PlainTextVerdicts.kt b/subprojects/gradle/build-verdict/src/test/kotlin/com/avito/android/build_verdict/PlainTextVerdicts.kt index 6a545ae4d0..b8d722a715 100644 --- a/subprojects/gradle/build-verdict/src/test/kotlin/com/avito/android/build_verdict/PlainTextVerdicts.kt +++ b/subprojects/gradle/build-verdict/src/test/kotlin/com/avito/android/build_verdict/PlainTextVerdicts.kt @@ -18,10 +18,13 @@ fun unitTestsFails(dir: File) = """ Execution failed for task ':app:testDebugUnitTest'. > There were failing tests. See the report at: file://${dir.canonicalPath}/app/build/reports/tests/testDebugUnitTest/index.html -* Error logs: +* Task result: FAILED tests: AppTest.test assert true AppTest.test runtime exception + +* Error logs: +No error logs """.trimIndent() fun kaptFails(dir: File) = """ @@ -44,8 +47,12 @@ val customTaskFails = """ Execution failed for task ':app:customTask'. > Surprise -* Error logs: +* Task result: Custom verdict +User added verdict + +* Error logs: +No error logs """.trimIndent() fun compileFails(dir: File) = """ diff --git a/subprojects/gradle/ci-logger/src/main/kotlin/com/avito/utils/logging/CILoggingDestination.kt b/subprojects/gradle/ci-logger/src/main/kotlin/com/avito/utils/logging/CILoggingDestination.kt index 3654273114..f3f1aa39f0 100644 --- a/subprojects/gradle/ci-logger/src/main/kotlin/com/avito/utils/logging/CILoggingDestination.kt +++ b/subprojects/gradle/ci-logger/src/main/kotlin/com/avito/utils/logging/CILoggingDestination.kt @@ -9,6 +9,16 @@ interface CILoggingDestination : Serializable { fun child(tag: String): CILoggingDestination } +internal object StderrDestination : CILoggingDestination { + + override fun write(message: String, throwable: Throwable?) { + System.err.println(message) + throwable?.also { System.err.println(it) } + } + + override fun child(tag: String): CILoggingDestination = this +} + internal object StdoutDestination : CILoggingDestination { override fun write(message: String, throwable: Throwable?) { diff --git a/subprojects/gradle/ci-logger/src/main/kotlin/com/avito/utils/logging/Gradle.kt b/subprojects/gradle/ci-logger/src/main/kotlin/com/avito/utils/logging/Gradle.kt index 5d602ac292..02dd48797f 100644 --- a/subprojects/gradle/ci-logger/src/main/kotlin/com/avito/utils/logging/Gradle.kt +++ b/subprojects/gradle/ci-logger/src/main/kotlin/com/avito/utils/logging/Gradle.kt @@ -66,6 +66,11 @@ private fun defaultCILogger( destination = StdoutDestination ) + val explicitStderrHandler = CILoggingHandlerImplementation( + formatter = AppendPrefixFormatter(prefix = name), + destination = StderrDestination + ) + val sentryConfig = project.sentryConfig val sentryHandler = CILoggingHandlerImplementation( @@ -101,7 +106,7 @@ private fun defaultCILogger( ), criticalHandler = CILoggingCombinedHandler( handlers = listOf( - explicitStdoutHandler, + explicitStderrHandler, destinationFileHandler, elasticHandler(elasticClient, tag = name, level = "ERROR"), sentryHandler