From 439aae477dac631cdc02a8939b7cb8733a47e920 Mon Sep 17 00:00:00 2001 From: Nouran Atef Date: Thu, 5 Jun 2025 23:24:18 +0200 Subject: [PATCH 01/31] Fix Compatibility in LintService and LintGradleTask --- .../lint/plugin/AppliedFilesAstVisitor.groovy | 4 +- .../nebula/lint/plugin/LintGradleTask.groovy | 96 ++++++++++++++++--- .../lint/plugin/LintRuleRegistry.groovy | 6 +- .../nebula/lint/plugin/LintService.groovy | 76 ++++++++------- .../nebula/lint/plugin/SourceCollector.groovy | 4 +- .../rule/dependency/DependencyService.groovy | 3 +- 6 files changed, 135 insertions(+), 54 deletions(-) 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..c62f758f 100644 --- a/src/main/groovy/com/netflix/nebula/lint/plugin/AppliedFilesAstVisitor.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/plugin/AppliedFilesAstVisitor.groovy @@ -12,11 +12,11 @@ import org.gradle.api.Project */ class AppliedFilesAstVisitor extends ClassCodeVisitorSupport { - Project project + ProjectInfo project List appliedFiles = new ArrayList() Map projectVariablesMapping - AppliedFilesAstVisitor(Project project) { + AppliedFilesAstVisitor(ProjectInfo project) { this.project = project projectVariablesMapping = [ "\$projectDir" : project.projectDir.toString(), 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..3ee6b004 100644 --- a/src/main/groovy/com/netflix/nebula/lint/plugin/LintGradleTask.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/plugin/LintGradleTask.groovy @@ -18,16 +18,66 @@ package com.netflix.nebula.lint.plugin import com.netflix.nebula.lint.* 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.Project import org.gradle.api.provider.Property -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.Internal -import org.gradle.api.tasks.Optional -import org.gradle.api.tasks.TaskAction +import org.gradle.api.tasks.* import org.gradle.internal.deprecation.DeprecationLogger +//import static com.netflix.nebula.lint.StyledTextService.Styling.* + +class ProjectInfo implements Serializable{ + @Input String name + @Input String path + @InputDirectory @PathSensitive(PathSensitivity.RELATIVE) File projectDir + @InputDirectory @PathSensitive(PathSensitivity.RELATIVE) File rootDir + @InputFile @PathSensitive(PathSensitivity.RELATIVE) File buildFile + @Input List effectiveRuleNames = [] + @Input List effectiveExcludedRuleNames = [] + @Input List criticalRuleNamesForThisProject = [] + + + static ProjectInfo from(Project project ,GradleLintExtension rootProjectExtension) { + if (rootProjectExtension == null) { + throw new IllegalStateException("Root project GradleLintExtension is required but not found.") + } + + GradleLintExtension projectSpecificExtension = project.extensions.findByType(GradleLintExtension.class) ?: rootProjectExtension + List calculatedEffectiveRules + if (project.hasProperty('gradleLint.rules')) { + calculatedEffectiveRules = project.property('gradleLint.rules').toString().split(',').collect { it.trim() }.unique() + } else { + calculatedEffectiveRules = ((projectSpecificExtension.getRules() ?: []) + (projectSpecificExtension.getCriticalRules() ?: [])).unique() + } + + List propertyExcludedRules = [] + if (project.hasProperty('gradleLint.excludedRules')) { + propertyExcludedRules = project.property('gradleLint.excludedRules').toString().split(',').collect { it.trim() } + } + + List calculatedEffectiveExcludedRules = (propertyExcludedRules + (projectSpecificExtension.getExcludedRules() ?: [])).unique() + List actualCriticalRulesForThisProject = new ArrayList<>(projectSpecificExtension.getCriticalRules() ?: []) + + + return new ProjectInfo( + name: project.name, + path: project.path, + projectDir: project.projectDir, + rootDir: project.rootDir, + buildFile: project.buildFile, + effectiveRuleNames: calculatedEffectiveRules, + effectiveExcludedRuleNames: calculatedEffectiveExcludedRules, + criticalRuleNamesForThisProject: actualCriticalRulesForThisProject + ) + } +} -import static com.netflix.nebula.lint.StyledTextService.Styling.* +class ProjectTree { + @Nested + List allProjects + + ProjectTree(List allProjects) { + this.allProjects = allProjects + } +} abstract class LintGradleTask extends DefaultTask { @Input @@ -45,25 +95,46 @@ abstract class LintGradleTask extends DefaultTask { @Input abstract Property getProjectRootDir() + @Nested + abstract Property getProjectTree() + + protected ProjectTree computeProjectTree(Project project) { + // TODO-Nouran: collect project and subproject information + // def projectInfos = ([project] + project.subprojects).collect {ProjectInfo.from(it)} + // return new ProjectTree( projectInfos) + GradleLintExtension rootExt = project.extensions.findByType(GradleLintExtension.class) + if (rootExt == null) { + throw new IllegalStateException("GradleLintExtension not found on root project '${project.path}'. Please ensure the lint plugin is applied to the root project.") + } + List projectInfos = ([project] + project.getSubprojects().asList()).collect {Project p -> ProjectInfo.from(p, rootExt) } + return new ProjectTree(projectInfos) + } + LintGradleTask() { failOnWarning.convention(false) onlyCriticalRules.convention(false) + getProjectRootDir().set(getProject().getRootProject().getProjectDir()) + projectTree.set(getProject().getProviders().provider(() -> computeProjectTree(getProject().getRootProject()))) + // projectTree.set(getProject().getProviders().provider(() -> computeProjectTree(getProject()))) group = 'lint' - try { + /* try { def method = Task.getMethod("notCompatibleWithConfigurationCache") method.invoke(this) } catch (NoSuchMethodException ignore) { - } + }*/ } @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 } + File rootDirFile = projectRootDir.get() + def patchAction = new GradleLintPatchAction(rootDirFile) + def infoAction = new GradleLintInfoBrokerAction(rootDirFile) - (getListeners() + new GradleLintPatchAction(project) + new GradleLintInfoBrokerAction(project) + consoleOutputAction).each { + (getListeners() + patchAction + infoAction + consoleOutputAction).each { it.lintFinished(violations) } } @@ -90,7 +161,6 @@ abstract class LintGradleTask extends DefaultTask { textOutput.println('Because none were serious, the build\'s overall status was unaffected.\n') } } - violations.groupBy { it.file }.each { buildFile, violationsByFile -> violationsByFile.each { v -> 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..b15a590b 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 project, boolean critical) { assert classLoader != null def ruleDescriptor = findRuleDescriptor(ruleId) if (ruleDescriptor == null) @@ -63,7 +65,7 @@ class LintRuleRegistry { try { Rule r = (Rule) classLoader.loadClass(implClassName).newInstance() if(r instanceof ModelAwareGradleLintRule) { - (r as ModelAwareGradleLintRule).project = project + (r as ModelAwareGradleLintRule).project = project.get() } if(r instanceof GradleLintRule) { 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..a4871c45 100644 --- a/src/main/groovy/com/netflix/nebula/lint/plugin/LintService.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/plugin/LintService.groovy @@ -16,6 +16,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 @@ -30,7 +31,8 @@ import org.codenarc.ruleset.ListRuleSet import org.codenarc.ruleset.RuleSet 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() @@ -39,14 +41,14 @@ class LintService { * An analyzer that can be used over and over again against multiple subprojects, compiling the results, and recording * the affected files according to which files the violation fixes touch */ - class ReportableAnalyzer extends AbstractSourceAnalyzer { + class ReportableAnalyzer extends AbstractSourceAnalyzer implements Serializable { DirectoryResults resultsForRootProject - ReportableAnalyzer(Project project) { - resultsForRootProject = new DirectoryResults(project.projectDir.absolutePath) + ReportableAnalyzer(File rootDir) { + resultsForRootProject = new DirectoryResults(rootDir.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,47 +77,53 @@ 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) - } + private RuleSet ruleSetForProject(ProjectInfo p, boolean onlyCriticalRules) { + if (p.buildFile == null || !p.buildFile.exists()) { + LOGGER.warn("Build file for project '{}' (path: '{}') is null or does not exist. Returning empty ruleset.", p.name, p.path) + return new ListRuleSet([]) + } - def rules = (p.hasProperty('gradleLint.rules') ? p.property('gradleLint.rules') : null)?.toString()?.split(',')?.toList() ?: - extension.rules + extension.criticalRules + List rulesToConsider = p.effectiveRuleNames ?: [] - def includedRules = rules.unique() - .collect { registry.buildRules(it, p, extension.criticalRules.contains(it)) } - .flatten() as List + Supplier projectSupplier = { -> null } as Supplier - if (onlyCriticalRules) { - includedRules = includedRules.findAll { it instanceof GradleLintRule && it.critical } - } + List includedRules = rulesToConsider.unique() + .collect { String ruleName -> + this.registry.buildRules(ruleName, projectSupplier, p.criticalRuleNamesForThisProject.contains(ruleName)) + } + .flatten() as List - def excludedRules = (p.hasProperty('gradleLint.excludedRules') ? - p.property('gradleLint.excludedRules').toString().split(',').toList() : []) + extension.excludedRules - if (!excludedRules.isEmpty()) - includedRules.retainAll { !excludedRules.contains(it.name) } + if (onlyCriticalRules) { + includedRules = includedRules.findAll { Rule rule -> rule.isCritical() } + } + List excludedRuleNames = p.effectiveExcludedRuleNames ?: [] - return RuleSetFactory.configureRuleSet(includedRules) + if (!excludedRuleNames.isEmpty()) { + includedRules.retainAll { Rule rule -> !excludedRuleNames.contains(rule.getName()) } } - return new ListRuleSet([]) + + return RuleSetFactory.configureRuleSet(includedRules) } - RuleSet ruleSet(Project project) { + + RuleSet ruleSet(ProjectTree projectTree) { def ruleSet = new CompositeRuleSet() - ([project] + project.subprojects).each { p -> ruleSet.addRuleSet(ruleSetForProject(p, false)) } + projectTree.allProjects.each { ProjectInfo pInfo -> + ruleSet.addRuleSet(ruleSetForProject(pInfo, false)) + } return ruleSet } - Results lint(Project project, boolean onlyCriticalRules) { - def analyzer = new ReportableAnalyzer(project) - - ([project] + project.subprojects).each { p -> + Results lint(ProjectTree projectTree , boolean onlyCriticalRules) { + if (projectTree.allProjects.isEmpty()) { + return new DirectoryResults("empty_project_tree_results") // Return empty results + } + File rootDir = projectTree.allProjects.first().rootDir + def analyzer = new ReportableAnalyzer(rootDir) + // assert projectTree.getOrNull() != null + //assert !projectTree.get().allProjects.empty + //List projectsToLint = [project] + project.subprojects + projectTree.allProjects.each {p -> def files = SourceCollector.getAllFiles(p.buildFile, p) def buildFiles = new BuildFiles(files) def ruleSet = ruleSetForProject(p, onlyCriticalRules) 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..7208f0aa 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) } 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..f563f8f2 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 @@ -3,6 +3,7 @@ package com.netflix.nebula.lint.rule.dependency import com.netflix.nebula.interop.GradleKt import com.netflix.nebula.lint.SourceSetUtils import com.netflix.nebula.lint.VersionNumber +import com.netflix.nebula.lint.plugin.ProjectInfo import groovy.transform.Memoized import groovyx.gpars.GParsPool import org.gradle.api.Project @@ -45,7 +46,7 @@ class DependencyService { return extension.dependencyService } - static synchronized void removeForProject(Project project) { + static synchronized void removeForProject(ProjectInfo project) { def extension = project.extensions.findByType(DependencyServiceExtension) if (extension) { extension.dependencyService = null From 85b579ef2d2a89449f9f1f72226efea3062891ef Mon Sep 17 00:00:00 2001 From: Nouran Atef Date: Mon, 16 Jun 2025 23:21:47 +0200 Subject: [PATCH 02/31] main --- .../lint/plugin/AppliedFilesAstVisitor.groovy | 4 +- .../nebula/lint/plugin/LintGradleTask.groovy | 96 +++---------------- .../lint/plugin/LintRuleRegistry.groovy | 6 +- .../nebula/lint/plugin/LintService.groovy | 76 +++++++-------- .../nebula/lint/plugin/SourceCollector.groovy | 4 +- .../rule/dependency/DependencyService.groovy | 3 +- 6 files changed, 54 insertions(+), 135 deletions(-) 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 c62f758f..b351f839 100644 --- a/src/main/groovy/com/netflix/nebula/lint/plugin/AppliedFilesAstVisitor.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/plugin/AppliedFilesAstVisitor.groovy @@ -12,11 +12,11 @@ import org.gradle.api.Project */ class AppliedFilesAstVisitor extends ClassCodeVisitorSupport { - ProjectInfo project + Project project List appliedFiles = new ArrayList() Map projectVariablesMapping - AppliedFilesAstVisitor(ProjectInfo project) { + AppliedFilesAstVisitor(Project project) { this.project = project projectVariablesMapping = [ "\$projectDir" : project.projectDir.toString(), 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 3ee6b004..ee4d4604 100644 --- a/src/main/groovy/com/netflix/nebula/lint/plugin/LintGradleTask.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/plugin/LintGradleTask.groovy @@ -18,66 +18,16 @@ package com.netflix.nebula.lint.plugin 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.* +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.Optional +import org.gradle.api.tasks.TaskAction import org.gradle.internal.deprecation.DeprecationLogger -//import static com.netflix.nebula.lint.StyledTextService.Styling.* - -class ProjectInfo implements Serializable{ - @Input String name - @Input String path - @InputDirectory @PathSensitive(PathSensitivity.RELATIVE) File projectDir - @InputDirectory @PathSensitive(PathSensitivity.RELATIVE) File rootDir - @InputFile @PathSensitive(PathSensitivity.RELATIVE) File buildFile - @Input List effectiveRuleNames = [] - @Input List effectiveExcludedRuleNames = [] - @Input List criticalRuleNamesForThisProject = [] - - - static ProjectInfo from(Project project ,GradleLintExtension rootProjectExtension) { - if (rootProjectExtension == null) { - throw new IllegalStateException("Root project GradleLintExtension is required but not found.") - } - - GradleLintExtension projectSpecificExtension = project.extensions.findByType(GradleLintExtension.class) ?: rootProjectExtension - List calculatedEffectiveRules - if (project.hasProperty('gradleLint.rules')) { - calculatedEffectiveRules = project.property('gradleLint.rules').toString().split(',').collect { it.trim() }.unique() - } else { - calculatedEffectiveRules = ((projectSpecificExtension.getRules() ?: []) + (projectSpecificExtension.getCriticalRules() ?: [])).unique() - } - - List propertyExcludedRules = [] - if (project.hasProperty('gradleLint.excludedRules')) { - propertyExcludedRules = project.property('gradleLint.excludedRules').toString().split(',').collect { it.trim() } - } - - List calculatedEffectiveExcludedRules = (propertyExcludedRules + (projectSpecificExtension.getExcludedRules() ?: [])).unique() - List actualCriticalRulesForThisProject = new ArrayList<>(projectSpecificExtension.getCriticalRules() ?: []) - - - return new ProjectInfo( - name: project.name, - path: project.path, - projectDir: project.projectDir, - rootDir: project.rootDir, - buildFile: project.buildFile, - effectiveRuleNames: calculatedEffectiveRules, - effectiveExcludedRuleNames: calculatedEffectiveExcludedRules, - criticalRuleNamesForThisProject: actualCriticalRulesForThisProject - ) - } -} -class ProjectTree { - @Nested - List allProjects - - ProjectTree(List allProjects) { - this.allProjects = allProjects - } -} +import static com.netflix.nebula.lint.StyledTextService.Styling.* abstract class LintGradleTask extends DefaultTask { @Input @@ -95,46 +45,25 @@ abstract class LintGradleTask extends DefaultTask { @Input abstract Property getProjectRootDir() - @Nested - abstract Property getProjectTree() - - protected ProjectTree computeProjectTree(Project project) { - // TODO-Nouran: collect project and subproject information - // def projectInfos = ([project] + project.subprojects).collect {ProjectInfo.from(it)} - // return new ProjectTree( projectInfos) - GradleLintExtension rootExt = project.extensions.findByType(GradleLintExtension.class) - if (rootExt == null) { - throw new IllegalStateException("GradleLintExtension not found on root project '${project.path}'. Please ensure the lint plugin is applied to the root project.") - } - List projectInfos = ([project] + project.getSubprojects().asList()).collect {Project p -> ProjectInfo.from(p, rootExt) } - return new ProjectTree(projectInfos) - } - LintGradleTask() { failOnWarning.convention(false) onlyCriticalRules.convention(false) - getProjectRootDir().set(getProject().getRootProject().getProjectDir()) - projectTree.set(getProject().getProviders().provider(() -> computeProjectTree(getProject().getRootProject()))) - // projectTree.set(getProject().getProviders().provider(() -> computeProjectTree(getProject()))) group = 'lint' - /* try { + try { def method = Task.getMethod("notCompatibleWithConfigurationCache") method.invoke(this) } catch (NoSuchMethodException ignore) { - }*/ + } } @TaskAction void lint() { - + //TODO: address Invocation of Task.project at execution time has been deprecated. DeprecationLogger.whileDisabled { - def violations = new LintService().lint(projectTree.get(),onlyCriticalRules.get()).violations + def violations = new LintService().lint(project, onlyCriticalRules.get()).violations .unique { v1, v2 -> v1.is(v2) ? 0 : 1 } - File rootDirFile = projectRootDir.get() - def patchAction = new GradleLintPatchAction(rootDirFile) - def infoAction = new GradleLintInfoBrokerAction(rootDirFile) - (getListeners() + patchAction + infoAction + consoleOutputAction).each { + (getListeners() + new GradleLintPatchAction(project) + new GradleLintInfoBrokerAction(project) + consoleOutputAction).each { it.lintFinished(violations) } } @@ -161,6 +90,7 @@ abstract class LintGradleTask extends DefaultTask { textOutput.println('Because none were serious, the build\'s overall status was unaffected.\n') } } + violations.groupBy { it.file }.each { buildFile, violationsByFile -> violationsByFile.each { v -> 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 b15a590b..6fbee6aa 100644 --- a/src/main/groovy/com/netflix/nebula/lint/plugin/LintRuleRegistry.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/plugin/LintRuleRegistry.groovy @@ -21,8 +21,6 @@ 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 @@ -46,7 +44,7 @@ class LintRuleRegistry { - List buildRules(String ruleId, Supplier project, boolean critical) { + List buildRules(String ruleId, Project project, boolean critical) { assert classLoader != null def ruleDescriptor = findRuleDescriptor(ruleId) if (ruleDescriptor == null) @@ -65,7 +63,7 @@ class LintRuleRegistry { try { Rule r = (Rule) classLoader.loadClass(implClassName).newInstance() if(r instanceof ModelAwareGradleLintRule) { - (r as ModelAwareGradleLintRule).project = project.get() + (r as ModelAwareGradleLintRule).project = project } if(r instanceof GradleLintRule) { 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 a4871c45..03e78e52 100644 --- a/src/main/groovy/com/netflix/nebula/lint/plugin/LintService.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/plugin/LintService.groovy @@ -16,7 +16,6 @@ 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 @@ -31,8 +30,7 @@ import org.codenarc.ruleset.ListRuleSet import org.codenarc.ruleset.RuleSet import org.codenarc.source.SourceString import org.gradle.api.Project - -import java.util.function.Supplier +import org.gradle.api.UnknownDomainObjectException class LintService { def registry = new LintRuleRegistry() @@ -41,14 +39,14 @@ class LintService { * An analyzer that can be used over and over again against multiple subprojects, compiling the results, and recording * the affected files according to which files the violation fixes touch */ - class ReportableAnalyzer extends AbstractSourceAnalyzer implements Serializable { + class ReportableAnalyzer extends AbstractSourceAnalyzer { DirectoryResults resultsForRootProject - ReportableAnalyzer(File rootDir) { - resultsForRootProject = new DirectoryResults(rootDir.absolutePath) + ReportableAnalyzer(Project project) { + resultsForRootProject = new DirectoryResults(project.projectDir.absolutePath) } - Results analyze(ProjectInfo analyzedProject, String source, RuleSet ruleSet) { + Results analyze(Project analyzedProject, String source, RuleSet ruleSet) { DirectoryResults results if (resultsForRootProject.path != analyzedProject.projectDir.absolutePath) { results = new DirectoryResults(analyzedProject.projectDir.absolutePath) @@ -77,53 +75,47 @@ class LintService { } } - private RuleSet ruleSetForProject(ProjectInfo p, boolean onlyCriticalRules) { - if (p.buildFile == null || !p.buildFile.exists()) { - LOGGER.warn("Build file for project '{}' (path: '{}') is null or does not exist. Returning empty ruleset.", p.name, p.path) - return new ListRuleSet([]) - } + 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) + } - List rulesToConsider = p.effectiveRuleNames ?: [] + def rules = (p.hasProperty('gradleLint.rules') ? p.property('gradleLint.rules') : null)?.toString()?.split(',')?.toList() ?: + extension.rules + extension.criticalRules - Supplier projectSupplier = { -> null } as Supplier + def includedRules = rules.unique() + .collect { registry.buildRules(it, p, extension.criticalRules.contains(it)) } + .flatten() as List - List includedRules = rulesToConsider.unique() - .collect { String ruleName -> - this.registry.buildRules(ruleName, projectSupplier, p.criticalRuleNamesForThisProject.contains(ruleName)) - } - .flatten() as List + if (onlyCriticalRules) { + includedRules = includedRules.findAll { it instanceof GradleLintRule && it.critical } + } - if (onlyCriticalRules) { - includedRules = includedRules.findAll { Rule rule -> rule.isCritical() } - } - List excludedRuleNames = p.effectiveExcludedRuleNames ?: [] + def excludedRules = (p.hasProperty('gradleLint.excludedRules') ? + p.property('gradleLint.excludedRules').toString().split(',').toList() : []) + extension.excludedRules + if (!excludedRules.isEmpty()) + includedRules.retainAll { !excludedRules.contains(it.name) } - if (!excludedRuleNames.isEmpty()) { - includedRules.retainAll { Rule rule -> !excludedRuleNames.contains(rule.getName()) } + return RuleSetFactory.configureRuleSet(includedRules) } - - return RuleSetFactory.configureRuleSet(includedRules) + return new ListRuleSet([]) } - - RuleSet ruleSet(ProjectTree projectTree) { + RuleSet ruleSet(Project project) { def ruleSet = new CompositeRuleSet() - projectTree.allProjects.each { ProjectInfo pInfo -> - ruleSet.addRuleSet(ruleSetForProject(pInfo, false)) - } + ([project] + project.subprojects).each { p -> ruleSet.addRuleSet(ruleSetForProject(p, false)) } return ruleSet } - Results lint(ProjectTree projectTree , boolean onlyCriticalRules) { - if (projectTree.allProjects.isEmpty()) { - return new DirectoryResults("empty_project_tree_results") // Return empty results - } - File rootDir = projectTree.allProjects.first().rootDir - def analyzer = new ReportableAnalyzer(rootDir) - // assert projectTree.getOrNull() != null - //assert !projectTree.get().allProjects.empty - //List projectsToLint = [project] + project.subprojects - projectTree.allProjects.each {p -> + Results lint(Project project, boolean onlyCriticalRules) { + def analyzer = new ReportableAnalyzer(project) + + ([project] + project.subprojects).each { p -> def files = SourceCollector.getAllFiles(p.buildFile, p) def buildFiles = new BuildFiles(files) def ruleSet = ruleSetForProject(p, onlyCriticalRules) 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 7208f0aa..59e67b46 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, ProjectInfo projectInfo) { + static List getAllFiles(File buildFile, Project project) { 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(projectInfo) + AppliedFilesAstVisitor visitor = new AppliedFilesAstVisitor(project) visitor.visitClass(classNode) result.addAll(visitor.appliedFiles) } 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 f563f8f2..1f72b5ae 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 @@ -3,7 +3,6 @@ package com.netflix.nebula.lint.rule.dependency import com.netflix.nebula.interop.GradleKt import com.netflix.nebula.lint.SourceSetUtils import com.netflix.nebula.lint.VersionNumber -import com.netflix.nebula.lint.plugin.ProjectInfo import groovy.transform.Memoized import groovyx.gpars.GParsPool import org.gradle.api.Project @@ -46,7 +45,7 @@ class DependencyService { return extension.dependencyService } - static synchronized void removeForProject(ProjectInfo project) { + static synchronized void removeForProject(Project project) { def extension = project.extensions.findByType(DependencyServiceExtension) if (extension) { extension.dependencyService = null From a500a1c3101280c4c390b9cfbf3a6afa10304570 Mon Sep 17 00:00:00 2001 From: Nouran Atef Date: Fri, 20 Jun 2025 20:48:18 +0200 Subject: [PATCH 03/31] Add `ProjectInfo` and `ProjectTree` classes --- .../nebula/lint/plugin/LintGradleTask.groovy | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) 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..f4472ca3 100644 --- a/src/main/groovy/com/netflix/nebula/lint/plugin/LintGradleTask.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/plugin/LintGradleTask.groovy @@ -18,6 +18,7 @@ package com.netflix.nebula.lint.plugin 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 @@ -139,3 +140,27 @@ abstract class LintGradleTask extends DefaultTask { } } } +class ProjectInfo implements Serializable{ + String name + String path + File rootDir + File buildFile + File projectDir + + static ProjectInfo from (Project project){ + return new ProjectInfo( + name:project.name, + path:project.path, + rootDir:project.rootDir, + buildFile: project.buildFile, + projectDir:project.projectDir + ) + } +} +class ProjectTree{ + List allProjects + + ProjectTree(List allProjects){ + this.allProjects = allProjects + } +} From 97c8efab4e34acdbb735c20b079a18a5ede0838f Mon Sep 17 00:00:00 2001 From: Nouran Atef Date: Fri, 20 Jun 2025 22:02:34 +0200 Subject: [PATCH 04/31] Integrate `ProjectTree` computation in `LintGradleTask` --- .../netflix/nebula/lint/plugin/LintGradleTask.groovy | 10 ++++++++++ 1 file changed, 10 insertions(+) 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 f4472ca3..763825e7 100644 --- a/src/main/groovy/com/netflix/nebula/lint/plugin/LintGradleTask.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/plugin/LintGradleTask.groovy @@ -46,9 +46,14 @@ abstract class LintGradleTask extends DefaultTask { @Input abstract Property getProjectRootDir() + @Input + abstract Property getProjectTree() + LintGradleTask() { failOnWarning.convention(false) onlyCriticalRules.convention(false) + projectTree.set(project.provider {computeProjectTree(project)}) + projectRootDir.set(project.rootDir) group = 'lint' try { def method = Task.getMethod("notCompatibleWithConfigurationCache") @@ -57,6 +62,11 @@ abstract class LintGradleTask extends DefaultTask { } } + ProjectTree computeProjectTree(Project project){ + List projectInfos = ([project] + project.getSubprojects().asList()).collect{Project p -> ProjectInfo.from(p)} + return new ProjectTree(projectInfos) + } + @TaskAction void lint() { //TODO: address Invocation of Task.project at execution time has been deprecated. From 520674743c3a149aee4f1055dd7c41f643a38ded Mon Sep 17 00:00:00 2001 From: Nouran Atef Date: Sun, 22 Jun 2025 07:12:06 +0200 Subject: [PATCH 05/31] Add `getAllFiles` method and`AppliedFilesAstVisitor` constructor with `ProjectInfo` --- .../lint/plugin/AppliedFilesAstVisitor.groovy | 8 ++++++++ .../nebula/lint/plugin/SourceCollector.groovy | 18 ++++++++++++++++++ 2 files changed, 26 insertions(+) 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..e6e67daa 100644 --- a/src/main/groovy/com/netflix/nebula/lint/plugin/AppliedFilesAstVisitor.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/plugin/AppliedFilesAstVisitor.groovy @@ -25,6 +25,14 @@ class AppliedFilesAstVisitor extends ClassCodeVisitorSupport { "\$project.rootDir" : project.rootDir.toString(), ] } + AppliedFilesAstVisitor(ProjectInfo project) { + projectVariablesMapping = [ + "\$projectDir" : project.projectDir.toString(), + "\$project.projectDir" : project.projectDir.toString(), + "\$rootDir" : project.rootDir.toString(), + "\$project.rootDir" : project.rootDir.toString(), + ] + } void visitApplyFrom(String from) { if (! isHttpLink(from)) { 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..4db00bd1 100644 --- a/src/main/groovy/com/netflix/nebula/lint/plugin/SourceCollector.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/plugin/SourceCollector.groovy @@ -12,6 +12,24 @@ 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, ProjectInfo project) { + if (buildFile.exists()) { + List result = new ArrayList<>() + result.add(buildFile) + SourceCode sourceCode = new SourceString(buildFile.text) + ModuleNode ast = sourceCode.getAst() + if (ast != null && ast.getClasses() != null) { + for (ClassNode classNode : ast.getClasses()) { + AppliedFilesAstVisitor visitor = new AppliedFilesAstVisitor(project) + visitor.visitClass(classNode) + result.addAll(visitor.appliedFiles) + } + } + return result + } else { + return Collections.emptyList() + } + } static List getAllFiles(File buildFile, Project project) { if (buildFile.exists()) { List result = new ArrayList<>() From 2e18e54b24d2b83721b110699b40abc27af3cf94 Mon Sep 17 00:00:00 2001 From: Nouran Atef Date: Sun, 22 Jun 2025 19:06:01 +0200 Subject: [PATCH 06/31] Conditionally Remove DependencyService for Model-Aware Rules --- .../netflix/nebula/lint/plugin/LintService.groovy | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) 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..1e13bcbe 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 @@ -120,15 +121,20 @@ class LintService { 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){ + DependencyService.removeForProject(p) + } } } From 3ca28beac409bc2b4a333eeba01ad159c8216129 Mon Sep 17 00:00:00 2001 From: Nouran Atef Date: Sun, 22 Jun 2025 23:17:48 +0200 Subject: [PATCH 07/31] Add Overloaded removeForProject Method and buildRules method with Supplier` --- .../netflix/nebula/lint/plugin/LintRuleRegistry.groovy | 10 +++++++++- .../lint/rule/dependency/DependencyService.groovy | 7 ++++++- 2 files changed, 15 insertions(+), 2 deletions(-) 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..77f9e2be 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,8 @@ class LintRuleRegistry { - List buildRules(String ruleId, Project project, boolean critical) { + List buildRules(String ruleId, Supplier projectSupplier, boolean critical) { + Project project = projectSupplier.get() assert classLoader != null def ruleDescriptor = findRuleDescriptor(ruleId) if (ruleDescriptor == null) @@ -80,4 +83,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/rule/dependency/DependencyService.groovy b/src/main/groovy/com/netflix/nebula/lint/rule/dependency/DependencyService.groovy index 1f72b5ae..49b326c8 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 From bd2837a2c418ef8c0a44bc6ad495f1a0a8b0ee73 Mon Sep 17 00:00:00 2001 From: Nouran Atef Date: Mon, 23 Jun 2025 15:40:28 +0200 Subject: [PATCH 08/31] Integrate `ProjectInfo` into `GradleLintPatchAction` --- .../nebula/lint/GradleLintPatchAction.groovy | 14 ++++++++++++-- .../nebula/lint/plugin/LintGradleTask.groovy | 4 +++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/main/groovy/com/netflix/nebula/lint/GradleLintPatchAction.groovy b/src/main/groovy/com/netflix/nebula/lint/GradleLintPatchAction.groovy index b43fb97d..a97678c2 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,21 @@ 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.buildDir : project.layout.buildDirectory.asFile.getOrElse(new File(project.projectDir, "build")) buildDir.mkdirs() try { def patch = patch(violations*.fixes.flatten() as List) @@ -252,7 +262,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/LintGradleTask.groovy b/src/main/groovy/com/netflix/nebula/lint/plugin/LintGradleTask.groovy index 763825e7..974bda6e 100644 --- a/src/main/groovy/com/netflix/nebula/lint/plugin/LintGradleTask.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/plugin/LintGradleTask.groovy @@ -156,6 +156,7 @@ class ProjectInfo implements Serializable{ File rootDir File buildFile File projectDir + File buildDir static ProjectInfo from (Project project){ return new ProjectInfo( @@ -163,7 +164,8 @@ class ProjectInfo implements Serializable{ path:project.path, rootDir:project.rootDir, buildFile: project.buildFile, - projectDir:project.projectDir + projectDir:project.projectDir, + buildDir: project.layout.buildDirectory.asFile.getOrElse(new File(project.projectDir, "build")) ) } } From 941e341a5ef6b810627028d20f098d41dffee2e3 Mon Sep 17 00:00:00 2001 From: Nouran Atef Date: Mon, 23 Jun 2025 15:51:36 +0200 Subject: [PATCH 09/31] Integrate projectInfo and ProjectTree instead of project --- .../lint/plugin/AppliedFilesAstVisitor.groovy | 4 +- .../nebula/lint/plugin/LintGradleTask.groovy | 31 ++++++++- .../lint/plugin/LintRuleRegistry.groovy | 5 +- .../nebula/lint/plugin/LintService.groovy | 65 ++++++++++++------- .../rule/dependency/DependencyService.groovy | 2 +- 5 files changed, 73 insertions(+), 34 deletions(-) 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 e6e67daa..62391a62 100644 --- a/src/main/groovy/com/netflix/nebula/lint/plugin/AppliedFilesAstVisitor.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/plugin/AppliedFilesAstVisitor.groovy @@ -12,12 +12,11 @@ import org.gradle.api.Project */ class AppliedFilesAstVisitor extends ClassCodeVisitorSupport { - Project project + ProjectInfo project List appliedFiles = new ArrayList() Map projectVariablesMapping AppliedFilesAstVisitor(Project project) { - this.project = project projectVariablesMapping = [ "\$projectDir" : project.projectDir.toString(), "\$project.projectDir" : project.projectDir.toString(), @@ -26,6 +25,7 @@ class AppliedFilesAstVisitor extends ClassCodeVisitorSupport { ] } AppliedFilesAstVisitor(ProjectInfo project) { + this.project = project projectVariablesMapping = [ "\$projectDir" : project.projectDir.toString(), "\$project.projectDir" : project.projectDir.toString(), 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 974bda6e..78e268d8 100644 --- a/src/main/groovy/com/netflix/nebula/lint/plugin/LintGradleTask.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/plugin/LintGradleTask.groovy @@ -27,6 +27,7 @@ import org.gradle.api.tasks.Internal import org.gradle.api.tasks.Optional import org.gradle.api.tasks.TaskAction import org.gradle.internal.deprecation.DeprecationLogger +import org.gradle.api.UnknownDomainObjectException import static com.netflix.nebula.lint.StyledTextService.Styling.* @@ -49,6 +50,12 @@ abstract class LintGradleTask extends DefaultTask { @Input abstract Property getProjectTree() + @Internal + Project getRootProject() { + return project.rootProject + } + + LintGradleTask() { failOnWarning.convention(false) onlyCriticalRules.convention(false) @@ -70,8 +77,9 @@ abstract class LintGradleTask extends DefaultTask { @TaskAction void lint() { //TODO: address Invocation of Task.project at execution time has been deprecated. + def rootProject = getRootProject() DeprecationLogger.whileDisabled { - def violations = new LintService().lint(project, onlyCriticalRules.get()).violations + def violations = new LintService().lint(projectTree.get(),rootProject, onlyCriticalRules.get()).violations .unique { v1, v2 -> v1.is(v2) ? 0 : 1 } (getListeners() + new GradleLintPatchAction(project) + new GradleLintInfoBrokerAction(project) + consoleOutputAction).each { @@ -156,16 +164,33 @@ class ProjectInfo implements Serializable{ File rootDir File buildFile File projectDir - File buildDir + GradleLintExtension extension + Map properties static ProjectInfo from (Project project){ + GradleLintExtension extension + try { + extension = project.extensions.getByType(GradleLintExtension) + } catch (UnknownDomainObjectException ignored) { + extension = project.rootProject.extensions.getByType(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, - buildDir: project.layout.buildDirectory.asFile.getOrElse(new File(project.projectDir, "build")) + extension: extension, + properties: properties ) } } 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 77f9e2be..76d5528e 100644 --- a/src/main/groovy/com/netflix/nebula/lint/plugin/LintRuleRegistry.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/plugin/LintRuleRegistry.groovy @@ -47,7 +47,6 @@ class LintRuleRegistry { List buildRules(String ruleId, Supplier projectSupplier, boolean critical) { - Project project = projectSupplier.get() assert classLoader != null def ruleDescriptor = findRuleDescriptor(ruleId) if (ruleDescriptor == null) @@ -60,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).project = projectSupplier.get() } if(r instanceof GradleLintRule) { 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 1e13bcbe..4e34b138 100644 --- a/src/main/groovy/com/netflix/nebula/lint/plugin/LintService.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/plugin/LintService.groovy @@ -33,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() @@ -43,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,30 +77,34 @@ class LintService { [] } } + Project findProjectByPath(Project rootProject, String path) { + if (rootProject.path == path) { + return rootProject + } + return rootProject.subprojects.find { it.path == path } + } - 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) - } + private Supplier createProjectSupplier(Project rootProject, String projectPath) { + return { -> findProjectByPath(rootProject, projectPath) } as Supplier + } - def rules = (p.hasProperty('gradleLint.rules') ? p.property('gradleLint.rules') : null)?.toString()?.split(',')?.toList() ?: + private RuleSet ruleSetForProject(ProjectInfo projectInfo, Project rootProject,boolean onlyCriticalRules) { + if (projectInfo.buildFile.exists()) { + def extension = projectInfo.extension + + def rules = (projectInfo.properties['gradleLint.rules'])?.toString()?.split(',')?.toList() ?: extension.rules + extension.criticalRules + Supplier projectSupplier = createProjectSupplier(rootProject, projectInfo.path) def includedRules = rules.unique() - .collect { registry.buildRules(it, p, extension.criticalRules.contains(it)) } + .collect { registry.buildRules(it,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,20 +112,28 @@ class LintService { } return new ListRuleSet([]) } + private RuleSet ruleSetForProject(Project project, boolean onlyCriticalRules) { + return ruleSetForProject({ project } as Supplier, onlyCriticalRules) + } - 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,projectTree, false))} + return ruleSet } - Results lint(Project project, boolean onlyCriticalRules) { - def analyzer = new ReportableAnalyzer(project) - ([project] + project.subprojects).each { p -> + Results lint(ProjectTree projectTree,Project rootProject ,boolean onlyCriticalRules) { + ProjectInfo rootProjectInfo = projectTree.allProjects.find { it.path == ":" } + def analyzer = new ReportableAnalyzer(rootProjectInfo) + + projectTree.allProjects.each { p -> + Supplier projectSupplier = createProjectSupplier(rootProject, p.path) def files = SourceCollector.getAllFiles(p.buildFile, p) def buildFiles = new BuildFiles(files) - def ruleSet = ruleSetForProject(p, onlyCriticalRules) + def ruleSet = ruleSetForProject(p,rootProject, onlyCriticalRules) if (!ruleSet.rules.isEmpty()) { boolean containsModelAwareRule = false // establish which file we are linting for each rule @@ -133,11 +147,12 @@ class LintService { } analyzer.analyze(p, buildFiles.text, ruleSet) if (containsModelAwareRule){ - DependencyService.removeForProject(p) + Project project = projectSupplier.get() + DependencyService.removeForProject(project) } } } return analyzer.resultsForRootProject } -} +} \ No newline at end of file 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 49b326c8..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 @@ -707,4 +707,4 @@ class DependencyService { } } } -} +} \ No newline at end of file From 77995b666eca50dc7bdc75bb6fcff77929b3789f Mon Sep 17 00:00:00 2001 From: Nouran Atef Date: Mon, 23 Jun 2025 18:11:16 +0200 Subject: [PATCH 10/31] Integrate projectInfo and ProjectTree instead of project --- .../nebula/lint/plugin/LintGradleTask.groovy | 28 +++++++++------- .../nebula/lint/plugin/LintService.groovy | 32 +++++++------------ 2 files changed, 29 insertions(+), 31 deletions(-) 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 78e268d8..9500e90a 100644 --- a/src/main/groovy/com/netflix/nebula/lint/plugin/LintGradleTask.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/plugin/LintGradleTask.groovy @@ -29,6 +29,8 @@ import org.gradle.api.tasks.TaskAction import org.gradle.internal.deprecation.DeprecationLogger import org.gradle.api.UnknownDomainObjectException +import java.util.function.Supplier + import static com.netflix.nebula.lint.StyledTextService.Styling.* abstract class LintGradleTask extends DefaultTask { @@ -59,7 +61,7 @@ abstract class LintGradleTask extends DefaultTask { LintGradleTask() { failOnWarning.convention(false) onlyCriticalRules.convention(false) - projectTree.set(project.provider {computeProjectTree(project)}) + projectTree.set(project.provider {ProjectTree.from(project)}) projectRootDir.set(project.rootDir) group = 'lint' try { @@ -69,17 +71,10 @@ abstract class LintGradleTask extends DefaultTask { } } - ProjectTree computeProjectTree(Project project){ - List projectInfos = ([project] + project.getSubprojects().asList()).collect{Project p -> ProjectInfo.from(p)} - return new ProjectTree(projectInfos) - } - @TaskAction void lint() { - //TODO: address Invocation of Task.project at execution time has been deprecated. - def rootProject = getRootProject() DeprecationLogger.whileDisabled { - def violations = new LintService().lint(projectTree.get(),rootProject, 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 { @@ -166,7 +161,7 @@ class ProjectInfo implements Serializable{ File projectDir GradleLintExtension extension Map properties - + Supplier projectSupplier static ProjectInfo from (Project project){ GradleLintExtension extension try { @@ -190,14 +185,25 @@ class ProjectInfo implements Serializable{ buildFile: project.buildFile, projectDir:project.projectDir, extension: extension, - properties: properties + properties: properties, + projectSupplier: { project } ) + } + + } class ProjectTree{ List allProjects + ProjectTree(List allProjects){ this.allProjects = allProjects + + } + + static from(Project project) { + List projectInfos = ([project] + project.getSubprojects().asList()).collect{Project p -> ProjectInfo.from(p)} + return new ProjectTree(projectInfos) } } 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 4e34b138..42bfc638 100644 --- a/src/main/groovy/com/netflix/nebula/lint/plugin/LintService.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/plugin/LintService.groovy @@ -77,27 +77,19 @@ class LintService { [] } } - Project findProjectByPath(Project rootProject, String path) { - if (rootProject.path == path) { - return rootProject - } - return rootProject.subprojects.find { it.path == path } - } - private Supplier createProjectSupplier(Project rootProject, String projectPath) { - return { -> findProjectByPath(rootProject, projectPath) } as Supplier - } - private RuleSet ruleSetForProject(ProjectInfo projectInfo, Project rootProject,boolean onlyCriticalRules) { + + + private RuleSet ruleSetForProject(ProjectInfo projectInfo,boolean onlyCriticalRules) { if (projectInfo.buildFile.exists()) { - def extension = projectInfo.extension + def extension = projectInfo.extension //work on it def rules = (projectInfo.properties['gradleLint.rules'])?.toString()?.split(',')?.toList() ?: extension.rules + extension.criticalRules - Supplier projectSupplier = createProjectSupplier(rootProject, projectInfo.path) def includedRules = rules.unique() - .collect { registry.buildRules(it,projectSupplier, extension.criticalRules.contains(it)) } + .collect { registry.buildRules(it, projectInfo.projectSupplier, extension.criticalRules.contains(it)) } .flatten() as List if (onlyCriticalRules) { @@ -112,9 +104,6 @@ class LintService { } return new ListRuleSet([]) } - private RuleSet ruleSetForProject(Project project, boolean onlyCriticalRules) { - return ruleSetForProject({ project } as Supplier, onlyCriticalRules) - } RuleSet ruleSet(ProjectTree projectTree) { @@ -124,16 +113,19 @@ class LintService { return ruleSet } + Results lint(Project project, boolean onlyCriticalRules) { + return lint(ProjectTree.from(project), onlyCriticalRules) + } - Results lint(ProjectTree projectTree,Project rootProject ,boolean onlyCriticalRules) { + Results lint(ProjectTree projectTree, boolean onlyCriticalRules) { ProjectInfo rootProjectInfo = projectTree.allProjects.find { it.path == ":" } def analyzer = new ReportableAnalyzer(rootProjectInfo) projectTree.allProjects.each { p -> - Supplier projectSupplier = createProjectSupplier(rootProject, p.path) + def files = SourceCollector.getAllFiles(p.buildFile, p) def buildFiles = new BuildFiles(files) - def ruleSet = ruleSetForProject(p,rootProject, onlyCriticalRules) + def ruleSet = ruleSetForProject(p, onlyCriticalRules) if (!ruleSet.rules.isEmpty()) { boolean containsModelAwareRule = false // establish which file we are linting for each rule @@ -147,7 +139,7 @@ class LintService { } analyzer.analyze(p, buildFiles.text, ruleSet) if (containsModelAwareRule){ - Project project = projectSupplier.get() + Project project = p.projectSupplier.get() DependencyService.removeForProject(project) } } From fc1a2921a4fe655bf7ff6feb65aca9789ceb6566 Mon Sep 17 00:00:00 2001 From: Nouran Atef Date: Mon, 23 Jun 2025 18:12:51 +0200 Subject: [PATCH 11/31] Integrate projectInfo and ProjectTree instead of project --- .../groovy/com/netflix/nebula/lint/plugin/LintService.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 42bfc638..a91f8d8e 100644 --- a/src/main/groovy/com/netflix/nebula/lint/plugin/LintService.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/plugin/LintService.groovy @@ -109,7 +109,7 @@ class LintService { RuleSet ruleSet(ProjectTree projectTree) { def ruleSet = new CompositeRuleSet() projectTree.allProjects.each { p -> - ruleSet.addRuleSet(ruleSetForProject(p,projectTree, false))} + ruleSet.addRuleSet(ruleSetForProject(p, false))} return ruleSet } From b46ff28f068b4f17aa7b97d113f1420c4b5bce16 Mon Sep 17 00:00:00 2001 From: Nouran Atef Date: Tue, 24 Jun 2025 22:15:40 +0200 Subject: [PATCH 12/31] Overload for lintService.ruleSet --- .../groovy/com/netflix/nebula/lint/plugin/LintService.groovy | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 a91f8d8e..2c7f3723 100644 --- a/src/main/groovy/com/netflix/nebula/lint/plugin/LintService.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/plugin/LintService.groovy @@ -105,7 +105,9 @@ class LintService { return new ListRuleSet([]) } - + RuleSet ruleSet(Project project){ + return ruleSet(ProjectTree.from(project)) + } RuleSet ruleSet(ProjectTree projectTree) { def ruleSet = new CompositeRuleSet() projectTree.allProjects.each { p -> From 94ece27bff1e3014acf462df4cb7f2322ffbe9e5 Mon Sep 17 00:00:00 2001 From: Nouran Atef Date: Tue, 24 Jun 2025 23:03:39 +0200 Subject: [PATCH 13/31] fix AppliedFilesAstVisitor --- .../lint/plugin/AppliedFilesAstVisitor.groovy | 27 +++++++------------ .../nebula/lint/plugin/SourceCollector.groovy | 23 +++------------- 2 files changed, 14 insertions(+), 36 deletions(-) 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 62391a62..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,25 +12,18 @@ import org.gradle.api.Project */ class AppliedFilesAstVisitor extends ClassCodeVisitorSupport { - ProjectInfo project + ProjectInfo projectInfo + List appliedFiles = new ArrayList() Map projectVariablesMapping - AppliedFilesAstVisitor(Project project) { - projectVariablesMapping = [ - "\$projectDir" : project.projectDir.toString(), - "\$project.projectDir" : project.projectDir.toString(), - "\$rootDir" : project.rootDir.toString(), - "\$project.rootDir" : project.rootDir.toString(), - ] - } - AppliedFilesAstVisitor(ProjectInfo 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(), ] } @@ -40,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/SourceCollector.groovy b/src/main/groovy/com/netflix/nebula/lint/plugin/SourceCollector.groovy index 4db00bd1..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, ProjectInfo 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) } @@ -31,21 +31,6 @@ class SourceCollector { } } static List getAllFiles(File buildFile, Project project) { - if (buildFile.exists()) { - List result = new ArrayList<>() - result.add(buildFile) - SourceCode sourceCode = new SourceString(buildFile.text) - ModuleNode ast = sourceCode.getAst() - if (ast != null && ast.getClasses() != null) { - for (ClassNode classNode : ast.getClasses()) { - AppliedFilesAstVisitor visitor = new AppliedFilesAstVisitor(project) - visitor.visitClass(classNode) - result.addAll(visitor.appliedFiles) - } - } - return result - } else { - return Collections.emptyList() - } + return getAllFiles(buildFile, ProjectInfo.from(project)) } -} +} \ No newline at end of file From 580bb0c83b302b5551514a1a22d164af2d5ec620 Mon Sep 17 00:00:00 2001 From: Nouran Atef Date: Wed, 25 Jun 2025 00:37:59 +0200 Subject: [PATCH 14/31] Use `findByType()` instead of `getByType()` --- .../nebula/lint/plugin/LintGradleTask.groovy | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) 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 9500e90a..193bf123 100644 --- a/src/main/groovy/com/netflix/nebula/lint/plugin/LintGradleTask.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/plugin/LintGradleTask.groovy @@ -27,7 +27,6 @@ import org.gradle.api.tasks.Internal import org.gradle.api.tasks.Optional import org.gradle.api.tasks.TaskAction import org.gradle.internal.deprecation.DeprecationLogger -import org.gradle.api.UnknownDomainObjectException import java.util.function.Supplier @@ -153,6 +152,9 @@ abstract class LintGradleTask extends DefaultTask { } } } +/** + * A CC-compatible projection of project data. + */ class ProjectInfo implements Serializable{ String name String path @@ -163,13 +165,9 @@ class ProjectInfo implements Serializable{ Map properties Supplier projectSupplier static ProjectInfo from (Project project){ - GradleLintExtension extension - try { - extension = project.extensions.getByType(GradleLintExtension) - } catch (UnknownDomainObjectException ignored) { - extension = project.rootProject.extensions.getByType(GradleLintExtension) - } - + 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') @@ -177,7 +175,7 @@ class ProjectInfo implements Serializable{ if (project.hasProperty('gradleLint.excludedRules')) { properties['gradleLint.excludedRules'] = project.property('gradleLint.excludedRules') } - + return new ProjectInfo( name:project.name, path:project.path, From ed9bf8a539b391f7f0974177064382dd68050a02 Mon Sep 17 00:00:00 2001 From: Nouran Atef Date: Wed, 25 Jun 2025 19:21:38 +0200 Subject: [PATCH 15/31] Integrate `ProjectInfo` into GradleLintPatchAction` --- .../nebula/lint/GradleLintPatchAction.groovy | 6 +++--- .../nebula/lint/plugin/LintGradleTask.groovy | 16 +++++++++------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/main/groovy/com/netflix/nebula/lint/GradleLintPatchAction.groovy b/src/main/groovy/com/netflix/nebula/lint/GradleLintPatchAction.groovy index a97678c2..733cc0a9 100644 --- a/src/main/groovy/com/netflix/nebula/lint/GradleLintPatchAction.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/GradleLintPatchAction.groovy @@ -34,7 +34,7 @@ class GradleLintPatchAction extends GradleLintViolationAction { ProjectInfo projectInfo GradleLintPatchAction(Project project) { - this.project = project + this.projectInfo = ProjectInfo.from(project) } GradleLintPatchAction(ProjectInfo projectInfo) { @@ -45,7 +45,7 @@ class GradleLintPatchAction extends GradleLintViolationAction { @Override void lintFinished(Collection violations) { - File buildDir = projectInfo ? projectInfo.buildDir : project.layout.buildDirectory.asFile.getOrElse(new File(project.projectDir, "build")) + File buildDir = projectInfo.buildDirectory.getOrElse(new File(project.projectDir, "build")) buildDir.mkdirs() try { def patch = patch(violations*.fixes.flatten() as List) @@ -262,7 +262,7 @@ Exception: ${e.getMessage()} if (i > 0) combinedPatch += '\n' - def relativePath = projectInfo ? projectInfo.rootDir.toPath().relativize(file.toPath()).toString() : project.rootDir.toPath().relativize(file.toPath()).toString() + def relativePath = projectInfo.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/LintGradleTask.groovy b/src/main/groovy/com/netflix/nebula/lint/plugin/LintGradleTask.groovy index 193bf123..a5fdeb2e 100644 --- a/src/main/groovy/com/netflix/nebula/lint/plugin/LintGradleTask.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/plugin/LintGradleTask.groovy @@ -20,6 +20,7 @@ import org.gradle.api.DefaultTask import org.gradle.api.GradleException import org.gradle.api.Project import org.gradle.api.Task +import org.gradle.api.file.DirectoryProperty import org.gradle.api.provider.ListProperty import org.gradle.api.provider.Property import org.gradle.api.tasks.Input @@ -51,16 +52,15 @@ abstract class LintGradleTask extends DefaultTask { @Input abstract Property getProjectTree() - @Internal - Project getRootProject() { - return project.rootProject - } + @Input + abstract Property getProjectInfo() LintGradleTask() { failOnWarning.convention(false) onlyCriticalRules.convention(false) - projectTree.set(project.provider {ProjectTree.from(project)}) + projectTree.set(project.provider {ProjectTree.from(project) }) + projectInfo.set(project.provider { ProjectInfo.from(project) }) projectRootDir.set(project.rootDir) group = 'lint' try { @@ -76,7 +76,7 @@ abstract class LintGradleTask extends DefaultTask { 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() + new GradleLintPatchAction(getProjectInfo().get()) + new GradleLintInfoBrokerAction(project) + consoleOutputAction).each { it.lintFinished(violations) } } @@ -161,6 +161,7 @@ class ProjectInfo implements Serializable{ File rootDir File buildFile File projectDir + File buildDirectory GradleLintExtension extension Map properties Supplier projectSupplier @@ -184,7 +185,8 @@ class ProjectInfo implements Serializable{ projectDir:project.projectDir, extension: extension, properties: properties, - projectSupplier: { project } + projectSupplier: { project }, + buildDirectory : project.layout.buildDirectory.get().asFile ) } From 8902d7883cd259cc7c7b5f2902808ffc3ed966c9 Mon Sep 17 00:00:00 2001 From: Nouran Atef Date: Wed, 25 Jun 2025 22:07:50 +0200 Subject: [PATCH 16/31] Fix `GradleLintPatchAction` --- .../com/netflix/nebula/lint/GradleLintPatchAction.groovy | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/groovy/com/netflix/nebula/lint/GradleLintPatchAction.groovy b/src/main/groovy/com/netflix/nebula/lint/GradleLintPatchAction.groovy index 733cc0a9..c173e4ba 100644 --- a/src/main/groovy/com/netflix/nebula/lint/GradleLintPatchAction.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/GradleLintPatchAction.groovy @@ -34,7 +34,8 @@ class GradleLintPatchAction extends GradleLintViolationAction { ProjectInfo projectInfo GradleLintPatchAction(Project project) { - this.projectInfo = ProjectInfo.from(project) + this.project = project + } GradleLintPatchAction(ProjectInfo projectInfo) { @@ -45,7 +46,7 @@ class GradleLintPatchAction extends GradleLintViolationAction { @Override void lintFinished(Collection violations) { - File buildDir = projectInfo.buildDirectory.getOrElse(new File(project.projectDir, "build")) + File buildDir = projectInfo ? projectInfo.buildDirectory.getOrElse(new File(project.projectDir, "build")) : project.layout.buildDirectory.asFile.getOrElse(new File(project.projectDir, "build")) buildDir.mkdirs() try { def patch = patch(violations*.fixes.flatten() as List) @@ -262,7 +263,7 @@ Exception: ${e.getMessage()} if (i > 0) combinedPatch += '\n' - def relativePath = projectInfo.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} From db9e58e9f1b097b94db375abfe2d65430e7bd08a Mon Sep 17 00:00:00 2001 From: Nouran Atef Date: Wed, 25 Jun 2025 22:37:00 +0200 Subject: [PATCH 17/31] Fix `GradleLintPatchActionSpec` --- .../nebula/lint/plugin/LintGradleTask.groovy | 4 ++-- .../netflix/nebula/lint/plugin/LintService.groovy | 2 +- .../lint/rule/GradleLintPatchActionSpec.groovy | 13 ++++++++++++- 3 files changed, 15 insertions(+), 4 deletions(-) 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 a5fdeb2e..5f544cdd 100644 --- a/src/main/groovy/com/netflix/nebula/lint/plugin/LintGradleTask.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/plugin/LintGradleTask.groovy @@ -165,7 +165,7 @@ class ProjectInfo implements Serializable{ GradleLintExtension extension Map properties Supplier projectSupplier - static ProjectInfo from (Project project){ + static ProjectInfo from(Project project){ GradleLintExtension extension = project.extensions.findByType(GradleLintExtension) ?: project.rootProject.extensions.findByType(GradleLintExtension) @@ -186,7 +186,7 @@ class ProjectInfo implements Serializable{ extension: extension, properties: properties, projectSupplier: { project }, - buildDirectory : project.layout.buildDirectory.get().asFile + buildDirectory : project.buildDir ) } 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 2c7f3723..3d17b225 100644 --- a/src/main/groovy/com/netflix/nebula/lint/plugin/LintService.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/plugin/LintService.groovy @@ -83,7 +83,7 @@ class LintService { private RuleSet ruleSetForProject(ProjectInfo projectInfo,boolean onlyCriticalRules) { if (projectInfo.buildFile.exists()) { - def extension = projectInfo.extension //work on it + def extension = projectInfo.extension def rules = (projectInfo.properties['gradleLint.rules'])?.toString()?.split(',')?.toList() ?: extension.rules + extension.criticalRules 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 From 40372e7ccdbbf8f34af0f52fb51596e427edc5b9 Mon Sep 17 00:00:00 2001 From: Nouran Atef Date: Wed, 25 Jun 2025 22:44:48 +0200 Subject: [PATCH 18/31] Fix `GradleLintPatchAction` --- .../groovy/com/netflix/nebula/lint/GradleLintPatchAction.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/groovy/com/netflix/nebula/lint/GradleLintPatchAction.groovy b/src/main/groovy/com/netflix/nebula/lint/GradleLintPatchAction.groovy index c173e4ba..ba95df97 100644 --- a/src/main/groovy/com/netflix/nebula/lint/GradleLintPatchAction.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/GradleLintPatchAction.groovy @@ -46,7 +46,7 @@ class GradleLintPatchAction extends GradleLintViolationAction { @Override void lintFinished(Collection violations) { - File buildDir = projectInfo ? projectInfo.buildDirectory.getOrElse(new File(project.projectDir, "build")) : 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) From 3b64570c2f92a152091bfb85b45c1d49db8fb6d5 Mon Sep 17 00:00:00 2001 From: Nouran Atef Date: Thu, 26 Jun 2025 16:48:10 +0200 Subject: [PATCH 19/31] Integrate `ProjectInfo` into `GradleLintInfoBrokerAction` --- .../lint/GradleLintInfoBrokerAction.groovy | 24 ++++++++++++++++--- .../nebula/lint/plugin/LintGradleTask.groovy | 6 ++++- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/main/groovy/com/netflix/nebula/lint/GradleLintInfoBrokerAction.groovy b/src/main/groovy/com/netflix/nebula/lint/GradleLintInfoBrokerAction.groovy index 412c5db9..76224b6d 100644 --- a/src/main/groovy/com/netflix/nebula/lint/GradleLintInfoBrokerAction.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/GradleLintInfoBrokerAction.groovy @@ -1,15 +1,33 @@ package com.netflix.nebula.lint +import com.netflix.nebula.lint.plugin.ProjectInfo import groovy.transform.Canonical +import org.gradle.api.Plugin import org.gradle.api.Project @Canonical class GradleLintInfoBrokerAction extends GradleLintViolationAction { + Plugin nebulaInfoBroker + ProjectInfo projectInfo Project project + GradleLintInfoBrokerAction(Project project){ + this.project = project + project.getPlugins().withId('nebula.info-broker') { plugin -> + nebulaInfoBroker = plugin + } + } + + GradleLintInfoBrokerAction(ProjectInfo projectInfo){ + this.projectInfo = projectInfo + 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 +35,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 +43,7 @@ class GradleLintInfoBrokerAction extends GradleLintViolationAction { } LintReportItem buildReportItem(GradleViolation v) { - def buildFilePath = project.rootDir.toURI().relativize(v.file.toURI()).toString() + def buildFilePath = projectInfo? projectInfo.rootDir.toURI().relativize(v.file.toURI()).toString() : project.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/plugin/LintGradleTask.groovy b/src/main/groovy/com/netflix/nebula/lint/plugin/LintGradleTask.groovy index 5f544cdd..82bceed4 100644 --- a/src/main/groovy/com/netflix/nebula/lint/plugin/LintGradleTask.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/plugin/LintGradleTask.groovy @@ -55,6 +55,9 @@ abstract class LintGradleTask extends DefaultTask { @Input abstract Property getProjectInfo() + @Internal + GradleLintInfoBrokerAction infoBrokerAction + LintGradleTask() { failOnWarning.convention(false) @@ -62,6 +65,7 @@ abstract class LintGradleTask extends DefaultTask { projectTree.set(project.provider {ProjectTree.from(project) }) projectInfo.set(project.provider { ProjectInfo.from(project) }) projectRootDir.set(project.rootDir) + infoBrokerAction = new GradleLintInfoBrokerAction(projectInfo.get()) group = 'lint' try { def method = Task.getMethod("notCompatibleWithConfigurationCache") @@ -76,7 +80,7 @@ abstract class LintGradleTask extends DefaultTask { def violations = new LintService().lint(projectTree.get(), onlyCriticalRules.get()).violations .unique { v1, v2 -> v1.is(v2) ? 0 : 1 } - (getListeners() + new GradleLintPatchAction(getProjectInfo().get()) + new GradleLintInfoBrokerAction(project) + consoleOutputAction).each { + (getListeners() + new GradleLintPatchAction(getProjectInfo().get()) + infoBrokerAction + consoleOutputAction).each { it.lintFinished(violations) } } From 6e2776f525ba577d89d8940bd4155ca8b5b0cb50 Mon Sep 17 00:00:00 2001 From: Nouran Atef Date: Thu, 26 Jun 2025 17:44:00 +0200 Subject: [PATCH 20/31] fix `GradleLintInfoBrokerAction` --- .../nebula/lint/GradleLintInfoBrokerAction.groovy | 13 +++---------- .../nebula/lint/plugin/LintGradleTask.groovy | 2 +- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/main/groovy/com/netflix/nebula/lint/GradleLintInfoBrokerAction.groovy b/src/main/groovy/com/netflix/nebula/lint/GradleLintInfoBrokerAction.groovy index 76224b6d..af315692 100644 --- a/src/main/groovy/com/netflix/nebula/lint/GradleLintInfoBrokerAction.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/GradleLintInfoBrokerAction.groovy @@ -9,17 +9,10 @@ import org.gradle.api.Project class GradleLintInfoBrokerAction extends GradleLintViolationAction { Plugin nebulaInfoBroker ProjectInfo projectInfo - Project project - GradleLintInfoBrokerAction(Project project){ - this.project = project - project.getPlugins().withId('nebula.info-broker') { plugin -> - nebulaInfoBroker = plugin - } - } - GradleLintInfoBrokerAction(ProjectInfo projectInfo){ - this.projectInfo = projectInfo + GradleLintInfoBrokerAction(Project project){ + this.projectInfo = ProjectInfo.from(project) project.getPlugins().withId('nebula.info-broker') { plugin -> nebulaInfoBroker = plugin } @@ -43,7 +36,7 @@ class GradleLintInfoBrokerAction extends GradleLintViolationAction { } LintReportItem buildReportItem(GradleViolation v) { - def buildFilePath = projectInfo? projectInfo.rootDir.toURI().relativize(v.file.toURI()).toString() : 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/plugin/LintGradleTask.groovy b/src/main/groovy/com/netflix/nebula/lint/plugin/LintGradleTask.groovy index 82bceed4..f84d6b33 100644 --- a/src/main/groovy/com/netflix/nebula/lint/plugin/LintGradleTask.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/plugin/LintGradleTask.groovy @@ -65,7 +65,7 @@ abstract class LintGradleTask extends DefaultTask { projectTree.set(project.provider {ProjectTree.from(project) }) projectInfo.set(project.provider { ProjectInfo.from(project) }) projectRootDir.set(project.rootDir) - infoBrokerAction = new GradleLintInfoBrokerAction(projectInfo.get()) + infoBrokerAction = new GradleLintInfoBrokerAction(project) group = 'lint' try { def method = Task.getMethod("notCompatibleWithConfigurationCache") From 66fcc31e74171209f4e551ffa6d8f4d49b3ec77e Mon Sep 17 00:00:00 2001 From: Nouran Atef Date: Thu, 26 Jun 2025 22:19:53 +0200 Subject: [PATCH 21/31] Move action creation to configuration time in FixGradleLintTask - Create GradleLintPatchAction at configuration time using ProjectInfo - Update console output action to use ProjectInfo instead of project - Avoid accessing project in @TaskAction method --- .../lint/plugin/FixGradleLintTask.groovy | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) 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..8fca4edc 100644 --- a/src/main/groovy/com/netflix/nebula/lint/plugin/FixGradleLintTask.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/plugin/FixGradleLintTask.groovy @@ -25,6 +25,7 @@ import org.eclipse.jgit.api.ApplyCommand import org.gradle.api.DefaultTask import org.gradle.api.GradleException 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,9 +47,22 @@ abstract class FixGradleLintTask extends DefaultTask implements VerificationTask @Internal GradleLintInfoBrokerAction infoBrokerAction + @Input + abstract Property getProjectInfo() + + @Internal + GradleLintPatchAction patchAction + + @Input + abstract Property getProjectTree() + + FixGradleLintTask() { infoBrokerAction = new GradleLintInfoBrokerAction(project) + patchAction = new GradleLintPatchAction(projectInfo.get()) + projectInfo.set(project.provider { ProjectInfo.from(project) }) + projectTree.set(project.provider {ProjectTree.from(project) }) userDefinedListeners.convention([]) outputs.upToDateWhen { false } group = 'lint' @@ -58,16 +72,16 @@ abstract class FixGradleLintTask extends DefaultTask implements VerificationTask 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(projectInfo.get().buildDirectory, GradleLintPatchAction.PATCH_NAME) if (patchFile.exists()) { - new ApplyCommand(new NotNecessarilyGitRepository(project.projectDir)).setPatch(patchFile.newInputStream()).call() + new ApplyCommand(new NotNecessarilyGitRepository(projectInfo.get().projectDir)).setPatch(patchFile.newInputStream()).call() } (userDefinedListeners.get() + infoBrokerAction + consoleOutputAction()).each { From f9af7c43fa2e5c4f8ec7ad1d57e027db999ac3d8 Mon Sep 17 00:00:00 2001 From: Nouran Atef Date: Thu, 26 Jun 2025 22:24:37 +0200 Subject: [PATCH 22/31] Integrate `ProjectInfo` and `ProjectTree` into `GradleLintReportTask` --- .../nebula/lint/plugin/GradleLintReportTask.groovy | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) 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..edd60d86 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,15 @@ abstract class GradleLintReportTask extends DefaultTask implements VerificationT @Internal final NamedDomainObjectContainer reports + @Input + abstract Property getProjectInfo() + + @Input + abstract Property getProjectTree() + @Inject GradleLintReportTask(ObjectFactory objects) { - projectName = objects.property(String).convention(project.name) + projectName = objects.property(String).convention(projectInfo.get().name) reportsDir = objects.directoryProperty() reports = objects.domainObjectContainer( @@ -90,7 +96,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 +107,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 +148,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 } } From c6046f8aad5ddbb0f6e623c1af3d5fc6d350c3a6 Mon Sep 17 00:00:00 2001 From: Nouran Atef Date: Thu, 26 Jun 2025 23:47:00 +0200 Subject: [PATCH 23/31] Create GradleLintPatchAction at configuration time using ProjectInfo in LintGradleTask --- .../com/netflix/nebula/lint/plugin/LintGradleTask.groovy | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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 f84d6b33..f36c2210 100644 --- a/src/main/groovy/com/netflix/nebula/lint/plugin/LintGradleTask.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/plugin/LintGradleTask.groovy @@ -58,6 +58,9 @@ abstract class LintGradleTask extends DefaultTask { @Internal GradleLintInfoBrokerAction infoBrokerAction + @Internal + GradleLintPatchAction patchAction + LintGradleTask() { failOnWarning.convention(false) @@ -66,6 +69,7 @@ abstract class LintGradleTask extends DefaultTask { projectInfo.set(project.provider { ProjectInfo.from(project) }) projectRootDir.set(project.rootDir) infoBrokerAction = new GradleLintInfoBrokerAction(project) + patchAction = new GradleLintPatchAction(getProjectInfo().get()) group = 'lint' try { def method = Task.getMethod("notCompatibleWithConfigurationCache") @@ -80,7 +84,7 @@ abstract class LintGradleTask extends DefaultTask { def violations = new LintService().lint(projectTree.get(), onlyCriticalRules.get()).violations .unique { v1, v2 -> v1.is(v2) ? 0 : 1 } - (getListeners() + new GradleLintPatchAction(getProjectInfo().get()) + infoBrokerAction + consoleOutputAction).each { + (getListeners() + patchAction + infoBrokerAction + consoleOutputAction).each { it.lintFinished(violations) } } From 0568d44bc5fcd17783fe54af31c69aeea1fe697f Mon Sep 17 00:00:00 2001 From: Nouran Atef Date: Thu, 26 Jun 2025 23:47:34 +0200 Subject: [PATCH 24/31] Make `ProjectInfo` and `ProjectTree` internal in GradleLintReportTask and FixGradleLintTask. --- .../netflix/nebula/lint/plugin/FixGradleLintTask.groovy | 8 ++++---- .../nebula/lint/plugin/GradleLintReportTask.groovy | 6 ++++-- 2 files changed, 8 insertions(+), 6 deletions(-) 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 8fca4edc..d0076191 100644 --- a/src/main/groovy/com/netflix/nebula/lint/plugin/FixGradleLintTask.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/plugin/FixGradleLintTask.groovy @@ -47,22 +47,22 @@ abstract class FixGradleLintTask extends DefaultTask implements VerificationTask @Internal GradleLintInfoBrokerAction infoBrokerAction - @Input + @Internal abstract Property getProjectInfo() @Internal GradleLintPatchAction patchAction - @Input + @Internal abstract Property getProjectTree() FixGradleLintTask() { - infoBrokerAction = new GradleLintInfoBrokerAction(project) - patchAction = new GradleLintPatchAction(projectInfo.get()) projectInfo.set(project.provider { ProjectInfo.from(project) }) projectTree.set(project.provider {ProjectTree.from(project) }) + infoBrokerAction = new GradleLintInfoBrokerAction(project) + patchAction = new GradleLintPatchAction(projectInfo.get()) userDefinedListeners.convention([]) outputs.upToDateWhen { false } group = 'lint' 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 edd60d86..3b755c0f 100644 --- a/src/main/groovy/com/netflix/nebula/lint/plugin/GradleLintReportTask.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/plugin/GradleLintReportTask.groovy @@ -57,14 +57,16 @@ abstract class GradleLintReportTask extends DefaultTask implements VerificationT @Internal final NamedDomainObjectContainer reports - @Input + @Internal abstract Property getProjectInfo() - @Input + @Internal abstract Property getProjectTree() @Inject GradleLintReportTask(ObjectFactory objects) { + projectInfo.set(project.provider { ProjectInfo.from(project) }) + projectTree.set(project.provider { ProjectTree.from(project) }) projectName = objects.property(String).convention(projectInfo.get().name) reportsDir = objects.directoryProperty() reports = From 5777258fd17b42381c4dffeea3ab83afd25a15dc Mon Sep 17 00:00:00 2001 From: Nouran Atef Date: Mon, 30 Jun 2025 05:16:40 +0200 Subject: [PATCH 25/31] Add try and catch of config cache compatibility --- .../netflix/nebula/lint/plugin/FixGradleLintTask.groovy | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) 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 d0076191..fa7d8e06 100644 --- a/src/main/groovy/com/netflix/nebula/lint/plugin/FixGradleLintTask.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/plugin/FixGradleLintTask.groovy @@ -24,6 +24,7 @@ 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 @@ -66,11 +67,15 @@ abstract class FixGradleLintTask extends DefaultTask implements VerificationTask 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(projectTree.get(), false).violations .unique { v1, v2 -> v1.is(v2) ? 0 : 1 } From 37354cdcef5c9c779aace3f0f77aba0ada06449d Mon Sep 17 00:00:00 2001 From: Rafael Chaves Date: Tue, 1 Jul 2025 10:35:49 -0300 Subject: [PATCH 26/31] Trade serialization problems for potential execution problems --- .../lint/GradleLintInfoBrokerAction.groovy | 8 ++-- .../lint/plugin/FixGradleLintTask.groovy | 21 +++++----- .../lint/plugin/GradleLintReportTask.groovy | 4 +- .../nebula/lint/plugin/LintGradleTask.groovy | 42 ++++++++++++++----- .../lint/plugin/LintRuleRegistry.groovy | 2 +- .../nebula/lint/plugin/LintService.groovy | 7 ---- .../nebula/lint/rule/GradleLintRule.groovy | 10 ++++- .../lint/rule/test/AbstractRuleSpec.groovy | 2 +- .../FixGradleLintTaskCriticalRulesSpec.groovy | 2 - .../lint/plugin/LintRuleRegistrySpec.groovy | 1 - .../lint/plugin/SourceCollectorTest.groovy | 7 +++- .../UnusedDependencyExcludeRuleSpec.groovy | 2 +- ...nusedExcludeByConfigurationRuleSpec.groovy | 2 +- 13 files changed, 65 insertions(+), 45 deletions(-) diff --git a/src/main/groovy/com/netflix/nebula/lint/GradleLintInfoBrokerAction.groovy b/src/main/groovy/com/netflix/nebula/lint/GradleLintInfoBrokerAction.groovy index af315692..682c636c 100644 --- a/src/main/groovy/com/netflix/nebula/lint/GradleLintInfoBrokerAction.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/GradleLintInfoBrokerAction.groovy @@ -3,7 +3,7 @@ package com.netflix.nebula.lint import com.netflix.nebula.lint.plugin.ProjectInfo import groovy.transform.Canonical import org.gradle.api.Plugin -import org.gradle.api.Project +import org.gradle.api.Task @Canonical class GradleLintInfoBrokerAction extends GradleLintViolationAction { @@ -11,9 +11,9 @@ class GradleLintInfoBrokerAction extends GradleLintViolationAction { ProjectInfo projectInfo - GradleLintInfoBrokerAction(Project project){ - this.projectInfo = ProjectInfo.from(project) - project.getPlugins().withId('nebula.info-broker') { plugin -> + GradleLintInfoBrokerAction(Task task){ + this.projectInfo = ProjectInfo.from(task) + task.project.project.getPlugins().withId('nebula.info-broker') { plugin -> nebulaInfoBroker = plugin } } 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 fa7d8e06..805c7cf6 100644 --- a/src/main/groovy/com/netflix/nebula/lint/plugin/FixGradleLintTask.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/plugin/FixGradleLintTask.groovy @@ -48,22 +48,21 @@ abstract class FixGradleLintTask extends DefaultTask implements VerificationTask @Internal GradleLintInfoBrokerAction infoBrokerAction - @Internal - abstract Property getProjectInfo() - @Internal GradleLintPatchAction patchAction @Internal abstract Property getProjectTree() - + @Internal + ProjectInfo getProjectInfo() { + return projectTree.get().baseProject + } FixGradleLintTask() { - projectInfo.set(project.provider { ProjectInfo.from(project) }) - projectTree.set(project.provider {ProjectTree.from(project) }) - infoBrokerAction = new GradleLintInfoBrokerAction(project) - patchAction = new GradleLintPatchAction(projectInfo.get()) + projectTree.set(project.provider {ProjectTree.from(this) }) + infoBrokerAction = new GradleLintInfoBrokerAction(this) + patchAction = new GradleLintPatchAction(getProjectInfo()) userDefinedListeners.convention([]) outputs.upToDateWhen { false } group = 'lint' @@ -84,9 +83,9 @@ abstract class FixGradleLintTask extends DefaultTask implements VerificationTask it.lintFinished(violations) } - def patchFile = new File(projectInfo.get().buildDirectory, GradleLintPatchAction.PATCH_NAME) + def patchFile = new File(getProjectInfo().buildDirectory, GradleLintPatchAction.PATCH_NAME) if (patchFile.exists()) { - new ApplyCommand(new NotNecessarilyGitRepository(projectInfo.get().projectDir)).setPatch(patchFile.newInputStream()).call() + new ApplyCommand(new NotNecessarilyGitRepository(projectInfo.projectDir)).setPatch(patchFile.newInputStream()).call() } (userDefinedListeners.get() + infoBrokerAction + consoleOutputAction()).each { @@ -116,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 3b755c0f..c76f2fd3 100644 --- a/src/main/groovy/com/netflix/nebula/lint/plugin/GradleLintReportTask.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/plugin/GradleLintReportTask.groovy @@ -65,8 +65,8 @@ abstract class GradleLintReportTask extends DefaultTask implements VerificationT @Inject GradleLintReportTask(ObjectFactory objects) { - projectInfo.set(project.provider { ProjectInfo.from(project) }) - projectTree.set(project.provider { ProjectTree.from(project) }) + 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 = 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 f36c2210..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,13 +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.file.DirectoryProperty -import org.gradle.api.provider.ListProperty import org.gradle.api.provider.Property import org.gradle.api.tasks.Input import org.gradle.api.tasks.Internal @@ -65,10 +64,10 @@ abstract class LintGradleTask extends DefaultTask { LintGradleTask() { failOnWarning.convention(false) onlyCriticalRules.convention(false) - projectTree.set(project.provider {ProjectTree.from(project) }) - projectInfo.set(project.provider { ProjectInfo.from(project) }) + projectTree.set(project.provider {ProjectTree.from(this) }) + projectInfo.convention(projectTree.map(ProjectTree::getBaseProject)) projectRootDir.set(project.rootDir) - infoBrokerAction = new GradleLintInfoBrokerAction(project) + infoBrokerAction = new GradleLintInfoBrokerAction(this) patchAction = new GradleLintPatchAction(getProjectInfo().get()) group = 'lint' try { @@ -173,7 +172,18 @@ class ProjectInfo implements Serializable{ GradleLintExtension extension Map properties Supplier projectSupplier - static ProjectInfo from(Project project){ + + 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) @@ -193,7 +203,7 @@ class ProjectInfo implements Serializable{ projectDir:project.projectDir, extension: extension, properties: properties, - projectSupplier: { project }, + projectSupplier: projectSupplier, buildDirectory : project.buildDir ) @@ -201,17 +211,29 @@ class ProjectInfo implements Serializable{ } + class ProjectTree{ List allProjects - ProjectTree(List allProjects){ this.allProjects = allProjects + } + /** + * Returns the base project this tree was built from. + */ + ProjectInfo getBaseProject() { + return allProjects.head() } - static from(Project project) { - List projectInfos = ([project] + project.getSubprojects().asList()).collect{Project p -> ProjectInfo.from(p)} + /** + * 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 76d5528e..39cbe1d9 100644 --- a/src/main/groovy/com/netflix/nebula/lint/plugin/LintRuleRegistry.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/plugin/LintRuleRegistry.groovy @@ -65,7 +65,7 @@ class LintRuleRegistry { try { Rule r = (Rule) classLoader.loadClass(implClassName).newInstance() if(r instanceof ModelAwareGradleLintRule) { - (r as ModelAwareGradleLintRule).project = projectSupplier.get() + (r as ModelAwareGradleLintRule).projectSupplier = projectSupplier } if(r instanceof GradleLintRule) { 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 3d17b225..2cce0ccc 100644 --- a/src/main/groovy/com/netflix/nebula/lint/plugin/LintService.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/plugin/LintService.groovy @@ -105,9 +105,6 @@ class LintService { return new ListRuleSet([]) } - RuleSet ruleSet(Project project){ - return ruleSet(ProjectTree.from(project)) - } RuleSet ruleSet(ProjectTree projectTree) { def ruleSet = new CompositeRuleSet() projectTree.allProjects.each { p -> @@ -115,10 +112,6 @@ class LintService { return ruleSet } - Results lint(Project project, boolean onlyCriticalRules) { - return lint(ProjectTree.from(project), onlyCriticalRules) - } - Results lint(ProjectTree projectTree, boolean onlyCriticalRules) { ProjectInfo rootProjectInfo = projectTree.allProjects.find { it.path == ":" } def analyzer = new ReportableAnalyzer(rootProjectInfo) 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/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/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)'() { From 1c0c02357160bdbdcc3e73b5383150ce4147e9fc Mon Sep 17 00:00:00 2001 From: Nouran Atef Date: Tue, 1 Jul 2025 23:37:04 +0200 Subject: [PATCH 27/31] Add CompitabilityTest --- .../lint/GradleLintInfoBrokerAction.groovy | 2 +- .../nebula/lint/CompitabilityTest.groovy | 27 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 src/test/groovy/com/netflix/nebula/lint/CompitabilityTest.groovy diff --git a/src/main/groovy/com/netflix/nebula/lint/GradleLintInfoBrokerAction.groovy b/src/main/groovy/com/netflix/nebula/lint/GradleLintInfoBrokerAction.groovy index 682c636c..6ce47ab4 100644 --- a/src/main/groovy/com/netflix/nebula/lint/GradleLintInfoBrokerAction.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/GradleLintInfoBrokerAction.groovy @@ -13,7 +13,7 @@ class GradleLintInfoBrokerAction extends GradleLintViolationAction { GradleLintInfoBrokerAction(Task task){ this.projectInfo = ProjectInfo.from(task) - task.project.project.getPlugins().withId('nebula.info-broker') { plugin -> + task.project.getPlugins().withId('nebula.info-broker') { plugin -> nebulaInfoBroker = plugin } } diff --git a/src/test/groovy/com/netflix/nebula/lint/CompitabilityTest.groovy b/src/test/groovy/com/netflix/nebula/lint/CompitabilityTest.groovy new file mode 100644 index 00000000..7f3c92c6 --- /dev/null +++ b/src/test/groovy/com/netflix/nebula/lint/CompitabilityTest.groovy @@ -0,0 +1,27 @@ + +package com.netflix.nebula.lint + + + +class ConfigurationCacheCompatibilityTest extends BaseIntegrationTestKitSpec { + def test() { + buildFile << """ + plugins { + id 'java' + id 'nebula.lint' + } + gradleLint.rules = ['dependency-parentheses'] + dependencies { + implementation('junit:junit:4.11') + } + """ + keepFiles = true + forwardOutput = true + + when: + def result = runTasks('autoLintGradle', '--warning-mode', 'none') + + then: + result.output.contains("1 problems (0 errors, 1 warning)") + } +} \ No newline at end of file From e1f2691cbbe43eb2dad575f21ff9c4cea6c17908 Mon Sep 17 00:00:00 2001 From: Nouran Atef Date: Wed, 2 Jul 2025 19:53:24 +0200 Subject: [PATCH 28/31] delete test --- .../nebula/lint/CompitabilityTest.groovy | 27 ------------------- 1 file changed, 27 deletions(-) delete mode 100644 src/test/groovy/com/netflix/nebula/lint/CompitabilityTest.groovy diff --git a/src/test/groovy/com/netflix/nebula/lint/CompitabilityTest.groovy b/src/test/groovy/com/netflix/nebula/lint/CompitabilityTest.groovy deleted file mode 100644 index 7f3c92c6..00000000 --- a/src/test/groovy/com/netflix/nebula/lint/CompitabilityTest.groovy +++ /dev/null @@ -1,27 +0,0 @@ - -package com.netflix.nebula.lint - - - -class ConfigurationCacheCompatibilityTest extends BaseIntegrationTestKitSpec { - def test() { - buildFile << """ - plugins { - id 'java' - id 'nebula.lint' - } - gradleLint.rules = ['dependency-parentheses'] - dependencies { - implementation('junit:junit:4.11') - } - """ - keepFiles = true - forwardOutput = true - - when: - def result = runTasks('autoLintGradle', '--warning-mode', 'none') - - then: - result.output.contains("1 problems (0 errors, 1 warning)") - } -} \ No newline at end of file From 00819736bf28186b58341e3269b1adef874cdc23 Mon Sep 17 00:00:00 2001 From: Nouran Atef Date: Wed, 2 Jul 2025 20:44:20 +0200 Subject: [PATCH 29/31] SpaceAssignmentRule compatibility --- .../lint/rule/dsl/SpaceAssignmentRule.groovy | 32 ++++++------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/src/main/groovy/com/netflix/nebula/lint/rule/dsl/SpaceAssignmentRule.groovy b/src/main/groovy/com/netflix/nebula/lint/rule/dsl/SpaceAssignmentRule.groovy index bd469fef..4324ecfd 100644 --- a/src/main/groovy/com/netflix/nebula/lint/rule/dsl/SpaceAssignmentRule.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/rule/dsl/SpaceAssignmentRule.groovy @@ -10,6 +10,12 @@ class SpaceAssignmentRule extends ModelAwareGradleLintRule { String description = "space-assignment syntax is deprecated" + static final Set knownDeprecatedAssignments = [ + 'group', + 'version', + 'description' + ] + @Override void visitMethodCallExpression(MethodCallExpression call) { if(dslStack().contains("plugins")) { @@ -22,30 +28,10 @@ class SpaceAssignmentRule extends ModelAwareGradleLintRule { return } - def receiverClass = receiver(call)?.clazz - if (receiverClass == null) { - return // no enough data to analyze - } - - def invokedMethodName = call.method.value + def invokedMethodName = call.methodAsString + if (!knownDeprecatedAssignments.contains(invokedMethodName)) return - // check if the method has a matching property - def setter = receiverClass.getMethods().find { it.name == "set${invokedMethodName.capitalize()}" } - if (setter == null) { - return // no matching property - } - - // check if it's a generated method for space assignment - def exactMethod = receiverClass.getMethods().find { it.name == invokedMethodName } - if (exactMethod != null) { - def deprecatedAnnotation = exactMethod.getAnnotation(Deprecated) - if (deprecatedAnnotation != null) { - // may be false positive when the explicit method is deprecated - addViolation(call) - } - } else { - addViolation(call) - } + addViolation(call) } private boolean isGradleGroup(MethodCallExpression call) { From 431297266ec96e9e79360307cb0249147bf3dcc6 Mon Sep 17 00:00:00 2001 From: Nouran Atef Date: Thu, 3 Jul 2025 14:06:35 +0200 Subject: [PATCH 30/31] SpaceAssignmentRule compatibility --- .../nebula/lint/rule/dsl/SpaceAssignmentRule.groovy | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/groovy/com/netflix/nebula/lint/rule/dsl/SpaceAssignmentRule.groovy b/src/main/groovy/com/netflix/nebula/lint/rule/dsl/SpaceAssignmentRule.groovy index 4324ecfd..7d659e55 100644 --- a/src/main/groovy/com/netflix/nebula/lint/rule/dsl/SpaceAssignmentRule.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/rule/dsl/SpaceAssignmentRule.groovy @@ -13,7 +13,15 @@ class SpaceAssignmentRule extends ModelAwareGradleLintRule { static final Set knownDeprecatedAssignments = [ 'group', 'version', - 'description' + 'status', + 'buildDir', + 'sourceCompatibility', + 'targetCompatibility', + 'url', + 'name', + 'description', + 'tempDir', + 'distributionPath', ] @Override From 653c6da7023c62a0f359d89a3d11bf1f84863ef7 Mon Sep 17 00:00:00 2001 From: Nouran Atef Date: Thu, 17 Jul 2025 06:49:30 +0200 Subject: [PATCH 31/31] revert changes for SpaceAssignmentRule --- .../lint/rule/dsl/SpaceAssignmentRule.groovy | 40 +++++++++++-------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/src/main/groovy/com/netflix/nebula/lint/rule/dsl/SpaceAssignmentRule.groovy b/src/main/groovy/com/netflix/nebula/lint/rule/dsl/SpaceAssignmentRule.groovy index 7d659e55..bd469fef 100644 --- a/src/main/groovy/com/netflix/nebula/lint/rule/dsl/SpaceAssignmentRule.groovy +++ b/src/main/groovy/com/netflix/nebula/lint/rule/dsl/SpaceAssignmentRule.groovy @@ -10,20 +10,6 @@ class SpaceAssignmentRule extends ModelAwareGradleLintRule { String description = "space-assignment syntax is deprecated" - static final Set knownDeprecatedAssignments = [ - 'group', - 'version', - 'status', - 'buildDir', - 'sourceCompatibility', - 'targetCompatibility', - 'url', - 'name', - 'description', - 'tempDir', - 'distributionPath', - ] - @Override void visitMethodCallExpression(MethodCallExpression call) { if(dslStack().contains("plugins")) { @@ -36,10 +22,30 @@ class SpaceAssignmentRule extends ModelAwareGradleLintRule { return } - def invokedMethodName = call.methodAsString - if (!knownDeprecatedAssignments.contains(invokedMethodName)) return + def receiverClass = receiver(call)?.clazz + if (receiverClass == null) { + return // no enough data to analyze + } + + def invokedMethodName = call.method.value - addViolation(call) + // check if the method has a matching property + def setter = receiverClass.getMethods().find { it.name == "set${invokedMethodName.capitalize()}" } + if (setter == null) { + return // no matching property + } + + // check if it's a generated method for space assignment + def exactMethod = receiverClass.getMethods().find { it.name == invokedMethodName } + if (exactMethod != null) { + def deprecatedAnnotation = exactMethod.getAnnotation(Deprecated) + if (deprecatedAnnotation != null) { + // may be false positive when the explicit method is deprecated + addViolation(call) + } + } else { + addViolation(call) + } } private boolean isGradleGroup(MethodCallExpression call) {