Initial commit

This commit is contained in:
Erik C. Thauvin 2023-11-25 14:41:22 -08:00
commit b333128660
55 changed files with 1638 additions and 0 deletions

View file

@ -0,0 +1,552 @@
/*
* Copyright 2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rife.bld.extension;
import rife.bld.BaseProject;
import rife.bld.operations.AbstractProcessOperation;
import java.io.File;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Performs static code analysis with <a href="https://detekt.dev/">Detekt</a>.
*
* @author <a href="https://erik.thauvin.net/">Erik C. Thauvin</a>
* @since 1.0
*/
public class DetektOperation extends AbstractProcessOperation<DetektOperation> {
private static final Logger LOGGER = Logger.getLogger(DetektReport.class.getName());
private final Collection<String> classpath_ = new ArrayList<>();
private final Collection<String> config_ = new ArrayList<>();
private final Collection<String> input_ = new ArrayList<>();
private final Collection<String> plugins_ = new ArrayList<>();
private final Collection<DetektReport> report_ = new ArrayList<>();
private boolean allRules_ = false;
private boolean autoCorrect_ = false;
private String basePath_;
private String baseline_;
private boolean buildUponDefaultConfig_;
private String configResource_;
private boolean createBaseline_;
private boolean debug_ = false;
private boolean disableDefaultRuleSets_ = false;
private String excludes_;
private boolean generateConfig_;
private String includes_;
private String jdkHome_;
private String jvmTarget_;
private String languageVersion_;
private int maxIssues_;
private boolean parallel_;
private BaseProject project_;
/**
* Activates all available (even unstable) rules.
*
* @param allRules {@code true} or {@code false}
* @return this operation instance
*/
public DetektOperation allRules(boolean allRules) {
allRules_ = allRules;
return this;
}
/**
* Allow rules to auto correct code if they support it. The default rule
* sets do NOT support auto correcting and won't change any line in the
* users code base. However custom rules can be written to support auto
* correcting. The additional 'formatting' rule set, added with
* {@link #plugins(String...) Plugins}, does support it and needs this flag.
*
* @param autoCorrect {@code true} or {@code false}
* @return this operation instance
*/
public DetektOperation autoCorrect(boolean autoCorrect) {
autoCorrect_ = autoCorrect;
return this;
}
/**
* Specifies a directory as the base path. Currently it impacts all file
* paths in the formatted reports. File paths in console output and txt
* report are not affected and remain as absolute paths.
*
* @param path the directory path
* @return this operation instance
*/
public DetektOperation basePath(String path) {
basePath_ = path;
return this;
}
/**
* If a baseline xml file is passed in, only new code smells not in the
* baseline are printed in the console.
*
* @param baseline the baseline xml file
* @return this operation instance
*/
public DetektOperation baseline(String baseline) {
baseline_ = baseline;
return this;
}
/**
* Preconfigures detekt with a bunch of rules and some opinionated defaults
* for you. Allows additional provided configurations to override the
* defaults.
*
* @param buildUponDefaultConfig {@code true} or {@code false}
* @return this operation instance
*/
public DetektOperation buildUponDefaultConfig(boolean buildUponDefaultConfig) {
buildUponDefaultConfig_ = buildUponDefaultConfig;
return this;
}
/**
* EXPERIMENTAL: Paths where to find user class files and depending jar
* files. Used for type resolution.
*
* @param paths one or more files
* @return this operation instance
*/
public DetektOperation classPath(String... paths) {
classpath_.addAll(List.of(paths));
return this;
}
/**
* EXPERIMENTAL: Paths where to find user class files and depending jar
* files. Used for type resolution.
*
* @param paths the list of files
* @return this operation instance
*/
public DetektOperation classPath(Collection<String> paths) {
classpath_.addAll(paths);
return this;
}
/**
* Path to the config file ({@code path/to/config.yml}).
*
* @param configs one or more config files
* @return this operation instance
*/
public DetektOperation config(String... configs) {
config_.addAll(List.of(configs));
return this;
}
/**
* Path to the config file ({@code path/to/config.yml}).
*
* @param configs the list pf config files
* @return this operation instance
*/
public DetektOperation config(Collection<String> configs) {
config_.addAll(configs);
return this;
}
/**
* Path to the config resource on detekt's classpath ({@code path/to/config.yml}).
*
* @param resource the config resource path
* @return this operation instance
*/
public DetektOperation configResource(String resource) {
configResource_ = resource;
return this;
}
/**
* Treats current analysis findings as a smell baseline for future detekt
* runs.
*
* @param createBaseline {@code true} or {@code false}
* @return this operation instance
*/
public DetektOperation createBaseline(boolean createBaseline) {
createBaseline_ = createBaseline;
return this;
}
/**
* Prints extra information about configurations and extensions.
*
* @param debug {@code true} or {@code false}
* @return this operation instance
*/
public DetektOperation debug(boolean debug) {
debug_ = debug;
return this;
}
/**
* Disables default rule sets.
*
* @param disable {@code true} or {@code false}
* @return this operation instance
*/
public DetektOperation disableDefaultRuleSets(boolean disable) {
disableDefaultRuleSets_ = disable;
return this;
}
/**
* Globbing patterns describing paths to exclude from the analysis.
*
* @param patterns the patterns
* @return this operation instance
*/
public DetektOperation excludes(String patterns) {
excludes_ = patterns;
return this;
}
/**
* Part of the {@link #execute} operation, constructs the command list
* to use for building the process.
*/
@Override
protected List<String> executeConstructProcessCommandList() {
if (project_ == null) {
LOGGER.severe("A project must be specified.");
}
final List<String> args = new ArrayList<>();
args.add(javaTool());
args.add("-cp");
args.add(Path.of(project_.libBldDirectory().getAbsolutePath(), "*").toString());
args.add("io.gitlab.arturbosch.detekt.cli.Main");
// all-rules
if (allRules_) {
args.add("--all-rules");
}
// auto-correct
if (autoCorrect_) {
args.add("--auto-correct");
}
// base-path
if (basePath_ != null) {
args.add("--base-path");
args.add(basePath_);
}
// baseline
if (baseline_ != null) {
args.add("--baseline");
args.add(baseline_);
}
// build-upon-default-config
if (buildUponDefaultConfig_) {
args.add("--build-upon-default-config");
}
// classpath
if (!classpath_.isEmpty()) {
args.add("--classpath");
args.add(String.join(File.pathSeparator, classpath_));
}
// config
if (!config_.isEmpty()) {
args.add("-config");
args.add(String.join(";", config_));
}
// config-resource
if (configResource_ != null) {
args.add("--config-resource");
args.add(configResource_);
}
// create-baseline
if (createBaseline_) {
args.add("--create-baseline");
}
// debug
if (debug_) {
args.add("--debug");
}
// disable-default-rulesets
if (disableDefaultRuleSets_) {
args.add("--disable-default-rulesets");
}
// excludes
if (excludes_ != null) {
args.add("--excludes");
}
// generate-config
if (generateConfig_) {
args.add("--generate-config");
}
// includes
if (includes_ != null) {
args.add("--includes");
args.add(includes_);
}
// input
if (!input_.isEmpty()) {
args.add("--input");
args.add(String.join(",", input_));
}
// jdk-home
if (jdkHome_ != null) {
args.add("--jdk-home");
args.add(jdkHome_);
}
// jvm-target
if (jvmTarget_ != null) {
args.add("--jvm-target");
args.add(jvmTarget_);
}
// language-version
if (languageVersion_ != null) {
args.add("--language-version");
args.add(languageVersion_);
}
// max-issues
if (maxIssues_ > 0) {
args.add("--max-issues");
args.add(String.valueOf(maxIssues_));
}
// parallel
if (parallel_) {
args.add("--parallel");
}
// plugins
if (!plugins_.isEmpty()) {
args.add("--plugins");
args.add(String.join(".", plugins_));
}
// report
if (!report_.isEmpty()) {
report_.forEach(it -> {
args.add("-r");
args.add(it.id().name().toLowerCase() + ":" + it.path());
});
}
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.fine(String.join(" ", args));
}
return args;
}
/**
* Configures the operation from a {@link BaseProject}.
* <p>
* Sets the {@link #input input} to {@code src/main/kotlin}, {@code src/test/kotlin} and {@code detekt-baseline.xml}
* if they exist.
*
* @param project the project to configure the operation from
* @return this operation instance
*/
@Override
public DetektOperation fromProject(BaseProject project) {
project_ = project;
var main = new File(project.srcMainDirectory(), "kotlin");
if (main.exists()) {
input_.add(main.getAbsolutePath());
}
var test = new File(project.srcTestDirectory(), "kotlin");
if (test.exists()) {
input_.add(test.getAbsolutePath());
}
var baseline = new File(project.workDirectory(), "detekt-baseline.xml");
if (baseline.exists()) {
baseline_ = baseline.getAbsolutePath();
}
return this;
}
/**
* Export default config. Path can be specified with {@link #config config} option.
* <p>
* Default path: {@code default-detekt-config.yml}
*
* @param generate {@code true} or {@code false}
* @return this operation instance
*/
public DetektOperation generateConfig(boolean generate) {
generateConfig_ = generate;
return this;
}
/**
* Globbing patterns describing paths to include in the analysis. Useful in
* combination with {@link #excludes(String) excludes} patterns.
*
* @param patterns the patterns
* @return this operation instance
*/
public DetektOperation includes(String patterns) {
includes_ = patterns;
return this;
}
/**
* Input paths to analyze. If not specified the current working directory is used.
*
* @param paths the list of paths
* @return this operation instance
*/
public DetektOperation input(Collection<String> paths) {
input_.addAll(paths);
return this;
}
/**
* Input paths to analyze. If not specified the current working directory is used.
*
* @param paths one or more paths
* @return this operation instance
*/
public DetektOperation input(String... paths) {
input_.addAll(List.of(paths));
return this;
}
/**
* EXPERIMENTAL: Use a custom JDK home directory to include into the
* classpath.
*
* @param path the JDK home directory path
* @return this operation instance
*/
public DetektOperation jdkHome(String path) {
jdkHome_ = path;
return this;
}
/**
* EXPERIMENTAL: Target version of the generated JVM bytecode that was
* generated during compilation and is now being used for type resolution
* <p>
* Default: 1.8
*
* @param target the target version
* @return this operation instance
*/
public DetektOperation jvmTarget(String target) {
jvmTarget_ = target;
return this;
}
/**
* EXPERIMENTAL: Compatibility mode for Kotlin language version X.Y,
* reports errors for all language features that came out later.
* <p>
* Possible Values: [1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 2.1]
*
* @param version the version
* @return this operation instance
*/
public DetektOperation languageVersion(String version) {
languageVersion_ = version;
return this;
}
/**
* Return exit code 0 only when found issues count does not exceed
* specified issues count.
*
* @param max the issues code
* @return this operation instance
*/
public DetektOperation maxIssues(int max) {
maxIssues_ = max;
return this;
}
/**
* Enables parallel compilation and analysis of source files. Do some
* benchmarks first before enabling this flag. Heuristics show performance
* benefits starting from 2000 lines of Kotlin code.
*
* @param parallel{@code true} or {@code false}
* @return this operation instance
*/
public DetektOperation parallel(boolean parallel) {
parallel_ = parallel;
return this;
}
/**
* Enables parallel compilation and analysis of source files. Do some
* benchmarks first before enabling this flag. Heuristics show performance
* benefits starting from 2000 lines of Kotlin code.
*
* @param jars one or more jars
* @return this operation instance
*/
public DetektOperation plugins(String... jars) {
plugins_.addAll(List.of(jars));
return this;
}
/**
* Enables parallel compilation and analysis of source files. Do some
* benchmarks first before enabling this flag. Heuristics show performance
* benefits starting from 2000 lines of Kotlin code.
*
* @param jars the list of jars
* @return this operation instance
*/
public DetektOperation plugins(Collection<String> jars) {
plugins_.addAll(jars);
return this;
}
/**
* Generates a report for given 'report-id' and stores it on given 'path'.
*
* @param reports one or more reports
* @return this operation instance
*/
public DetektOperation report(DetektReport... reports) {
report_.addAll(List.of(reports));
return this;
}
}