From 4d506206b8ae4e5a7cac10ded9684d985ead53a3 Mon Sep 17 00:00:00 2001 From: Jesper Skov Date: Sun, 21 Jan 2024 19:31:32 +0100 Subject: [PATCH] Add sonar scanning (#11) --- build.gradle | 3 + gradle.properties | 3 + .../java/dk/mada/style/MadaStylePlugin.java | 30 ++++++++- .../style/config/ConfigFileExtractor.java | 2 +- .../style/config/PluginConfiguration.java | 62 ++++++++++++----- .../configurators/CheckstyleConfigurator.java | 16 ++--- .../configurators/SonarConfigurator.java | 66 +++++++++++++++++++ .../config/checkstyle/checkstyle-mada.xml | 2 +- 8 files changed, 157 insertions(+), 27 deletions(-) create mode 100644 src/main/java/dk/mada/style/configurators/SonarConfigurator.java diff --git a/build.gradle b/build.gradle index a954b93..c215fb9 100644 --- a/build.gradle +++ b/build.gradle @@ -29,6 +29,9 @@ dependencies { // null-checker implementation "net.ltgt.gradle:gradle-errorprone-plugin:3.1.0" + // sonar + implementation "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:4.4.1.3373" + // dependencies added by the plugin // not used in the plugin code, but here Dependabot will keep them up to date addedDependencies "com.uber.nullaway:nullaway:0.10.21@jar" diff --git a/gradle.properties b/gradle.properties index 891640c..7ac0b0a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,6 @@ org.gradle.caching=true version = 0.0.0-SNAPSHOT + +dk.mada.style.sonar.projectKey = jskov_mada-style-gradle +dk.mada.style.sonar.organization = jskov-github diff --git a/src/main/java/dk/mada/style/MadaStylePlugin.java b/src/main/java/dk/mada/style/MadaStylePlugin.java index 2cf4068..3909900 100644 --- a/src/main/java/dk/mada/style/MadaStylePlugin.java +++ b/src/main/java/dk/mada/style/MadaStylePlugin.java @@ -2,9 +2,12 @@ import org.gradle.api.Plugin; import org.gradle.api.Project; +import org.gradle.api.logging.Logger; import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.plugins.quality.CheckstyleExtension; import org.gradle.api.plugins.quality.CheckstylePlugin; +import org.sonarqube.gradle.SonarExtension; +import org.sonarqube.gradle.SonarQubePlugin; import com.diffplug.gradle.spotless.SpotlessExtension; import com.diffplug.gradle.spotless.SpotlessPlugin; @@ -13,6 +16,7 @@ import dk.mada.style.config.PluginConfiguration; import dk.mada.style.configurators.CheckstyleConfigurator; import dk.mada.style.configurators.ErrorProneConfigurator; +import dk.mada.style.configurators.SonarConfigurator; import dk.mada.style.configurators.SpotlessConfigurator; import net.ltgt.gradle.errorprone.ErrorPronePlugin; @@ -26,8 +30,11 @@ public void apply(Project project) { } private void applyPlugins(Project project) { + Logger logger = project.getLogger(); + logger.info("Applying mada.style plugin"); + var configuration = new PluginConfiguration(project); - var configExtractor = new ConfigFileExtractor(project.getLogger(), project.getGradle().getGradleHomeDir().toPath()); + var configExtractor = new ConfigFileExtractor(logger, project.getGradle().getGradleHomeDir().toPath()); if (configuration.isCheckstyleActive()) { project.getPluginManager().apply("checkstyle"); @@ -47,6 +54,16 @@ private void applyPlugins(Project project) { project.getPlugins().withType(ErrorPronePlugin.class, ep -> new ErrorProneConfigurator(project, configuration.errorProne(), configuration.nullchecker()).configure()); } + + if (configuration.isSonarActive()) { + // This should be a Gradle property, but is a system property + // https://sonarsource.atlassian.net/browse/SONARGRADL-134 + System.setProperty("sonar.gradle.skipCompile", "true"); + + project.getPluginManager().apply("org.sonarqube"); + + project.getPlugins().withType(SonarQubePlugin.class, sp -> lazyConfigureSonar(project, configuration)); + } } /** @@ -72,4 +89,15 @@ private void lazyConfigureFormatter(Project project, PluginConfiguration configu project.getExtensions().configure(SpotlessExtension.class, se -> new SpotlessConfigurator(project.getLogger(), configuration.formatter(), configExtractor).configure(se)); } + + /** + * Lazy configure sonar extension. + * + * @param project the project + * @param configuration the plugin configuration + */ + private void lazyConfigureSonar(Project project, PluginConfiguration configuration) { + project.getExtensions().configure(SonarExtension.class, + se -> new SonarConfigurator(project, configuration.sonar()).configure(se)); + } } diff --git a/src/main/java/dk/mada/style/config/ConfigFileExtractor.java b/src/main/java/dk/mada/style/config/ConfigFileExtractor.java index 067b7de..16a4ddf 100644 --- a/src/main/java/dk/mada/style/config/ConfigFileExtractor.java +++ b/src/main/java/dk/mada/style/config/ConfigFileExtractor.java @@ -53,7 +53,7 @@ public Path getLocalConfigFile(String path) { throw new IllegalStateException("Failed to read " + path + " from data checksums: " + dataChecksums); } - String suffix = "." + path.replaceAll(".*[.]", ""); + String suffix = path.substring(path.lastIndexOf('.')); String filename = path.replace('/', ':').replace(suffix, "") + "-" + value + suffix; Path targetFile = madaConfigDir.resolve(filename); diff --git a/src/main/java/dk/mada/style/config/PluginConfiguration.java b/src/main/java/dk/mada/style/config/PluginConfiguration.java index 9561f18..b81fd4d 100644 --- a/src/main/java/dk/mada/style/config/PluginConfiguration.java +++ b/src/main/java/dk/mada/style/config/PluginConfiguration.java @@ -1,5 +1,7 @@ package dk.mada.style.config; +import java.util.Map; + import org.gradle.api.Project; import org.jspecify.annotations.Nullable; @@ -21,7 +23,9 @@ public class PluginConfiguration { /** The parsed formatter configuration. */ private final FormatterConfiguration formatterConf; /** The parsed null-checker configuration. */ - private final NullcheckerConfiguration nullcheckerProps; + private final NullcheckerConfiguration nullcheckerConf; + /** The parsed sonar configuration. */ + private final SonarConfiguration sonarConf; /** * Checkstyle configuration. @@ -38,6 +42,19 @@ public record CheckstyleConfiguration(boolean enabled, boolean ignoreFailures, b @Nullable String configPath) { } + /** + * ErrorProne configuration. + * + * @param enabled flag to activate error prone + * @param ignoreTestSource flag to ignore test source files + * @param ignoreGeneratedSource flag to ignore generated source files + * @param excludePathsRegexp a regular expression of source paths to ignore + * @param disabledRules a comma-separated list of rule names to disable + */ + public record ErrorProneConfiguration(boolean enabled, boolean ignoreTestSource, boolean ignoreGeneratedSource, + String excludePathsRegexp, String disabledRules) { + } + /** * Formatter configuration. * @@ -60,16 +77,12 @@ public record NullcheckerConfiguration(boolean enabled, String includePackages, } /** - * ErrorProne configuration. + * SonarSource sonar configuration. * - * @param enabled flag to activate error prone - * @param ignoreTestSource flag to ignore test source files - * @param ignoreGeneratedSource flag to ignore generated source files - * @param excludePathsRegexp a regular expression of source paths to ignore - * @param disabledRules a comma-separated list of rule names to disable + * @param enabled flag to activate sonar + * @param madaConventionProperties convention properties for sonar */ - public record ErrorProneConfiguration(boolean enabled, boolean ignoreTestSource, boolean ignoreGeneratedSource, - String excludePathsRegexp, String disabledRules) { + public record SonarConfiguration(boolean enabled, Map madaConventionProperties) { } /** @@ -105,10 +118,17 @@ public PluginConfiguration(Project project) { getProperty("formatter.exclude", ""), getNullableProperty("formatter.eclipse-config-path", null)); - nullcheckerProps = new NullcheckerConfiguration( + nullcheckerConf = new NullcheckerConfiguration( getBoolProperty("null-checker.enabled", true), getProperty("null-checker.include-packages", "dk"), getProperty("null-checker.exclude-packages", "")); + + sonarConf = new SonarConfiguration( + getBoolProperty("sonar.enabled", true), + Map.of( + "sonar.host.url", "https://sonarcloud.io", + "sonar.inclusions", "**/src/main/**", + "sonar.sourceEncoding", "UTF-8")); } /** {@return the CheckStyle configuration} */ @@ -131,14 +151,19 @@ public boolean isErrorProneActive() { return errorProne().enabled(); } + /** {@return the formatter configuration} */ + public FormatterConfiguration formatter() { + return formatterConf; + } + /** {@return true if the formatter is active} */ public boolean isFormatterActive() { return formatter().enabled(); } - /** {@return the formatter configuration} */ - public FormatterConfiguration formatter() { - return formatterConf; + /** {@return the null-checker configuration} */ + public NullcheckerConfiguration nullchecker() { + return nullcheckerConf; } /** {@return true if the null-checker is active} */ @@ -146,9 +171,14 @@ public boolean isNullcheckerActive() { return nullchecker().enabled(); } - /** {@return the null-checker configuration} */ - public NullcheckerConfiguration nullchecker() { - return nullcheckerProps; + /** {@return the sonar configuration} */ + public SonarConfiguration sonar() { + return sonarConf; + } + + /** {@return true if sonar is active} */ + public boolean isSonarActive() { + return sonar().enabled(); } private boolean getBoolProperty(String name, boolean defaultValue) { diff --git a/src/main/java/dk/mada/style/configurators/CheckstyleConfigurator.java b/src/main/java/dk/mada/style/configurators/CheckstyleConfigurator.java index 162c12e..7dc461b 100644 --- a/src/main/java/dk/mada/style/configurators/CheckstyleConfigurator.java +++ b/src/main/java/dk/mada/style/configurators/CheckstyleConfigurator.java @@ -8,6 +8,7 @@ import org.gradle.api.logging.Logger; import org.gradle.api.plugins.quality.Checkstyle; import org.gradle.api.plugins.quality.CheckstyleExtension; +import org.gradle.api.tasks.TaskContainer; import dk.mada.style.config.ConfigFileExtractor; import dk.mada.style.config.PluginConfiguration.CheckstyleConfiguration; @@ -56,17 +57,16 @@ public void configure(CheckstyleExtension ce) { if (toolVersion != null) { ce.setToolVersion(toolVersion); } + TaskContainer taskContainer = project.getTasks(); if (checkstyleConfig.ignoreTestSource()) { - project.getTasks().named("checkstyleTest", this::disableTask); + taskContainer.named("checkstyleTest", this::disableTask); } - if (checkstyleConfig.ignoreGeneratedSource()) { - project.getTasks().withType(Checkstyle.class, t -> { - if (t.getName().endsWith("Apt")) { - disableTask(t); - } - }); - } + taskContainer.withType(Checkstyle.class, t -> { + if (checkstyleConfig.ignoreGeneratedSource() && t.getName().endsWith("Apt")) { + disableTask(t); + } + }); } private void disableTask(Task t) { diff --git a/src/main/java/dk/mada/style/configurators/SonarConfigurator.java b/src/main/java/dk/mada/style/configurators/SonarConfigurator.java new file mode 100644 index 0000000..2305429 --- /dev/null +++ b/src/main/java/dk/mada/style/configurators/SonarConfigurator.java @@ -0,0 +1,66 @@ +package dk.mada.style.configurators; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +import org.gradle.api.Project; +import org.gradle.api.logging.Logger; +import org.gradle.api.plugins.quality.Checkstyle; +import org.gradle.api.tasks.TaskContainer; +import org.sonarqube.gradle.SonarExtension; +import org.sonarqube.gradle.SonarTask; + +import dk.mada.style.config.PluginConfiguration.SonarConfiguration; + +/** + * Configures Sonar. + */ +public class SonarConfigurator { + /** The gradle project. */ + private final Project project; + /** The gradle logger. */ + private final Logger logger; + /** The sonarqube configuration. */ + private final SonarConfiguration sonarConfig; + + /** + * Creates new instance. + * + * @param project the gradle project + * @param sonarConfig the sonarqube configuration + */ + public SonarConfigurator(Project project, SonarConfiguration sonarConfig) { + this.project = project; + this.logger = project.getLogger(); + this.sonarConfig = sonarConfig; + } + + /** + * Configures the sonarqube extension. + * + * @param se the spotless extension + */ + public void configure(SonarExtension se) { + logger.info("dk.mada.style configure sonar"); + + TaskContainer taskContainer = project.getTasks(); + + // Make sonar depend on checkstyle tasks (we want sonar to run last) + taskContainer.withType(SonarTask.class, sonar -> taskContainer.withType(Checkstyle.class, sonar::dependsOn)); + + Map inputProps = project.getProperties().entrySet().stream() + .filter(e -> !e.getKey().equals("dk.mada.style.sonar.enabled")) + .filter(e -> e.getKey().startsWith("dk.mada.style.sonar.")) + .collect(Collectors.toMap(e -> e.getKey().replace("dk.mada.style.", ""), e -> Objects.toString(e.getValue()))); + + Map combinedMadaSonarProps = new HashMap<>(); + combinedMadaSonarProps.putAll(sonarConfig.madaConventionProperties()); + combinedMadaSonarProps.putAll(inputProps); + + logger.info("Set sonar properties: {}", combinedMadaSonarProps); + + se.properties(sp -> combinedMadaSonarProps.forEach(sp::property)); + } +} diff --git a/src/main/resources/config/checkstyle/checkstyle-mada.xml b/src/main/resources/config/checkstyle/checkstyle-mada.xml index 206e138..2eecd2a 100644 --- a/src/main/resources/config/checkstyle/checkstyle-mada.xml +++ b/src/main/resources/config/checkstyle/checkstyle-mada.xml @@ -91,7 +91,7 @@ - +