Skip to content

Commit

Permalink
Report lint issues (#372)
Browse files Browse the repository at this point in the history
* Add missing lint plugin

* Fix broken test

* Introduce module metadata

* Introduce module metadata

* Introduce back lint

* Disable lint unless ran explicitly

* Add basic LintReport plugin with printing stuff

* Try to add Lint to CI

* Proper name to Lint job

* Add proper analytics reporting

* Add Lint report to scheduled job

* Add Lint report to scheduled job

* Add Lint report to check job to test End to End

* Fix caching, remove lint report from pipeline and add reporting message
  • Loading branch information
jraska committed Dec 13, 2020
1 parent 5f58340 commit 3e2a4a0
Show file tree
Hide file tree
Showing 22 changed files with 561 additions and 43 deletions.
32 changes: 27 additions & 5 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ jobs:
steps:
- checkout
- restore_cache:
key: jars-{{ checksum "build.gradle" }}-{{ checksum "app/build.gradle" }}-{{ checksum "core/build.gradle" }}-{{ checksum "core-android-api/build.gradle" }}
key: cache-{{ checksum "build.gradle" }}-{{ checksum "dependencies.gradle" }}-{{ checksum "app/build.gradle" }}
- run:
name: Get Dependencies
command: ./gradlew androidDependencies --max-workers=2 --stacktrace
- save_cache:
paths:
- ~/.gradle
key: jars-{{ checksum "build.gradle" }}-{{ checksum "app/build.gradle" }}-{{ checksum "core/build.gradle" }}-{{ checksum "core-android-api/build.gradle" }}
key: cache-{{ checksum "build.gradle" }}-{{ checksum "dependencies.gradle" }}-{{ checksum "app/build.gradle" }}
- run:
name: Run Tests
command: ./gradlew check --max-workers=2 --stacktrace
Expand All @@ -35,16 +35,37 @@ jobs:
- run:
name: Execute Data Processing
command: ./gradlew reportModuleStatistics --max-workers=2 --stacktrace --no-configure-on-demand
- save_cache:
paths:
- ~/.gradle
lint:
environment:
JVM_OPTS: -Xmx4096m
docker:
- image: circleci/android:api-30
steps:
- checkout
- restore_cache:
key: cache-{{ checksum "build.gradle" }}-{{ checksum "dependencies.gradle" }}-{{ checksum "app/build.gradle" }}
- run:
name: Run Lint
command: ./gradlew lint --max-workers=2 --stacktrace
lintReport:
environment:
JVM_OPTS: -Xmx4096m
docker:
- image: circleci/android:api-30
steps:
- checkout
- restore_cache:
key: cache-{{ checksum "build.gradle" }}-{{ checksum "dependencies.gradle" }}-{{ checksum "app/build.gradle" }}
- run:
name: Run Lint Report
command: ./gradlew lintStatisticsReport --max-workers=2 --stacktrace

workflows:
version: 2
check:
jobs:
- build
- lint
statisticsReport:
triggers:
- schedule:
Expand All @@ -55,4 +76,5 @@ workflows:
- master
jobs:
- moduleStatisticsReport
- lintReport

1 change: 1 addition & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ plugins {
id 'com.jraska.gradle.buildtime'
id 'com.jraska.github.client.release'
id 'com.jraska.module.stats'
id 'com.jraska.module.lint.report'
}

apply plugin: 'com.android.application'
Expand Down
10 changes: 6 additions & 4 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:4.1.1'
classpath ('com.google.firebase:firebase-plugins:2.0.0') {
classpath('com.google.firebase:firebase-plugins:2.0.0') {
exclude group: 'com.google.api-client', module: 'google-api-client' // conflicts with com.github.triplet.play
}
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.4.1'
Expand Down Expand Up @@ -45,9 +45,11 @@ subprojects {
}

subprojects {
tasks.configureEach {
if (name.startsWith("lint")) {
enabled = false
if (!gradle.startParameter.taskNames.any { it.contains("lint") }) {
tasks.configureEach {
if (name.startsWith("lint")) {
enabled = false
}
}
}
}
Expand Down
1 change: 1 addition & 0 deletions core-api/build.gradle
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
apply plugin: 'java-library'
apply plugin: 'com.android.lint'
apply plugin: 'kotlin'

dependencies {
Expand Down
1 change: 1 addition & 0 deletions core-testing/build.gradle
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
apply plugin: 'java-library'
apply plugin: 'com.android.lint'
apply plugin: 'kotlin'
apply plugin: 'kotlin-kapt'

Expand Down
1 change: 1 addition & 0 deletions feature/config-debug-api/build.gradle
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
apply plugin: 'java-library'
apply plugin: 'com.android.lint'
apply plugin: 'kotlin'

dependencies {
Expand Down
1 change: 1 addition & 0 deletions feature/identity-api/build.gradle
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
apply plugin: 'java-library'
apply plugin: 'com.android.lint'
apply plugin: 'kotlin'

dependencies {
Expand Down
1 change: 1 addition & 0 deletions feature/identity/build.gradle
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
apply plugin: 'java-library'
apply plugin: 'com.android.lint'
apply plugin: 'kotlin'
apply plugin: 'kotlin-kapt'

Expand Down
1 change: 1 addition & 0 deletions feature/navigation-deeplink/build.gradle
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
apply plugin: 'java-library'
apply plugin: 'com.android.lint'
apply plugin: 'kotlin'
apply plugin: 'kotlin-kapt'

Expand Down
4 changes: 4 additions & 0 deletions plugins/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,9 @@ gradlePlugin {
id = 'com.jraska.module.stats'
implementationClass = 'com.jraska.module.ModuleStatsPlugin'
}
lintReportPlugin {
id = 'com.jraska.module.lint.report'
implementationClass = 'com.jraska.lint.LintReporterPlugin'
}
}
}
77 changes: 77 additions & 0 deletions plugins/src/main/java/com/jraska/lint/LintAnalyticsReporter.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.jraska.lint

import com.jraska.analytics.AnalyticsEvent
import com.jraska.analytics.AnalyticsReporter

class LintAnalyticsReporter(
private val analyticsReporter: AnalyticsReporter
) {
fun report(projectResult: LintProjectResult) {
val allEvents = mutableListOf(convertProjectResult(projectResult))

allEvents.addAll(
projectResult.moduleResults
.flatMap { convertModule(it) }
)

analyticsReporter.report(*allEvents.toTypedArray())

println("$GRAPH_ICON Lint results reported $GRAPH_ICON")
}

private fun convertModule(moduleResult: LintModuleResult): List<AnalyticsEvent> {
val moduleEvents = moduleResult.issues.map { convertIssue(it) }.toMutableList()

moduleEvents.add(
AnalyticsEvent(
"Lint Module Result",
mapOf(
"totalIssues" to moduleResult.issuesCount,
"totalWarnings" to moduleResult.warningsCount,
"totalInfo" to moduleResult.infoCount,
"totalErrors" to moduleResult.errorsCount,
"totalIgnored" to moduleResult.ignoredCount,
"moduleName" to moduleResult.metadata.moduleName,
"moduleType" to moduleResult.metadata.type.name
)
)
)

return moduleEvents
}

private fun convertIssue(issue: LintIssue): AnalyticsEvent {
return AnalyticsEvent(
"Lint Issue Result",
mapOf(
"category" to issue.category,
"errorLine" to issue.errorLine,
"issueId" to issue.id,
"location" to issue.location,
"message" to issue.message,
"moduleName" to issue.metadata.moduleName,
"moduleType" to issue.metadata.type.name,
"priority" to issue.priority,
"severity" to issue.severity.name,
"summary" to issue.summary,
)
)
}

private fun convertProjectResult(projectResult: LintProjectResult): AnalyticsEvent {
return AnalyticsEvent(
"Lint Project Result",
mapOf(
"totalIssues" to projectResult.issuesCount,
"totalWarnings" to projectResult.warningsCount,
"totalInfo" to projectResult.infoCount,
"totalErrors" to projectResult.errorsCount,
"totalIgnored" to projectResult.ignoredCount,
)
)
}

companion object {
private val GRAPH_ICON = "\uD83D\uDCC9"
}
}
42 changes: 42 additions & 0 deletions plugins/src/main/java/com/jraska/lint/LintIssue.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.jraska.lint

import com.jraska.module.ModuleMetadata

data class LintIssue(
val metadata: ModuleMetadata,
val id: String,
val message: String,
val severity: Severity,
val category: String,
val priority: Int,
val summary: String,
val errorLine: String?,
val location: String?
)

data class LintModuleResult(
val metadata: ModuleMetadata,
val issues: List<LintIssue>,
val issuesCount: Int,
val errorsCount: Int,
val warningsCount: Int,
val infoCount: Int,
val ignoredCount: Int
)

data class LintProjectResult(
val issuesCount: Int,
val errorsCount: Int,
val warningsCount: Int,
val infoCount: Int,
val ignoredCount: Int,
val moduleResults: List<LintModuleResult>
)

enum class Severity {
Error,
Warning,
Info,
Fatal,
Ignore
}
55 changes: 55 additions & 0 deletions plugins/src/main/java/com/jraska/lint/LintProjectExtractor.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.jraska.lint

import com.jraska.module.extract.StatisticsGradleExtractor.Companion.moduleMetadata
import org.gradle.api.Project
import java.io.File

class LintProjectExtractor {
fun extract(project: Project): LintProjectResult {
val moduleStats = project.rootProject
.subprojects
.filter { it.childProjects.isEmpty() }
.map { extractFromModule(it) }

return LintProjectResult(
moduleResults = moduleStats,
issuesCount = moduleStats.map { it.issuesCount }.sum(),
warningsCount = moduleStats.map { it.warningsCount }.sum(),
infoCount = moduleStats.map { it.infoCount }.sum(),
errorsCount = moduleStats.map { it.errorsCount }.sum(),
ignoredCount = moduleStats.map { it.ignoredCount }.sum()
)
}

private fun extractFromModule(module: Project): LintModuleResult {
val moduleMetadata = module.moduleMetadata()

val xml = locateLintFile(module).readText()
val issues = LintXmlParser(moduleMetadata).parse(xml)

return LintModuleResult(
metadata = moduleMetadata,
issues = issues,
issuesCount = issues.size,
warningsCount = issues.count { it.severity == Severity.Warning },
errorsCount = issues.count { it.severity == Severity.Error || it.severity == Severity.Fatal },
ignoredCount = issues.count { it.severity == Severity.Ignore },
infoCount = issues.count { it.severity == Severity.Info }
)
}

private fun locateLintFile(project: Project): File {
val buildDir = project.buildDir
val androidModuleCase = File(buildDir, "reports/lint-results.xml")
if (androidModuleCase.exists()) {
return androidModuleCase
}

val jvmModuleCase = File(buildDir, "test-results/lint-results.xml")
if (jvmModuleCase.exists()) {
return jvmModuleCase
}

throw IllegalStateException("Lint results didn't found in paths: [$androidModuleCase, $jvmModuleCase]")
}
}
23 changes: 23 additions & 0 deletions plugins/src/main/java/com/jraska/lint/LintReportProcess.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.jraska.lint

import com.jraska.analytics.AnalyticsReporter
import org.gradle.api.Project

class LintReportProcess(
private val lintProjectExtractor: LintProjectExtractor,
private val lintAnalyticsReporter: LintAnalyticsReporter
) {
fun executeReport(project: Project) {
val report = lintProjectExtractor.extract(project)
lintAnalyticsReporter.report(report)
}

companion object {
fun create(): LintReportProcess {
return LintReportProcess(
LintProjectExtractor(),
LintAnalyticsReporter(AnalyticsReporter.create("Lint Reporter"))
)
}
}
}
22 changes: 22 additions & 0 deletions plugins/src/main/java/com/jraska/lint/LintReporterPlugin.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.jraska.lint

import org.gradle.api.Plugin
import org.gradle.api.Project

class LintReporterPlugin : Plugin<Project> {
override fun apply(project: Project) {
project.afterEvaluate {
it.tasks.register("lintStatisticsReport") { lintReportTask ->
lintReportTask.doLast {
LintReportProcess.create().executeReport(lintReportTask.project)
}

project.rootProject.subprojects
.filter { it.childProjects.isEmpty() }
.forEach {
lintReportTask.dependsOn(it.tasks.named("lint"))
}
}
}
}
}
Loading

0 comments on commit 3e2a4a0

Please sign in to comment.