Skip to content
This repository has been archived by the owner on Feb 11, 2022. It is now read-only.

[PT-511] Configure lint via SAP extension #51

Merged

Conversation

tobiasheine
Copy link
Contributor

@tobiasheine tobiasheine commented Jan 30, 2018

PT-511

Description

Scope of this PR is to add basic support for Android Lint to the SAP plugin.

This targets a feature branch.

Considerations

  • Added CollectLintViolationsTask which parses lint-result.xml and appends errors and warnings to the violations

  • Added LintConfigurator which applies the lintConfig closure to Lint and orchestrates the tasks

Tests

  • Added a test for the CollectLintViolationsTask to verify we parse the lint-result.xml correctly
  • Added the LintIntegrationTest which verifies warnings and errors are considered for the thresholds

Paired with @lgvalle on parts of it

@@ -15,11 +13,6 @@ import static com.novoda.test.LogsSubject.assertThat
@RunWith(Parameterized.class)
class DetektIntegrationTest {

@ClassRule
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just some boy scouting, we're not using the artifact in this test.

@@ -63,11 +63,6 @@ ${formatExtension(project)}
.join('\n\t\t')
}

@Override
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mr-archano what was the reasons for this? Can we kill it? All tests are still passing...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the reason for this was to temporary disable lint as the default configuration breaks the build on error. I would have expected the other integration tests to fail if these lines are removed, mmm

@rock3r rock3r self-requested a review January 30, 2018 16:38
Copy link
Contributor

@tasomaniac tasomaniac left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice. Have 1 confusion and small comments.

@@ -63,11 +63,6 @@ ${formatExtension(project)}
.join('\n\t\t')
}

@Override
List<String> defaultArguments() {
['-x', 'lint'] + super.defaultArguments()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}

private CollectLintViolationsTask createCollectViolationsTask(Violations violations) {
def outputFolder = new File(project.projectDir, 'build/reports')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't hardcode build/reports, android team may change the location. I wonder if the lint task has output?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, I agree. Also the user can change the location.

return
}

project.extensions.findByName('android').lintOptions(config)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if you like more groovy magic but I believe this can be replaced by
project.android.lintOptions(config)

static LintConfigurator create(Project project,
NamedDomainObjectContainer<Violations> violationsContainer,
Task evaluateViolations) {
Violations violations = violationsContainer.maybeCreate('Lint')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused by this maybeCreate('Lint')
This means that the container is supposed to have a named closure called Lint but I couldn't find another usage. Not in the tests for example.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am also confused since I don't fully understand what's going in here. Tbh I just copy pasted it from the other configurators. From what I understand we pass the same Violations to each Configurator. But I don't understand how this relates to it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe @mr-archano can enlighten us around this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We just hold all the violations in the plugin extension to keep the output of each tool output in memory before the final evaluation (see StaticAnalysisExtension.allViolations).
Each tool is now responsible of creating the Violations entry in the container, that is then passed to the evaluateViolations task.

As already mentioned IRL couple of times this is a pretty lame way of collecting all the info, and I already logged an issue to get rid of this in the next future: #43

}

private CollectLintViolationsTask createCollectViolationsTask(Violations violations) {
def outputFolder = new File(project.projectDir, 'build/reports')
Copy link
Contributor Author

@tobiasheine tobiasheine Jan 30, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As follow-up it would be good to resolve this path dynamically. Otherwise this will break in case the user configures another xmlOutput.

Copy link
Contributor

@mr-archano mr-archano Jan 30, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should fix this now, as we shouldn't hardcode anything that can be configured. The only constraint we actually have is that we need to get this information after lint is configured, in order to read the correct path to the xml report. We have two ways of doing this as I see it: we get it from lintOptions or from the lint task.
If we assume that the user will configure lint through our plugin - effectively done at L36 - then we should be able to read the value from the lint extension right afterwards, right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also keep in mind that lint is run for all variants. Maybe we should have includeVariants here too. But in any case, should we also support multiple outputs?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mr-archano this PR is towards a feature branch. Not on develop. So I think we can just merge it without this? with a follow-up soon?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mr-archano just checked that, but the output path of lintOptions is null in case the user hasn't configured it. So we need to find another way. I agree with @tasomaniac to follow up on this as part of this ticket.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We agreed on for now to check whether the output path of lintOptions is configured or to fall back to the hardcoded one.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Override
void execute() {
project.extensions.findByType(StaticAnalysisExtension).ext."lintOptions" = { Closure config ->

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: extra whiteline


private CollectLintViolationsTask createCollectViolationsTask(Violations violations) {
def outputFolder = new File(project.projectDir, 'build/reports')
project.tasks.create("collectLintViolations", CollectLintViolationsTask) { collectViolations ->
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: use single quotes for "collectLintViolations" for consistency

Project project = ProjectBuilder.builder().build()
def task = project.task('collectLintViolationsTask', type: CollectLintViolationsTask)

Violations violations = new Violations("Android Lint")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: use single quotes for "Android Lint" for consistency

public class Warning {

public Warning() {
// assertions are ignored at runtime
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean the plugin can't deal with Lint warnings, but only with errors? Not really clear to me

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, using assertions in code results in a Warning for lint. This is just a fixture that can be used for tests when provoking a lint warning.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I'm blind and I hadn't seen the path. My bad, sorry :)

testProject.withToolsConfig(lintConfiguration())
}

private static GString lintConfiguration() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not a constant or inlined as a string?

return
}

project.android.lintOptions(config)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to be missing the bit where we avoid lint to break the build (we want to be in charge of when doing that). I believe we should force abortOnError false here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having a look at the whole options here: https://google.github.io/android-gradle-dsl/current/com.android.build.gradle.internal.dsl.LintOptions.html

I think we can also add xmlReport true to make sure that xml report is enabled.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same for the htmlReport I'd say

}

private CollectLintViolationsTask createCollectViolationsTask(Violations violations) {
def outputFolder = new File(project.projectDir, 'build/reports')
Copy link
Contributor

@mr-archano mr-archano Jan 30, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should fix this now, as we shouldn't hardcode anything that can be configured. The only constraint we actually have is that we need to get this information after lint is configured, in order to read the correct path to the xml report. We have two ways of doing this as I see it: we get it from lintOptions or from the lint task.
If we assume that the user will configure lint through our plugin - effectively done at L36 - then we should be able to read the value from the lint extension right afterwards, right?

@@ -63,11 +63,6 @@ ${formatExtension(project)}
.join('\n\t\t')
}

@Override
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the reason for this was to temporary disable lint as the default configuration breaks the build on error. I would have expected the other integration tests to fail if these lines are removed, mmm

@tobiasheine tobiasheine force-pushed the PT-511/configure_lint_via_extension branch from 5744c3b to ff98b70 Compare January 31, 2018 09:10
collectViolations.violations = violations
}
}

private File xmlOutputFile() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry to be a PITA, but this could be a getter getXmlOutputFile(), so you can use it simply at L58 as xmlOutputFile property

project.android.lintOptions.xmlOutput ?: new File(defaultOutputFolder(), 'lint-results.xml')
}

private File defaultOutputFolder() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

}

private File defaultOutputFolder() {
new File(project.projectDir, 'build/reports')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you should never add build/, but rather use project.buildDir. this will become then:

new File(project.buildDir, 'reports')

@mr-archano mr-archano dismissed their stale review January 31, 2018 11:05

obsolete review

@mr-archano mr-archano merged commit 8db73ac into PT-282/integrate_android_lint Jan 31, 2018
@mr-archano mr-archano deleted the PT-511/configure_lint_via_extension branch January 31, 2018 11:06
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants