diff --git a/src/main/groovy/com/netflix/nebula/lint/GradleLintInfoBrokerAction.groovy b/src/main/groovy/com/netflix/nebula/lint/GradleLintInfoBrokerAction.groovy index 412c5db9..6ce47ab4 100644 --- a/src/main/groovy/com/netflix/nebula/lint/GradleLintInfoBrokerAction.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/GradleLintInfoBrokerAction.groovy @@ -1,15 +1,26 @@ package com.netflix.nebula.lint +import com.netflix.nebula.lint.plugin.ProjectInfo import groovy.transform.Canonical -import org.gradle.api.Project +import org.gradle.api.Plugin +import org.gradle.api.Task @Canonical class GradleLintInfoBrokerAction extends GradleLintViolationAction { - Project project + Plugin nebulaInfoBroker + ProjectInfo projectInfo + + + GradleLintInfoBrokerAction(Task task){ + this.projectInfo = ProjectInfo.from(task) + task.project.getPlugins().withId('nebula.info-broker') { plugin -> + nebulaInfoBroker = plugin + } + } @Override void lintFinished(Collection violations) { - project.getPlugins().withId('nebula.info-broker') { + nebulaInfoBroker?.tap{ def reportItems = violations.collect { buildReportItem(it) } it.addReport('gradleLintViolations', reportItems) } @@ -17,7 +28,7 @@ class GradleLintInfoBrokerAction extends GradleLintViolationAction { @Override void lintFixesApplied(Collection violations) { - project.getPlugins().withId('nebula.info-broker') { + nebulaInfoBroker?.tap { def reportItems = violations.findAll { !it.fixes.any { it.reasonForNotFixing } } .collect { buildReportItem(it) } it.addReport('fixedGradleLintViolations', reportItems) @@ -25,7 +36,7 @@ class GradleLintInfoBrokerAction extends GradleLintViolationAction { } LintReportItem buildReportItem(GradleViolation v) { - def buildFilePath = project.rootDir.toURI().relativize(v.file.toURI()).toString() + def buildFilePath = projectInfo.rootDir.toURI().relativize(v.file.toURI()).toString() new LintReportItem(buildFilePath, v.rule.name, v.rule.getPriority() as String, v.lineNumber ?: -1, v.sourceLine ?: 'unspecified', v.message ?: "") } diff --git a/src/main/groovy/com/netflix/nebula/lint/GradleLintPatchAction.groovy b/src/main/groovy/com/netflix/nebula/lint/GradleLintPatchAction.groovy index b43fb97d..ba95df97 100644 --- a/src/main/groovy/com/netflix/nebula/lint/GradleLintPatchAction.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/GradleLintPatchAction.groovy @@ -16,6 +16,7 @@ package com.netflix.nebula.lint +import com.netflix.nebula.lint.plugin.ProjectInfo import com.netflix.nebula.lint.plugin.UnexpectedLintRuleFailureException import groovy.transform.Canonical import org.apache.commons.lang.StringUtils @@ -30,12 +31,22 @@ import static java.nio.file.Files.readSymbolicLink @Canonical class GradleLintPatchAction extends GradleLintViolationAction { Project project + ProjectInfo projectInfo + + GradleLintPatchAction(Project project) { + this.project = project + + } + + GradleLintPatchAction(ProjectInfo projectInfo) { + this.projectInfo = projectInfo + } static final String PATCH_NAME = 'lint.patch' @Override void lintFinished(Collection violations) { - File buildDir = project.layout.buildDirectory.asFile.getOrElse(new File(project.projectDir, "build")) + File buildDir = projectInfo ? projectInfo.buildDirectory : project.layout.buildDirectory.asFile.getOrElse(new File(project.projectDir, "build")) buildDir.mkdirs() try { def patch = patch(violations*.fixes.flatten() as List) @@ -252,7 +263,7 @@ Exception: ${e.getMessage()} if (i > 0) combinedPatch += '\n' - def relativePath = project.rootDir.toPath().relativize(file.toPath()).toString() + def relativePath = projectInfo? projectInfo.rootDir.toPath().relativize(file.toPath()).toString() : project.rootDir.toPath().relativize(file.toPath()).toString() def diffHeader = """\ ${diffHintsWithMargin(relativePath, patchType, fileMode)} |--- ${patchType == Create ? '/dev/null' : 'a/' + relativePath} diff --git a/src/main/groovy/com/netflix/nebula/lint/plugin/AppliedFilesAstVisitor.groovy b/src/main/groovy/com/netflix/nebula/lint/plugin/AppliedFilesAstVisitor.groovy index b351f839..b3ed21d7 100644 --- a/src/main/groovy/com/netflix/nebula/lint/plugin/AppliedFilesAstVisitor.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/plugin/AppliedFilesAstVisitor.groovy @@ -12,17 +12,18 @@ import org.gradle.api.Project */ class AppliedFilesAstVisitor extends ClassCodeVisitorSupport { - Project project + ProjectInfo projectInfo + List appliedFiles = new ArrayList() Map projectVariablesMapping - AppliedFilesAstVisitor(Project project) { - this.project = project + AppliedFilesAstVisitor(ProjectInfo projectInfo) { + this.projectInfo = projectInfo projectVariablesMapping = [ - "\$projectDir" : project.projectDir.toString(), - "\$project.projectDir" : project.projectDir.toString(), - "\$rootDir" : project.rootDir.toString(), - "\$project.rootDir" : project.rootDir.toString(), + "\$projectDir" : projectInfo.projectDir.toString(), + "\$project.projectDir" : projectInfo.projectDir.toString(), + "\$rootDir" : projectInfo.rootDir.toString(), + "\$project.rootDir" : projectInfo.rootDir.toString(), ] } @@ -32,9 +33,9 @@ class AppliedFilesAstVisitor extends ClassCodeVisitorSupport { def projectVariable = projectVariablesMapping.find {from.contains(it.key) } if (projectVariable) { def absolutePath = from.replaceAll("\\$projectVariable.key", projectVariable.value) - appliedFiles.addAll(SourceCollector.getAllFiles(new File(absolutePath), project)) + appliedFiles.addAll(SourceCollector.getAllFiles(new File(absolutePath), projectInfo)) } else { - appliedFiles.addAll(SourceCollector.getAllFiles(new File(project.projectDir, from), project)) + appliedFiles.addAll(SourceCollector.getAllFiles(new File(projectInfo.projectDir, from), projectInfo)) } } } diff --git a/src/main/groovy/com/netflix/nebula/lint/plugin/FixGradleLintTask.groovy b/src/main/groovy/com/netflix/nebula/lint/plugin/FixGradleLintTask.groovy index 23f807cd..805c7cf6 100644 --- a/src/main/groovy/com/netflix/nebula/lint/plugin/FixGradleLintTask.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/plugin/FixGradleLintTask.groovy @@ -24,7 +24,9 @@ import com.netflix.nebula.lint.StyledTextService import org.eclipse.jgit.api.ApplyCommand import org.gradle.api.DefaultTask import org.gradle.api.GradleException +import org.gradle.api.Task import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.Property import org.gradle.api.tasks.Input import org.gradle.api.tasks.Internal import org.gradle.api.tasks.Optional @@ -46,28 +48,44 @@ abstract class FixGradleLintTask extends DefaultTask implements VerificationTask @Internal GradleLintInfoBrokerAction infoBrokerAction + @Internal + GradleLintPatchAction patchAction + + @Internal + abstract Property getProjectTree() + + @Internal + ProjectInfo getProjectInfo() { + return projectTree.get().baseProject + } FixGradleLintTask() { - infoBrokerAction = new GradleLintInfoBrokerAction(project) + projectTree.set(project.provider {ProjectTree.from(this) }) + infoBrokerAction = new GradleLintInfoBrokerAction(this) + patchAction = new GradleLintPatchAction(getProjectInfo()) userDefinedListeners.convention([]) outputs.upToDateWhen { false } group = 'lint' + try { + def method = Task.getMethod("notCompatibleWithConfigurationCache") + method.invoke(this) + } catch (NoSuchMethodException ignore) { + } } @TaskAction void lintCorrections() { - //TODO: address Invocation of Task.project at execution time has been deprecated. DeprecationLogger.whileDisabled { - def violations = new LintService().lint(project, false).violations + def violations = new LintService().lint(projectTree.get(), false).violations .unique { v1, v2 -> v1.is(v2) ? 0 : 1 } - (userDefinedListeners.get() + infoBrokerAction + new GradleLintPatchAction(project)).each { + (userDefinedListeners.get() + infoBrokerAction + patchAction).each { it.lintFinished(violations) } - def patchFile = new File(project.layout.buildDirectory.asFile.get(), GradleLintPatchAction.PATCH_NAME) + def patchFile = new File(getProjectInfo().buildDirectory, GradleLintPatchAction.PATCH_NAME) if (patchFile.exists()) { - new ApplyCommand(new NotNecessarilyGitRepository(project.projectDir)).setPatch(patchFile.newInputStream()).call() + new ApplyCommand(new NotNecessarilyGitRepository(projectInfo.projectDir)).setPatch(patchFile.newInputStream()).call() } (userDefinedListeners.get() + infoBrokerAction + consoleOutputAction()).each { @@ -97,7 +115,7 @@ abstract class FixGradleLintTask extends DefaultTask implements VerificationTask violations.groupBy { it.file }.each { buildFile, projectViolations -> projectViolations.each { v -> - String buildFilePath = project.rootDir.toURI().relativize(v.file.toURI()).toString() + String buildFilePath = projectTree.get().baseProject.rootDir.toURI().relativize(v.file.toURI()).toString() def unfixed = v.fixes.findAll { it.reasonForNotFixing != null } if (v.fixes.empty) { textOutput.withStyle(Yellow).text('needs fixing'.padRight(15)) diff --git a/src/main/groovy/com/netflix/nebula/lint/plugin/GradleLintReportTask.groovy b/src/main/groovy/com/netflix/nebula/lint/plugin/GradleLintReportTask.groovy index e5144e14..c76f2fd3 100644 --- a/src/main/groovy/com/netflix/nebula/lint/plugin/GradleLintReportTask.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/plugin/GradleLintReportTask.groovy @@ -57,9 +57,17 @@ abstract class GradleLintReportTask extends DefaultTask implements VerificationT @Internal final NamedDomainObjectContainer reports + @Internal + abstract Property getProjectInfo() + + @Internal + abstract Property getProjectTree() + @Inject GradleLintReportTask(ObjectFactory objects) { - projectName = objects.property(String).convention(project.name) + projectInfo.convention(projectTree.map {it.baseProject }) + projectTree.set(project.provider { ProjectTree.from(this) }) + projectName = objects.property(String).convention(projectInfo.get().name) reportsDir = objects.directoryProperty() reports = objects.domainObjectContainer( @@ -90,7 +98,7 @@ abstract class GradleLintReportTask extends DefaultTask implements VerificationT DeprecationLogger.whileDisabled { if (reports.any { it.required.isPresent() && it.required.get()}) { def lintService = new LintService() - def results = lintService.lint(project, false) + def results = lintService.lint(projectTree.get(), false) filterOnlyFixableViolations(results) def violationCount = results.violations.size() def textOutput = new StyledTextService(getServices()) @@ -101,7 +109,7 @@ abstract class GradleLintReportTask extends DefaultTask implements VerificationT reports.each { if(it.required.isPresent() && it.required.get()) { - it.write(new AnalysisContext(ruleSet: lintService.ruleSet(project)), results) + it.write(new AnalysisContext(ruleSet: lintService.ruleSet(projectTree.get())), results) } } @@ -142,7 +150,7 @@ abstract class GradleLintReportTask extends DefaultTask implements VerificationT void filterOnlyFixableViolations(Results results) { if (reportOnlyFixableViolations.isPresent() && reportOnlyFixableViolations.get()) { - new GradleLintPatchAction(project).lintFinished(results.violations) + new GradleLintPatchAction(projectInfo.get()).lintFinished(results.violations) List toRemove = results.violations.findAll { it.fixes.size() == 0 || it.fixes.any { it.reasonForNotFixing != null } } diff --git a/src/main/groovy/com/netflix/nebula/lint/plugin/LintGradleTask.groovy b/src/main/groovy/com/netflix/nebula/lint/plugin/LintGradleTask.groovy index ee4d4604..146f5d6d 100644 --- a/src/main/groovy/com/netflix/nebula/lint/plugin/LintGradleTask.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/plugin/LintGradleTask.groovy @@ -15,11 +15,12 @@ */ package com.netflix.nebula.lint.plugin +import com.google.common.annotations.VisibleForTesting import com.netflix.nebula.lint.* import org.gradle.api.DefaultTask import org.gradle.api.GradleException +import org.gradle.api.Project import org.gradle.api.Task -import org.gradle.api.provider.ListProperty import org.gradle.api.provider.Property import org.gradle.api.tasks.Input import org.gradle.api.tasks.Internal @@ -27,6 +28,8 @@ import org.gradle.api.tasks.Optional import org.gradle.api.tasks.TaskAction import org.gradle.internal.deprecation.DeprecationLogger +import java.util.function.Supplier + import static com.netflix.nebula.lint.StyledTextService.Styling.* abstract class LintGradleTask extends DefaultTask { @@ -45,9 +48,27 @@ abstract class LintGradleTask extends DefaultTask { @Input abstract Property getProjectRootDir() + @Input + abstract Property getProjectTree() + + @Input + abstract Property getProjectInfo() + + @Internal + GradleLintInfoBrokerAction infoBrokerAction + + @Internal + GradleLintPatchAction patchAction + + LintGradleTask() { failOnWarning.convention(false) onlyCriticalRules.convention(false) + projectTree.set(project.provider {ProjectTree.from(this) }) + projectInfo.convention(projectTree.map(ProjectTree::getBaseProject)) + projectRootDir.set(project.rootDir) + infoBrokerAction = new GradleLintInfoBrokerAction(this) + patchAction = new GradleLintPatchAction(getProjectInfo().get()) group = 'lint' try { def method = Task.getMethod("notCompatibleWithConfigurationCache") @@ -58,12 +79,11 @@ abstract class LintGradleTask extends DefaultTask { @TaskAction void lint() { - //TODO: address Invocation of Task.project at execution time has been deprecated. DeprecationLogger.whileDisabled { - def violations = new LintService().lint(project, onlyCriticalRules.get()).violations + def violations = new LintService().lint(projectTree.get(), onlyCriticalRules.get()).violations .unique { v1, v2 -> v1.is(v2) ? 0 : 1 } - (getListeners() + new GradleLintPatchAction(project) + new GradleLintInfoBrokerAction(project) + consoleOutputAction).each { + (getListeners() + patchAction + infoBrokerAction + consoleOutputAction).each { it.lintFinished(violations) } } @@ -139,3 +159,81 @@ abstract class LintGradleTask extends DefaultTask { } } } +/** + * A CC-compatible projection of project data. + */ +class ProjectInfo implements Serializable{ + String name + String path + File rootDir + File buildFile + File projectDir + File buildDirectory + GradleLintExtension extension + Map properties + Supplier projectSupplier + + static ProjectInfo from(Task task, Project subproject) { + String subprojectPath = subproject.path + return build(subproject, { task.project.project(subprojectPath) }) + } + + static ProjectInfo from(Task task) { + return build(task.project, task::getProject) + } + + @VisibleForTesting + private static ProjectInfo build(Project project, Supplier projectSupplier) { + GradleLintExtension extension = + project.extensions.findByType(GradleLintExtension) ?: + project.rootProject.extensions.findByType(GradleLintExtension) + Map properties = [:] + if (project.hasProperty('gradleLint.rules')) { + properties['gradleLint.rules'] = project.property('gradleLint.rules') + } + if (project.hasProperty('gradleLint.excludedRules')) { + properties['gradleLint.excludedRules'] = project.property('gradleLint.excludedRules') + } + + return new ProjectInfo( + name:project.name, + path:project.path, + rootDir:project.rootDir, + buildFile: project.buildFile, + projectDir:project.projectDir, + extension: extension, + properties: properties, + projectSupplier: projectSupplier, + buildDirectory : project.buildDir + ) + + } + + +} + +class ProjectTree{ + List allProjects + + ProjectTree(List allProjects){ + this.allProjects = allProjects + } + + /** + * Returns the base project this tree was built from. + */ + ProjectInfo getBaseProject() { + return allProjects.head() + } + + /** + * Build a project tree based on the given task's project. + * + * @return a project tree reflecting information and the structure of the given task's project + */ + static from(Task task) { + def baseProject = task.project + List projectInfos = [ProjectInfo.from(task)] + baseProject.subprojects.collect { Project p -> ProjectInfo.from(task, p) } + return new ProjectTree(projectInfos) + } +} diff --git a/src/main/groovy/com/netflix/nebula/lint/plugin/LintRuleRegistry.groovy b/src/main/groovy/com/netflix/nebula/lint/plugin/LintRuleRegistry.groovy index 6fbee6aa..39cbe1d9 100644 --- a/src/main/groovy/com/netflix/nebula/lint/plugin/LintRuleRegistry.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/plugin/LintRuleRegistry.groovy @@ -21,6 +21,8 @@ import com.netflix.nebula.lint.rule.ModelAwareGradleLintRule import org.codenarc.rule.Rule import org.gradle.api.Project +import java.util.function.Supplier + class LintRuleRegistry { static ClassLoader classLoader = null @@ -44,7 +46,7 @@ class LintRuleRegistry { - List buildRules(String ruleId, Project project, boolean critical) { + List buildRules(String ruleId, Supplier projectSupplier, boolean critical) { assert classLoader != null def ruleDescriptor = findRuleDescriptor(ruleId) if (ruleDescriptor == null) @@ -57,13 +59,13 @@ class LintRuleRegistry { throw new InvalidRuleException(String.format("No implementation class or includes specified for rule '%s' in %s.", ruleId, ruleDescriptor)) } - def included = includes.collect { buildRules(it as String, project, critical) }.flatten() as List + def included = includes.collect { buildRules(it as String, projectSupplier, critical) }.flatten() as List if(implClassName) { try { Rule r = (Rule) classLoader.loadClass(implClassName).newInstance() if(r instanceof ModelAwareGradleLintRule) { - (r as ModelAwareGradleLintRule).project = project + (r as ModelAwareGradleLintRule).projectSupplier = projectSupplier } if(r instanceof GradleLintRule) { @@ -80,4 +82,9 @@ class LintRuleRegistry { return included } } + + List buildRules(String ruleId, Project project, boolean critical) { + return buildRules(ruleId, { project } as Supplier, critical) + } + } diff --git a/src/main/groovy/com/netflix/nebula/lint/plugin/LintService.groovy b/src/main/groovy/com/netflix/nebula/lint/plugin/LintService.groovy index 03e78e52..2cce0ccc 100644 --- a/src/main/groovy/com/netflix/nebula/lint/plugin/LintService.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/plugin/LintService.groovy @@ -19,6 +19,7 @@ package com.netflix.nebula.lint.plugin import com.netflix.nebula.lint.GradleViolation import com.netflix.nebula.lint.rule.BuildFiles import com.netflix.nebula.lint.rule.GradleLintRule +import com.netflix.nebula.lint.rule.ModelAwareGradleLintRule import com.netflix.nebula.lint.rule.dependency.DependencyService import org.codenarc.analyzer.AbstractSourceAnalyzer import org.codenarc.results.DirectoryResults @@ -32,6 +33,8 @@ import org.codenarc.source.SourceString import org.gradle.api.Project import org.gradle.api.UnknownDomainObjectException +import java.util.function.Supplier + class LintService { def registry = new LintRuleRegistry() @@ -42,11 +45,11 @@ class LintService { class ReportableAnalyzer extends AbstractSourceAnalyzer { DirectoryResults resultsForRootProject - ReportableAnalyzer(Project project) { - resultsForRootProject = new DirectoryResults(project.projectDir.absolutePath) + ReportableAnalyzer(ProjectInfo projectDirInfo) { + resultsForRootProject = new DirectoryResults(projectDirInfo.projectDir.absolutePath) } - Results analyze(Project analyzedProject, String source, RuleSet ruleSet) { + Results analyze(ProjectInfo analyzedProject, String source, RuleSet ruleSet) { DirectoryResults results if (resultsForRootProject.path != analyzedProject.projectDir.absolutePath) { results = new DirectoryResults(analyzedProject.projectDir.absolutePath) @@ -75,29 +78,25 @@ class LintService { } } - private RuleSet ruleSetForProject(Project p, boolean onlyCriticalRules) { - if (p.buildFile.exists()) { - GradleLintExtension extension - try { - extension = p.extensions.getByType(GradleLintExtension) - } catch (UnknownDomainObjectException ignored) { - // if the subproject has not applied lint, use the extension configuration from the root project - extension = p.rootProject.extensions.getByType(GradleLintExtension) - } - def rules = (p.hasProperty('gradleLint.rules') ? p.property('gradleLint.rules') : null)?.toString()?.split(',')?.toList() ?: + + + private RuleSet ruleSetForProject(ProjectInfo projectInfo,boolean onlyCriticalRules) { + if (projectInfo.buildFile.exists()) { + def extension = projectInfo.extension + + def rules = (projectInfo.properties['gradleLint.rules'])?.toString()?.split(',')?.toList() ?: extension.rules + extension.criticalRules def includedRules = rules.unique() - .collect { registry.buildRules(it, p, extension.criticalRules.contains(it)) } + .collect { registry.buildRules(it, projectInfo.projectSupplier, extension.criticalRules.contains(it)) } .flatten() as List if (onlyCriticalRules) { includedRules = includedRules.findAll { it instanceof GradleLintRule && it.critical } } - def excludedRules = (p.hasProperty('gradleLint.excludedRules') ? - p.property('gradleLint.excludedRules').toString().split(',').toList() : []) + extension.excludedRules + def excludedRules = (projectInfo.properties['gradleLint.excludedRules']?.toString()?.split(',')?.toList() ?: []) + extension.excludedRules if (!excludedRules.isEmpty()) includedRules.retainAll { !excludedRules.contains(it.name) } @@ -106,32 +105,41 @@ class LintService { return new ListRuleSet([]) } - RuleSet ruleSet(Project project) { + RuleSet ruleSet(ProjectTree projectTree) { def ruleSet = new CompositeRuleSet() - ([project] + project.subprojects).each { p -> ruleSet.addRuleSet(ruleSetForProject(p, false)) } - return ruleSet + projectTree.allProjects.each { p -> + ruleSet.addRuleSet(ruleSetForProject(p, false))} + return ruleSet } - Results lint(Project project, boolean onlyCriticalRules) { - def analyzer = new ReportableAnalyzer(project) + Results lint(ProjectTree projectTree, boolean onlyCriticalRules) { + ProjectInfo rootProjectInfo = projectTree.allProjects.find { it.path == ":" } + def analyzer = new ReportableAnalyzer(rootProjectInfo) + + projectTree.allProjects.each { p -> - ([project] + project.subprojects).each { p -> def files = SourceCollector.getAllFiles(p.buildFile, p) def buildFiles = new BuildFiles(files) def ruleSet = ruleSetForProject(p, onlyCriticalRules) if (!ruleSet.rules.isEmpty()) { + boolean containsModelAwareRule = false // establish which file we are linting for each rule ruleSet.rules.each { rule -> - if (rule instanceof GradleLintRule) + if (rule instanceof GradleLintRule) { rule.buildFiles = buildFiles + } + if (rule instanceof ModelAwareGradleLintRule) { + containsModelAwareRule = true + } } - analyzer.analyze(p, buildFiles.text, ruleSet) - - DependencyService.removeForProject(p) + if (containsModelAwareRule){ + Project project = p.projectSupplier.get() + DependencyService.removeForProject(project) + } } } return analyzer.resultsForRootProject } -} +} \ No newline at end of file diff --git a/src/main/groovy/com/netflix/nebula/lint/plugin/SourceCollector.groovy b/src/main/groovy/com/netflix/nebula/lint/plugin/SourceCollector.groovy index 59e67b46..2122bd49 100644 --- a/src/main/groovy/com/netflix/nebula/lint/plugin/SourceCollector.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/plugin/SourceCollector.groovy @@ -12,7 +12,7 @@ class SourceCollector { * It scans given build file for possible `apply from: 'another.gradle'` and recursively * collect all build files which are present. */ - static List getAllFiles(File buildFile, Project project) { + static List getAllFiles(File buildFile, ProjectInfo projectInfo) { if (buildFile.exists()) { List result = new ArrayList<>() result.add(buildFile) @@ -20,7 +20,7 @@ class SourceCollector { ModuleNode ast = sourceCode.getAst() if (ast != null && ast.getClasses() != null) { for (ClassNode classNode : ast.getClasses()) { - AppliedFilesAstVisitor visitor = new AppliedFilesAstVisitor(project) + AppliedFilesAstVisitor visitor = new AppliedFilesAstVisitor(projectInfo) visitor.visitClass(classNode) result.addAll(visitor.appliedFiles) } @@ -30,4 +30,7 @@ class SourceCollector { return Collections.emptyList() } } -} + static List getAllFiles(File buildFile, Project project) { + return getAllFiles(buildFile, ProjectInfo.from(project)) + } +} \ No newline at end of file diff --git a/src/main/groovy/com/netflix/nebula/lint/rule/GradleLintRule.groovy b/src/main/groovy/com/netflix/nebula/lint/rule/GradleLintRule.groovy index 9dec80d3..7b021aa7 100644 --- a/src/main/groovy/com/netflix/nebula/lint/rule/GradleLintRule.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/rule/GradleLintRule.groovy @@ -42,9 +42,9 @@ import org.slf4j.Logger import org.slf4j.LoggerFactory import java.text.ParseException +import java.util.function.Supplier abstract class GradleLintRule extends GroovyAstVisitor implements Rule { - Project project BuildFiles buildFiles SourceCode sourceCode List gradleViolations = [] @@ -59,6 +59,12 @@ abstract class GradleLintRule extends GroovyAstVisitor implements Rule { // the properties file resource that makes this rule available for use String ruleId + Supplier projectSupplier + + Project getProject() { + return projectSupplier?.get() + } + @Override final String getName() { return ruleId @@ -560,7 +566,7 @@ abstract class GradleLintRule extends GroovyAstVisitor implements Rule { } private boolean hasConfiguration(String name) { - if (!project) { + if (!projectSupplier) { return Collections.emptySet() } def configurations diff --git a/src/main/groovy/com/netflix/nebula/lint/rule/dependency/DependencyService.groovy b/src/main/groovy/com/netflix/nebula/lint/rule/dependency/DependencyService.groovy index 1f72b5ae..182108ad 100644 --- a/src/main/groovy/com/netflix/nebula/lint/rule/dependency/DependencyService.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/rule/dependency/DependencyService.groovy @@ -25,6 +25,7 @@ import java.nio.file.Files import java.nio.file.Path import java.nio.file.SimpleFileVisitor import java.nio.file.attribute.BasicFileAttributes +import java.util.function.Supplier import java.util.jar.JarEntry import java.util.jar.JarFile import java.util.zip.ZipException @@ -45,12 +46,16 @@ class DependencyService { return extension.dependencyService } - static synchronized void removeForProject(Project project) { + static synchronized void removeForProject(Supplier projectSupplier) { + Project project = projectSupplier.get() def extension = project.extensions.findByType(DependencyServiceExtension) if (extension) { extension.dependencyService = null } } + static synchronized void removeForProject(Project project) { + removeForProject({ project } as Supplier) + } static class DependencyServiceExtension { DependencyService dependencyService @@ -702,4 +707,4 @@ class DependencyService { } } } -} +} \ No newline at end of file diff --git a/src/main/groovy/com/netflix/nebula/lint/rule/test/AbstractRuleSpec.groovy b/src/main/groovy/com/netflix/nebula/lint/rule/test/AbstractRuleSpec.groovy index 082b02c8..da8aa045 100644 --- a/src/main/groovy/com/netflix/nebula/lint/rule/test/AbstractRuleSpec.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/rule/test/AbstractRuleSpec.groovy @@ -47,7 +47,7 @@ abstract class AbstractRuleSpec extends ProjectSpec { rules.each { ruleSet.addRule(it) if (it instanceof ModelAwareGradleLintRule) { - it.project = project + it.projectSupplier = { project } } } ruleSet diff --git a/src/test/groovy/com/netflix/nebula/lint/plugin/FixGradleLintTaskCriticalRulesSpec.groovy b/src/test/groovy/com/netflix/nebula/lint/plugin/FixGradleLintTaskCriticalRulesSpec.groovy index 6ad2fc86..28006232 100644 --- a/src/test/groovy/com/netflix/nebula/lint/plugin/FixGradleLintTaskCriticalRulesSpec.groovy +++ b/src/test/groovy/com/netflix/nebula/lint/plugin/FixGradleLintTaskCriticalRulesSpec.groovy @@ -1,8 +1,6 @@ package com.netflix.nebula.lint.plugin -import com.netflix.nebula.lint.rule.AbstractExampleGradleLintRule import com.netflix.nebula.lint.rule.AbstractModelAwareExampleGradleLintRule -import com.netflix.nebula.lint.rule.GradleModelAware import nebula.test.IntegrationSpec import org.codehaus.groovy.ast.expr.MethodCallExpression diff --git a/src/test/groovy/com/netflix/nebula/lint/plugin/LintRuleRegistrySpec.groovy b/src/test/groovy/com/netflix/nebula/lint/plugin/LintRuleRegistrySpec.groovy index 8af3ac4d..20984f48 100644 --- a/src/test/groovy/com/netflix/nebula/lint/plugin/LintRuleRegistrySpec.groovy +++ b/src/test/groovy/com/netflix/nebula/lint/plugin/LintRuleRegistrySpec.groovy @@ -17,7 +17,6 @@ package com.netflix.nebula.lint.plugin import com.netflix.nebula.lint.rule.AbstractExampleGradleLintRule -import com.netflix.nebula.lint.rule.GradleLintRule import org.gradle.api.Project import org.junit.Rule import org.junit.rules.TemporaryFolder diff --git a/src/test/groovy/com/netflix/nebula/lint/plugin/SourceCollectorTest.groovy b/src/test/groovy/com/netflix/nebula/lint/plugin/SourceCollectorTest.groovy index 8e8822b4..04cc9a85 100644 --- a/src/test/groovy/com/netflix/nebula/lint/plugin/SourceCollectorTest.groovy +++ b/src/test/groovy/com/netflix/nebula/lint/plugin/SourceCollectorTest.groovy @@ -4,6 +4,9 @@ import nebula.test.ProjectSpec class SourceCollectorTest extends ProjectSpec { + private ProjectInfo getProjectInfo() { + return ProjectInfo.build(project, { project }) + } def 'all build files are collected'() { given: @@ -24,7 +27,7 @@ class SourceCollectorTest extends ProjectSpec { level2.text = " " when: - def files = SourceCollector.getAllFiles(rootFile, project) + def files = SourceCollector.getAllFiles(rootFile, projectInfo) then: files.containsAll([rootFile, level1Sibling1, level1Sibling2, level2]) @@ -51,7 +54,7 @@ class SourceCollectorTest extends ProjectSpec { level2.text = " " when: - def files = SourceCollector.getAllFiles(rootFile, project) + def files = SourceCollector.getAllFiles(rootFile, projectInfo) then: files.containsAll([rootFile, level1Sibling1, level1Sibling2, level1Sibling3, level2]) diff --git a/src/test/groovy/com/netflix/nebula/lint/rule/GradleLintPatchActionSpec.groovy b/src/test/groovy/com/netflix/nebula/lint/rule/GradleLintPatchActionSpec.groovy index 21502ac1..cb901ef0 100644 --- a/src/test/groovy/com/netflix/nebula/lint/rule/GradleLintPatchActionSpec.groovy +++ b/src/test/groovy/com/netflix/nebula/lint/rule/GradleLintPatchActionSpec.groovy @@ -18,6 +18,7 @@ package com.netflix.nebula.lint.rule import com.netflix.nebula.lint.* import org.gradle.api.Project +import org.gradle.api.plugins.ExtensionContainer import org.junit.Rule import org.junit.rules.TemporaryFolder import spock.lang.Specification @@ -34,7 +35,17 @@ class GradleLintPatchActionSpec extends Specification { def setup() { buildFile = temp.newFile('build.gradle') - project = [getRootDir: { temp.root }] as Project + project = [ + getRootDir: { temp.root }, + getExtensions: { [findByType: { null } ] as ExtensionContainer }, + getRootProject: { project }, + hasProperty: { false }, + getName: { ":" }, + getPath: { ":" }, + getBuildFile: { buildFile }, + getProjectDir: { temp.root }, + getBuildDir: { temp.root }, + ] as Project violation = new GradleViolation( new BuildFiles([buildFile]), // does not matter null, // does not matter diff --git a/src/test/groovy/com/netflix/nebula/lint/rule/dependency/UnusedDependencyExcludeRuleSpec.groovy b/src/test/groovy/com/netflix/nebula/lint/rule/dependency/UnusedDependencyExcludeRuleSpec.groovy index 9bb58335..70737168 100644 --- a/src/test/groovy/com/netflix/nebula/lint/rule/dependency/UnusedDependencyExcludeRuleSpec.groovy +++ b/src/test/groovy/com/netflix/nebula/lint/rule/dependency/UnusedDependencyExcludeRuleSpec.groovy @@ -23,7 +23,7 @@ class UnusedDependencyExcludeRuleSpec extends AbstractRuleSpec { def rule def setup() { - rule = new UnusedDependencyExcludeRule(project: project) + rule = new UnusedDependencyExcludeRule(projectSupplier: { project }) } def 'unused exclude violates'() { diff --git a/src/test/groovy/com/netflix/nebula/lint/rule/dependency/UnusedExcludeByConfigurationRuleSpec.groovy b/src/test/groovy/com/netflix/nebula/lint/rule/dependency/UnusedExcludeByConfigurationRuleSpec.groovy index 58c35069..73db36b7 100644 --- a/src/test/groovy/com/netflix/nebula/lint/rule/dependency/UnusedExcludeByConfigurationRuleSpec.groovy +++ b/src/test/groovy/com/netflix/nebula/lint/rule/dependency/UnusedExcludeByConfigurationRuleSpec.groovy @@ -22,7 +22,7 @@ class UnusedExcludeByConfigurationRuleSpec extends AbstractRuleSpec { def rule def setup() { - rule = new UnusedExcludeByConfigurationRule(project: project) + rule = new UnusedExcludeByConfigurationRule(projectSupplier: { project }) } def 'unused exclude violates (no closure)'() {