Skip to content

Commit

Permalink
stop using org.gradle.api.plugins.quality.internal.CodeNarcReportsImp…
Browse files Browse the repository at this point in the history
…l to support Gradle 8.11 (#409)
  • Loading branch information
rpalcolea authored Oct 8, 2024
1 parent a12ad89 commit 6f583e5
Show file tree
Hide file tree
Showing 7 changed files with 313 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,54 +15,77 @@
*/
package com.netflix.nebula.lint.plugin

import com.netflix.nebula.interop.GradleKt
import com.netflix.nebula.lint.GradleLintPatchAction
import com.netflix.nebula.lint.StyledTextService
import com.netflix.nebula.lint.utils.DeprecationLoggerUtils
import com.netflix.nebula.lint.plugin.report.LintReport
import com.netflix.nebula.lint.plugin.report.internal.LintHtmlReport
import com.netflix.nebula.lint.plugin.report.internal.LintTextReport
import com.netflix.nebula.lint.plugin.report.internal.LintXmlReport
import org.codenarc.AnalysisContext
import org.codenarc.report.HtmlReportWriter
import org.codenarc.report.ReportWriter
import org.codenarc.report.TextReportWriter
import org.codenarc.report.XmlReportWriter
import org.codenarc.results.Results
import org.codenarc.rule.Violation
import org.gradle.api.Action
import org.gradle.api.DefaultTask
import org.gradle.api.GradleException
import org.gradle.api.plugins.quality.CodeNarcReports
import org.gradle.api.plugins.quality.internal.CodeNarcReportsImpl
import org.gradle.api.InvalidUserDataException
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.Property
import org.gradle.api.reporting.Report
import org.gradle.api.reporting.Reporting
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.Nested
import org.gradle.api.tasks.TaskAction
import org.gradle.api.tasks.VerificationTask
import org.gradle.internal.reflect.Instantiator
import org.gradle.util.GradleVersion

import javax.inject.Inject

import static com.netflix.nebula.lint.StyledTextService.Styling.Bold

abstract class GradleLintReportTask extends DefaultTask implements VerificationTask, Reporting<CodeNarcReports> {

@Nested
private final CodeNarcReportsImpl reports
abstract class GradleLintReportTask extends DefaultTask implements VerificationTask {

@Input
abstract Property<Boolean> getReportOnlyFixableViolations()

GradleLintReportTask() {
reports = project.objects.newInstance(CodeNarcReportsImpl.class, this)
@Internal
final Property<String> projectName

@Internal
final DirectoryProperty reportsDir

@Internal
final NamedDomainObjectContainer<LintReport> reports

@Inject
GradleLintReportTask(ObjectFactory objects) {
projectName = objects.property(String).convention(project.name)
reportsDir = objects.directoryProperty()
reports =
objects.domainObjectContainer(
LintReport, { name ->
switch (name) {
case "html":
return objects.newInstance(LintHtmlReport, objects, this)
case "xml":
return objects.newInstance(LintXmlReport, objects, this)
case "text":
return objects.newInstance(LintTextReport, objects, this)
default:
throw new InvalidUserDataException(name + " is invalid as the report name")
}
})
reports.create('text', {
it.required.set(true)
})
reports.create('xml')
reports.create('html')
outputs.upToDateWhen { false }
group = 'lint'
}

@TaskAction
void generateReport() {
if (reports.enabled) {
if (reports.any { it.required.isPresent() && it.required.get()}) {
def lintService = new LintService()
def results = lintService.lint(project, false)
filterOnlyFixableViolations(results)
Expand All @@ -73,24 +96,10 @@ abstract class GradleLintReportTask extends DefaultTask implements VerificationT
textOutput.withStyle(Bold).text("$violationCount lint violation${violationCount == 1 ? '' : 's'}")
textOutput.println(' in this project')

reports.enabled.each { Report r ->
ReportWriter writer = null

if (GradleKt.versionCompareTo(project.gradle, '7.1') >= 0) {
switch (r.name) {
case 'xml': writer = new XmlReportWriter(outputFile: r.outputLocation.get().asFile); break
case 'html': writer = new HtmlReportWriter(outputFile: r.outputLocation.get().asFile); break
case 'text': writer = new TextReportWriter(outputFile: r.outputLocation.get().asFile); break
}
} else {
switch (r.name) {
case 'xml': writer = new XmlReportWriter(outputFile: r.destination); break
case 'html': writer = new HtmlReportWriter(outputFile: r.destination); break
case 'text': writer = new TextReportWriter(outputFile: r.destination); break
}
reports.each {
if(it.required.isPresent() && it.required.get()) {
it.write(new AnalysisContext(ruleSet: lintService.ruleSet(project)), results)
}

writer.writeReport(new AnalysisContext(ruleSet: lintService.ruleSet(project)), results)
}

int errors = results.violations.count { Violation v -> v.rule.priority == 1 }
Expand All @@ -101,6 +110,7 @@ abstract class GradleLintReportTask extends DefaultTask implements VerificationT

}


@Inject
Instantiator getInstantiator() {
null // see http://gradle.1045684.n5.nabble.com/injecting-dependencies-into-task-instances-td5712637.html
Expand All @@ -109,20 +119,21 @@ abstract class GradleLintReportTask extends DefaultTask implements VerificationT
/**
* Returns the reports to be generated by this task.
*/
@Override
CodeNarcReports getReports() {
NamedDomainObjectContainer<LintReport> getReports() {
reports
}

/**
* Configures the reports to be generated by this task.
*/
@Override
CodeNarcReports reports(Closure closure) {
NamedDomainObjectContainer<LintReport> reports(Closure closure) {
reports.configure(closure)
}

CodeNarcReports reports(Action<? super CodeNarcReports> action) {
/**
* Configures the reports to be generated by this task.
*/
NamedDomainObjectContainer<LintReport> reports(Action<? super LintReport> action) {
return action.execute(reports)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
package com.netflix.nebula.lint.plugin.report

import com.netflix.nebula.lint.plugin.GradleLintReportTask
import org.codehaus.groovy.runtime.GeneratedClosure
import org.codenarc.AnalysisContext
import org.codenarc.report.AbstractReportWriter
import org.codenarc.results.Results
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider
import org.gradle.api.reporting.CustomizableHtmlReport
import org.gradle.api.reporting.Report
import org.gradle.api.reporting.SingleFileReport
import org.gradle.api.resources.TextResource
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.Internal
import org.gradle.internal.metaobject.ConfigureDelegate
import org.gradle.util.internal.ClosureBackedAction

import javax.annotation.Nullable
import javax.inject.Inject

abstract class LintReport implements SingleFileReport,
CustomizableHtmlReport {
private final RegularFileProperty destination
private final Property<Boolean> isEnabled
private final Property<Boolean> isRequired
private final GradleLintReportTask task

@Inject
LintReport(ObjectFactory objects, GradleLintReportTask task) {
this.destination = objects.fileProperty()
this.isEnabled = objects.property(Boolean.class)
this.isRequired = objects.property(Boolean.class).value(Boolean.TRUE)
this.task = task
}

/**
* @deprecated use {@link #getOutputLocation()} instead.
*/
@Deprecated
@Internal
File getDestination() {
return destination.get().getAsFile()
}

// @Override // New API from 6.1 see https://github.com/gradle/gradle/issues/11923
@Override
public RegularFileProperty getOutputLocation() {
return destination
}

@Override
@Internal("This property returns always same value")
public OutputType getOutputType() {
return OutputType.FILE
}

// @Override // New API from 6.1 see https://github.com/gradle/gradle/issues/11923
@Input
Property<Boolean> getRequired() {
return isRequired
}

/**
* @deprecated use {@link #getRequired()} instead.
*/
@Deprecated
@Internal
boolean isEnabled() {
return isRequired.get()
}

/**
* @deprecated use {@code getRequired().set(value)} instead.
*/
@Deprecated
void setEnabled(boolean b) {
isRequired.set(b)
}

/**
* @deprecated use {@code getRequired().set(provider)} instead.
*/
@Deprecated
void setEnabled(Provider<Boolean> provider) {
isRequired.set(provider)
}

/**
* @deprecated use {@code getOutputLocation().set(provider)} instead.
*/
@Deprecated
void setDestination(File file) {
destination.set(file)
}

/**
* @deprecated use {@code getOutputLocation().set(provider)} instead.
*/
@Deprecated
void setDestination(Provider<File> provider) {
destination.set(this.task.getProject().getLayout().file(provider))
}

@Override
Report configure(Closure closure) {
configureSelf(closure, this)
return this
}

@Override
@Internal("This property provides only a human readable name.")
String getDisplayName() {
return String.format("%s type report generated by the task %s", getName(), getTask().getPath())
}

@Override
TextResource getStylesheet() {
return null
}

@Override
void setStylesheet(@Nullable TextResource textResource) {
throw new UnsupportedOperationException(
String.format("stylesheet property is not available in the %s type report", getName()))
}

void setStylesheet(@Nullable String path) {
throw new UnsupportedOperationException(
String.format("stylesheet property is not available in the %s type report", getName()))
}

@Internal
protected final GradleLintReportTask getTask() {
return task
}

abstract AbstractReportWriter getWriter()

void write(AnalysisContext analysisContext, Results results) {
writer.writeReport(analysisContext, results)
}

/**
* Called from an object's {@link org.gradle.util.Configurable#configure} method.
*/
private static <T> T configureSelf(@Nullable Closure configureClosure, T target) {
if (configureClosure == null) {
return target
}

configureTarget(configureClosure, target, new ConfigureDelegate(configureClosure, target))
return target
}

private static <T> void configureTarget(Closure configureClosure, T target, ConfigureDelegate closureDelegate) {
if (!(configureClosure instanceof GeneratedClosure)) {
new ClosureBackedAction<T>(configureClosure, Closure.DELEGATE_FIRST, false).execute(target)
return
}

// Hackery to make closure execution faster, by short-circuiting the expensive property and method lookup on Closure
Closure withNewOwner = configureClosure.rehydrate(target, closureDelegate, configureClosure.getThisObject())
new ClosureBackedAction<T>(withNewOwner, Closure.OWNER_ONLY, false).execute(target)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.netflix.nebula.lint.plugin.report.internal

import com.netflix.nebula.lint.plugin.GradleLintReportTask
import com.netflix.nebula.lint.plugin.report.LintReport
import org.codenarc.report.AbstractReportWriter
import org.codenarc.report.HtmlReportWriter
import org.gradle.api.file.RegularFile
import org.gradle.api.model.ObjectFactory

import javax.inject.Inject

abstract class LintHtmlReport extends LintReport {
@Inject
LintHtmlReport(ObjectFactory objects, GradleLintReportTask task) {
super(objects, task)
}

@Override
String getName() {
return "html"
}

@Override
AbstractReportWriter getWriter() {
return new HtmlReportWriter(outputFile: outputLocation.get().asFile)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.netflix.nebula.lint.plugin.report.internal

import com.netflix.nebula.lint.plugin.GradleLintReportTask
import com.netflix.nebula.lint.plugin.report.LintReport
import org.codenarc.report.AbstractReportWriter
import org.codenarc.report.TextReportWriter
import org.gradle.api.file.RegularFile
import org.gradle.api.model.ObjectFactory

import javax.inject.Inject

abstract class LintTextReport extends LintReport {
@Inject
LintTextReport(ObjectFactory objects, GradleLintReportTask task) {
super(objects, task)
}

@Override
String getName() {
return "text"
}

@Override
AbstractReportWriter getWriter() {
return new TextReportWriter(outputFile: outputLocation.get().asFile)
}
}
Loading

0 comments on commit 6f583e5

Please sign in to comment.