Skip to content

Commit

Permalink
Mbs 9936 lint crashes in report (#634)
Browse files Browse the repository at this point in the history
* MBS-9936: separate channel for lint bug reports
  • Loading branch information
dsvoronin committed Nov 16, 2020
1 parent ebfeff0 commit 4076f5e
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,17 @@ internal class LintSlackAlertIntegrationTest {
lintSlackReporter.report(
lintReport = reportModels,
channel = testChannel,
channelForLintBugs = testChannel,
buildUrl = "https://stubbuildurl".toHttpUrl()
)
}
}

inline fun <reified C> fileFromJarResources(name: String) = File(C::class.java.classLoader.getResource(name).file)
inline fun <reified C> fileFromJarResources(name: String): File {
val file = C::class.java.classLoader
?.getResource(name)
?.file
?.let { File(it) }

return requireNotNull(file) { "$name not found in resources" }
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ open class LintReportExtension @Inject constructor(objects: ObjectFactory) {
//todo some global slack settings?
val slackToken = objects.property<String>()
val slackWorkspace = objects.property<String>()
val slackChannelToReportLintBugs = objects.property<String>()
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,9 @@ class LintIssue(
val severity: Severity
) {
enum class Severity { UNKNOWN, WARNING, ERROR }

/**
* "lint failed to parse file" type of errors
*/
val isFatal: Boolean = id == "LintError"
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@ class LintReportToSlackTaskFactory(
private val androidLintAccessor: AndroidLintAccessor = AndroidLintAccessor(project)
) {

private val extension: LintReportExtension by lazy {
project.extensions.getByType<LintReportExtension>()
}

@Suppress("UnstableApiUsage")
private val slackClientProvider: Provider<SlackClient> by lazy {
val extension = project.extensions.getByType<LintReportExtension>()

extension.slackToken.zip(extension.slackWorkspace) { token, workspace ->
SlackClient.Impl(
token = token,
Expand Down Expand Up @@ -53,6 +55,7 @@ class LintReportToSlackTaskFactory(
}

slackReportChannel.set(slackChannel)
slackChannelForLintBugs.set(extension.slackChannelToReportLintBugs)

slackClient.set(slackClientProvider)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ abstract class LintSlackReportTask : DefaultTask() {
@get:Input
abstract val slackReportChannel: Property<String>

@get:Input
abstract val slackChannelForLintBugs: Property<String>

@get:InputFile
abstract val lintXml: RegularFileProperty

Expand All @@ -40,6 +43,7 @@ abstract class LintSlackReportTask : DefaultTask() {
createLintSlackAlert().report(
lintReport = models,
channel = SlackChannel(slackReportChannel.get()),
channelForLintBugs = SlackChannel(slackChannelForLintBugs.get()),
buildUrl = teamcityBuildLinkAccessor.getBuildUrl()
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ import com.avito.slack.SlackClient
import com.avito.slack.model.SlackChannel
import com.avito.utils.logging.CILogger
import okhttp3.HttpUrl
import java.io.File

interface LintSlackReporter {

fun report(
lintReport: LintReportModel,
channel: SlackChannel,
channelForLintBugs: SlackChannel,
buildUrl: HttpUrl
)

Expand All @@ -25,43 +27,112 @@ interface LintSlackReporter {
override fun report(
lintReport: LintReportModel,
channel: SlackChannel,
channelForLintBugs: SlackChannel,
buildUrl: HttpUrl
) {
if (shouldSendAlert(lintReport)) {
logger.debug("$tag: Sending lint alert...")

slackClient.uploadHtml(
channel = channel,
message = buildSlackMessage(lintReport, buildUrl),
file = lintReport.htmlFile
).fold(
{ logger.debug("$tag: Report sent successfully") },
{ error -> logger.critical("$tag: Can't send report ${error.message}") }
)
} else {
logger.debug("$tag Skip sending lint alert")
when (lintReport) {
is LintReportModel.Valid -> {
val (errors, everythingElse) = lintReport.issues.partition { it.severity == LintIssue.Severity.ERROR }
val (warnings, unknowns) = everythingElse.partition { it.severity == LintIssue.Severity.WARNING }
val (fatalErrors, regularErrors) = errors.partition { it.isFatal }

var isMessageSent = false

if (regularErrors.isNotEmpty()) {
sendReport(
channel = channel,
message = buildSlackMessage(
projectPath = lintReport.projectRelativePath,
errors = regularErrors,
warnings = warnings,
buildUrl = buildUrl
),
htmlReport = lintReport.htmlFile
)

isMessageSent = true
}

if (fatalErrors.isNotEmpty() || unknowns.isNotEmpty()) {
sendReport(
channel = channelForLintBugs,
message = buildSlackMessageAboutLintBugs(
projectPath = lintReport.projectRelativePath,
fatalErrors = fatalErrors,
unknownErrors = unknowns,
buildUrl = buildUrl
),
htmlReport = lintReport.htmlFile
)

isMessageSent = true
}

if (!isMessageSent) {
logger.debug("$tag Not sending any reports")
}
}
is LintReportModel.Invalid -> {
logger.critical("$tag Not sending report: can't parse", lintReport.error)
}
}
}

private fun shouldSendAlert(model: LintReportModel): Boolean {
return (model is LintReportModel.Valid) && model.issues.any { it.severity == LintIssue.Severity.ERROR }
private fun sendReport(
channel: SlackChannel,
message: String,
htmlReport: File
) {
slackClient.uploadHtml(
channel = channel,
message = message,
file = htmlReport
).fold(
{ logger.debug("$tag: Report sent successfully to $channel") },
{ error -> logger.critical("$tag: Can't send report to $channel", error) }
)
}

private fun buildSlackMessage(model: LintReportModel, buildUrl: HttpUrl): String {
private fun buildSlackMessage(
projectPath: String,
errors: List<LintIssue>,
warnings: List<LintIssue>,
buildUrl: HttpUrl
): String {
return buildString {
appendln("*Critical lint problems detected for project ${model.projectRelativePath}*")
appendln("*Critical lint problems detected for project ${projectPath}*")
appendln("Build: <$buildUrl|link>")
appendln()

if (model is LintReportModel.Valid) {
val errors = model.issues.filter { it.severity == LintIssue.Severity.ERROR }
val groupedErrors = errors.groupBy { it.summary }
groupedErrors.forEach { (summary, issue) ->
appendln(":red_circle: [${issue.size}x] $summary")
}
appendln(":warning: also ${model.issues.count { it.severity == LintIssue.Severity.WARNING }} warnings")
val groupedErrors = errors.groupBy { it.summary }
groupedErrors.forEach { (summary, issue) ->
appendln(":red_circle: [${issue.size}x] $summary")
}
appendln()
if (warnings.isEmpty()) {
appendln(":green_flag: No warnings!")
} else {
logger.critical("LintSlackAlerter: There is a problem with report: ${model.htmlFile.path}")
appendln(":warning: also ${warnings.count()} warnings")
}
}
}

private fun buildSlackMessageAboutLintBugs(
projectPath: String,
fatalErrors: List<LintIssue>,
unknownErrors: List<LintIssue>,
buildUrl: HttpUrl
): String {
return buildString {
appendln("*Lint encountered a problem on project $projectPath*")
appendln("Build: <$buildUrl|link>")
appendln()

if (fatalErrors.isNotEmpty()) {
appendln(":skull: [${fatalErrors.size}] Lint fatal errors")
}
if (unknownErrors.isNotEmpty()) {
appendln(":alien: [${unknownErrors.size}] Unknown type issues")
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrl
import org.gradle.api.Project

interface TeamcityBuildLinkAccessor {
internal interface TeamcityBuildLinkAccessor {

fun getBuildUrl(): HttpUrl

Expand Down

0 comments on commit 4076f5e

Please sign in to comment.