Merge branch 'feature/add_static_analysis_gradle' into 'master'

add staticAnalysis gradle

See merge request touchinstinct/BuildScripts!4
This commit is contained in:
Roman Pelmegov 2023-03-23 15:18:33 +00:00
commit 23c1180b3b
1 changed files with 243 additions and 0 deletions

View File

@ -0,0 +1,243 @@
import groovy.transform.InheritConstructors
import groovy.transform.ToString
import groovy.transform.TupleConstructor
import groovy.xml.MarkupBuilder
apply plugin: 'io.gitlab.arturbosch.detekt'
task staticAnalysis {
dependsOn 'cleanAnalyzerReports'
if (static_analyzer_enable_detekt) {
dependsOn ':detekt'
}
if (static_analyzer_enable_lint) {
dependsOn ':app:lint'
}
doLast {
def reportDir = new File(static_analyzer_report_dir)
def detektErrors = collectDetektCodeAnalyzerErrors(reportDir)
def lintErrors = collectLintCodeAnalyzerErrors(reportDir)
def totalReportFile = new File(reportDir, 'total_report.html')
totalReportFile << generateReportHtml(detektErrors, lintErrors)
println("|----------------------static_analyzer--------------------------------")
println("| static_analyzer_fail_on_lint_error : $static_analyzer_fail_on_lint_error")
println("| static_analyzer_fail_on_detect_error : $static_analyzer_fail_on_detect_error")
println("|")
println("| total_detekt_errors : ${detektErrors.size()}" + (static_analyzer_enable_detekt ? "" : " [DISABLED]"))
println("| total_lint_errors : ${lintErrors.size()}" + (static_analyzer_enable_lint ? "" : " [DISABLED]"))
println("| total_errors : ${detektErrors.size() + lintErrors.size()}")
println("|")
println("| Successfully generated HtmlTotalReport at ${totalReportFile.path}")
println("|-------------------------------------------------------------------")
if (static_analyzer_fail_on_lint_error || static_analyzer_fail_on_detect_error) {
def message = ""
def hasLintError = static_analyzer_fail_on_lint_error && !lintErrors.isEmpty()
def hasDetektError = static_analyzer_fail_on_detect_error && !detektErrors.isEmpty()
if (hasLintError) {
message += "There is ${lintErrors.size()} lint errors; "
}
if (hasDetektError) {
message += "There is ${detektErrors.size()} detekt errors; "
}
if (hasLintError || hasDetektError) {
message += "\n See report at ${totalReportFile.path}"
throw new StaticAnalysisException(message)
}
}
}
}
detekt {
debug = true
//filters = ".*test.*,.*androidTest.*,.*//*resources/.*,.*//*build/.*"
input = files("$rootDir/app/src/main/kotlin", "$rootDir/data/src/main/kotlin", "$rootDir/domain/src/main/kotlin")
config = files("$static_analyzer_config_dir/detekt-config.yml")
reports {
xml {
enabled = true
destination = file("$static_analyzer_report_dir/report_detekt.xml")
}
html {
enabled = false
}
}
}
task cleanAnalyzerReports(type: Delete) {
delete static_analyzer_report_dir
}
static def collectDetektCodeAnalyzerErrors(File reportDir) {
def detektReport = new File("$reportDir/report_detekt.xml")
if (detektReport == null) {
println("can't find detekt report")
return Collections.emptyList()
}
return new XmlSlurper()
.parse(detektReport)
.children()
.inject(new ArrayList<CodeAnalyzerError>()) { acc, file ->
println(file)
def fileName = file.@name
file.children().each { error ->
acc.add(
new CodeAnalyzerError(
position: "$fileName:${error.@line ?: "-"}:${error.@column ?: "-"}",
severity: error.@severity,
message: error.@message,
source: error.@source
)
)
}
acc
}
.sort { error -> error.position }
}
static def collectLintCodeAnalyzerErrors(File reportDir) {
def lintReports = reportDir.listFiles().findAll { file -> file.name.matches("^lint.*xml\$") }
if (lintReports == null || lintReports.isEmpty()) {
println("can't find any lint report")
return Collections.emptyList()
}
return lintReports.inject(new ArrayList<CodeAnalyzerError>()) { acc, report ->
acc.addAll(parseLintReport(report))
acc
}.sort { error -> error.position }
}
static def parseLintReport(File report) {
return new XmlSlurper()
.parse(report)
.children()
.inject(new ArrayList<CodeAnalyzerError>()) { acc, issue ->
def fileName = issue.location.@file
def line = issue.location.@line ?: "-"
def column = issue.location.@column ?: "-"
acc.add(
new CodeAnalyzerError(
position: "$fileName:$line:$column",
severity: issue.@severity,
message: issue.@message,
source: issue.@id
)
)
acc
}
}
MarkupBuilder.metaClass.appendErrorsTable { errors ->
delegate.table {
tr {
th(["class": "Position"], "Position")
th(["class": "Severity"], "Severity")
th(["class": "Source"], "Source")
th(["class": "Message"], "Message")
}
errors.forEach { error ->
tr {
td(error.position)
td(error.severity.toLowerCase())
td(error.source)
td(error.message)
}
}
}
}
static
def generateReportHtml(List<CodeAnalyzerError> detektErrors, List<CodeAnalyzerError> lintErrors) {
def sb = new StringWriter()
def html = new MarkupBuilder(sb)
html.doubleQuotes = true
html.expandEmptyElements = true
html.omitEmptyAttributes = false
html.omitNullAttributes = false
html.html(lang: 'en') {
head {
meta('http-equiv': '"Content-Type" content="text/html; charset=utf-8"')
title('Static analyzer report')
mkp.yieldUnescaped(getErrorTableStyle())
}
body {
println("class: " + owner)
h2("Detekt errors: ${detektErrors.size()}")
appendErrorsTable(detektErrors)
h2("Lint errors: ${lintErrors.size()}")
appendErrorsTable(lintErrors)
}
}
return sb.toString()
}
static def getErrorTableStyle() {
return """
<style>
h2 { margin-top: 50px; }
table {
border-collapse: collapse;
border-spacing: 0;
width: 100%;
table-layout: fixed;
border: 1px solid #ddd;
}
th, td {
text-align: left;
padding: 8px;
word-wrap: break-word;
}
tr:nth-child(even) { background-color: #f2f2f2; }
.Position { width: 40%; }
.Severity { width: 10%; }
.Source { width: 15%; }
.Message { width: 35%; }
</style>
"""
}
@ToString
@TupleConstructor
class CodeAnalyzerError {
String position, severity, message, source
}
@InheritConstructors
class StaticAnalysisException extends GradleException {}