Merge pull request #4843 from adangel/cli-banner-display

[cli] Display pmd banner, version improvement, logging
This commit is contained in:
Juan Martín Sotuyo Dodero 2024-03-05 16:57:31 -03:00 committed by GitHub
commit daa741c817
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 199 additions and 39 deletions

View File

@ -39,7 +39,7 @@ See also [Getting Started](https://docs.pmd-code.org/latest/pmd_userdocs_install
**Demo:**
This shows how PMD can detect for loops, that can be replaced by for-each loops.
This shows how PMD analyses [openjdk](https://github.com/openjdk/jdk):
![Demo](docs/images/userdocs/pmd-demo.gif)

View File

@ -0,0 +1,31 @@
## How to create pmd-demo.gif
### Prepare
```shell
mkdir $HOME/pmd-demo
cd $HOME/pmd-demo
curl -L -o jdk-master.zip https://github.com/openjdk/jdk/archive/refs/heads/master.zip
unzip jdk-master.zip
alias pmd=$HOME/PMD/source/pmd/pmd-dist/target/pmd-bin-7.0.0-SNAPSHOT/bin/pmd
clear
pmd --version
pmd check -R rulesets/java/quickstart.xml -d jdk-master/src/java.base -f text --cache pmd.cache --report-file jdk-report.txt
```
Second terminal window: `cd $HOME/pmd-demo; tail -f jdk-report.txt`
### Recording
Record screencast with https://github.com/EasyScreenCast/EasyScreenCast (a gnome3 extension)
The recorded screencast can be found in `$HOME/Videos`.
### Converting
Convert webm to gif: https://engineering.giphy.com/how-to-make-gifs-with-ffmpeg/
```shell
cd $HOME/Videos
ffmpeg -i pmd7-demo.webm -filter_complex "[0:v] fps=12,scale=960:-1,split [a][b];[a] palettegen [p];[b][p] paletteuse" pmd7-demo.gif
```

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 MiB

After

Width:  |  Height:  |  Size: 3.0 MiB

View File

@ -0,0 +1,23 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.cli.commands.internal;
import java.util.ArrayList;
import java.util.List;
import net.sourceforge.pmd.PMDVersion;
import net.sourceforge.pmd.cli.internal.PmdBanner;
import picocli.CommandLine;
class PMDVersionProvider implements CommandLine.IVersionProvider {
@Override
public String[] getVersion() throws Exception {
List<String> lines = new ArrayList<>(PmdBanner.loadBanner());
lines.add(PMDVersion.getFullVersionName());
lines.add("Java version: " + System.getProperty("java.version") + ", vendor: " + System.getProperty("java.vendor") + ", runtime: " + System.getProperty("java.home"));
return lines.toArray(new String[0]);
}
}

View File

@ -4,11 +4,8 @@
package net.sourceforge.pmd.cli.commands.internal;
import net.sourceforge.pmd.PMDVersion;
import picocli.AutoComplete.GenerateCompletion;
import picocli.CommandLine.Command;
import picocli.CommandLine.IVersionProvider;
@Command(name = "pmd", mixinStandardHelpOptions = true,
versionProvider = PMDVersionProvider.class,
@ -20,11 +17,3 @@ import picocli.CommandLine.IVersionProvider;
public class PmdRootCommand {
}
class PMDVersionProvider implements IVersionProvider {
@Override
public String[] getVersion() throws Exception {
return new String[] { "PMD " + PMDVersion.VERSION };
}
}

View File

@ -0,0 +1,39 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.cli.internal;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class PmdBanner {
private static final Logger LOGGER = LoggerFactory.getLogger(PmdBanner.class);
private static final String BANNER_RESOURCE = "/net/sourceforge/pmd/cli/internal/banner.txt";
private PmdBanner() {}
public static List<String> loadBanner() {
List<String> lines = new ArrayList<>();
try (BufferedReader bannerReader = new BufferedReader(
new InputStreamReader(PmdBanner.class.getResourceAsStream(BANNER_RESOURCE), StandardCharsets.UTF_8))) {
String line = bannerReader.readLine();
while (line != null) {
lines.add(line);
line = bannerReader.readLine();
}
} catch (IOException e) {
LOGGER.debug("Couldn't load banner", e);
}
return lines;
}
}

View File

@ -44,6 +44,10 @@ public final class PmdRootLogger {
// need to reload the logger with the new configuration
log = LoggerFactory.getLogger(PMD_CLI_LOGGER);
resetLogLevel = true;
// logging, mostly for testing purposes
Level defaultLogLevel = Slf4jSimpleConfiguration.getDefaultLogLevel();
log.debug("Log level is at {}", defaultLogLevel);
}
PmdReporter pmdReporter = setupMessageReporter();
@ -67,9 +71,6 @@ public final class PmdRootLogger {
PmdReporter pmdReporter = new SimpleMessageReporter(log);
// always install java.util.logging to slf4j bridge
Slf4jSimpleConfiguration.installJulBridge();
// logging, mostly for testing purposes
Level defaultLogLevel = Slf4jSimpleConfiguration.getDefaultLogLevel();
log.info("Log level is at {}", defaultLogLevel);
return pmdReporter;
}
}

View File

@ -0,0 +1,8 @@
████ ████
██ ██
██ █████ █ ███ ███ ███████ ██
███ ██ ███ ████ ████ ██ ██ ███
███ ███████ ██ ████ ██ ██ ██ ███
██ ██ ██ ██ ██ ███████ ██
██ ██
████ ████

View File

@ -11,6 +11,7 @@ import static org.hamcrest.CoreMatchers.startsWith;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.emptyString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.not;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
@ -90,7 +91,7 @@ class CpdCliTest extends BaseCliTest {
@Test
void debugLogging() throws Exception {
CliExecutionResult result = runCliSuccessfully("--debug", "--minimum-tokens", "340", "--dir", SRC_DIR);
result.checkStdErr(containsString("[main] INFO net.sourceforge.pmd.cli - Log level is at TRACE"));
result.checkStdErr(containsString("[DEBUG] Log level is at TRACE"));
}
@Test
@ -102,7 +103,7 @@ class CpdCliTest extends BaseCliTest {
@Test
void defaultLogging() throws Exception {
CliExecutionResult result = runCliSuccessfully("--minimum-tokens", "340", "--dir", SRC_DIR);
result.checkStdErr(containsString("[main] INFO net.sourceforge.pmd.cli - Log level is at INFO"));
result.checkStdErr(not(containsString("[DEBUG] Log level is at TRACE")));
}
@Test

View File

@ -199,14 +199,13 @@ class PmdCliTest extends BaseCliTest {
@Test
void debugLogging() throws Exception {
CliExecutionResult result = runCliSuccessfully("--debug", "--dir", srcDir.toString(), "--rulesets", RULESET_NO_VIOLATIONS);
result.checkStdErr(containsString("[main] INFO net.sourceforge.pmd.cli - Log level is at TRACE"));
result.checkStdErr(containsString("[DEBUG] Log level is at TRACE"));
}
@Test
void defaultLogging() throws Exception {
CliExecutionResult result = runCliSuccessfully("--dir", srcDir.toString(), "--rulesets", RULESET_NO_VIOLATIONS);
result.checkStdErr(containsString("[main] INFO net.sourceforge.pmd.cli - Log level is at INFO"));
result.checkStdErr(not(containsPattern("Adding file .*"))); // not in debug mode
result.checkStdErr(not(containsString("[DEBUG] Log level is at TRACE")));
}
@Test

View File

@ -11,8 +11,42 @@
<relativePath>../pom.xml</relativePath>
</parent>
<properties>
<!-- default values for the properties provided by git-commit-id-maven-plugin -->
<git.commit.id>unknown</git.commit.id>
<git.commit.time>unknown</git.commit.time>
</properties>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>io.github.git-commit-id</groupId>
<artifactId>git-commit-id-maven-plugin</artifactId>
<version>7.0.0</version>
<executions>
<execution>
<id>get-the-git-infos</id>
<goals>
<goal>revision</goal>
</goals>
<phase>initialize</phase>
</execution>
</executions>
<configuration>
<failOnUnableToExtractRepoInfo>true</failOnUnableToExtractRepoInfo>
<includeOnlyProperties>
<includeOnlyProperty>^git.commit.id$</includeOnlyProperty>
<includeOnlyProperty>^git.commit.time$</includeOnlyProperty>
</includeOnlyProperties>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>

View File

@ -23,24 +23,38 @@ public final class PMDVersion {
*/
public static final String VERSION;
private static final String UNKNOWN_VERSION = "unknown";
private static final String RELEASE_TIMESTAMP;
private static final String GIT_COMMIT_ID;
private static final String GIT_COMMIT_TIME;
private static final String UNKNOWN = "unknown";
/*
* Determines the version from maven's generated pom.properties file.
*/
static {
String pmdVersion = UNKNOWN_VERSION;
try (InputStream stream = PMDVersion.class.getResourceAsStream("/META-INF/maven/net.sourceforge.pmd/pmd-core/pom.properties")) {
String pmdVersion = UNKNOWN;
String releaseTimestamp = UNKNOWN;
String gitCommitId = UNKNOWN;
String gitCommitTime = UNKNOWN;
try (InputStream stream = PMDVersion.class.getResourceAsStream("pmd-core-version.properties")) {
if (stream != null) {
final Properties properties = new Properties();
properties.load(stream);
pmdVersion = properties.getProperty("version");
releaseTimestamp = properties.getProperty("releaseTimestamp");
gitCommitId = properties.getProperty("gitCommitId");
gitCommitTime = properties.getProperty("gitCommitTime");
}
} catch (final IOException e) {
LOG.debug("Couldn't determine version of PMD", e);
}
VERSION = pmdVersion;
RELEASE_TIMESTAMP = releaseTimestamp;
GIT_COMMIT_ID = gitCommitId;
GIT_COMMIT_TIME = gitCommitTime;
}
private PMDVersion() {
@ -55,7 +69,7 @@ public final class PMDVersion {
*/
public static String getNextMajorRelease() {
if (isUnknown()) {
return UNKNOWN_VERSION;
return UNKNOWN;
}
final int major = Integer.parseInt(VERSION.split("\\.")[0]);
@ -68,7 +82,7 @@ public final class PMDVersion {
*/
@SuppressWarnings("PMD.LiteralsFirstInComparisons")
public static boolean isUnknown() {
return UNKNOWN_VERSION.equals(VERSION);
return UNKNOWN.equals(VERSION);
}
/**
@ -78,4 +92,11 @@ public final class PMDVersion {
public static boolean isSnapshot() {
return VERSION.endsWith("-SNAPSHOT");
}
public static String getFullVersionName() {
if (isSnapshot()) {
return "PMD " + VERSION + " (" + GIT_COMMIT_ID + ", " + GIT_COMMIT_TIME + ")";
}
return "PMD " + VERSION + " (" + GIT_COMMIT_ID + ", " + RELEASE_TIMESTAMP + ")";
}
}

View File

@ -0,0 +1,11 @@
#
# BSD-style license; for more info see http://pmd.sourceforge.net/license.html
#
# version and releaseTimestamp are from the parent pom.xml
version=${project.version}
releaseTimestamp=${project.build.outputTimestamp}
# gitCommitId and gitCommitTime are provided by git-commit-id-maven-plugin
gitCommitId=${git.commit.id}
gitCommitTime=${git.commit.time}

View File

@ -9,6 +9,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
import net.sourceforge.pmd.FooRule;
import net.sourceforge.pmd.PMDVersion;
import net.sourceforge.pmd.lang.document.FileId;
import net.sourceforge.pmd.lang.document.FileLocation;
import net.sourceforge.pmd.lang.document.TextRange2d;
@ -49,9 +50,11 @@ class XSLTRendererTest extends AbstractRendererTest {
@Override
String filter(String expected) {
return expected.replaceAll("<h2>PMD unknown Report\\. Generated on .+</h2>",
"<h2>PMD unknown Report. Generated on ...</h2>")
.replaceAll("\r\n", "\n"); // make the test run on Windows, too
return expected.replaceAll("<h2>PMD " + PMDVersion.VERSION + " Report\\. Generated on .+</h2>",
"<h2>PMD unknown Report. Generated on ...</h2>")
.replaceAll("<title>PMD " + PMDVersion.VERSION + " Report</title>",
"<title>PMD unknown Report</title>")
.replaceAll("\r\n", "\n"); // make the test run on Windows, too
}
@Test

View File

@ -5,11 +5,11 @@
org.slf4j.simpleLogger.logFile=System.err
org.slf4j.simpleLogger.showDateTime=false
org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd'T'HH:mm:ss.SSSXXX
org.slf4j.simpleLogger.showThreadName=true
org.slf4j.simpleLogger.showThreadName=false
org.slf4j.simpleLogger.showThreadId=false
org.slf4j.simpleLogger.showLogName=true
org.slf4j.simpleLogger.showLogName=false
org.slf4j.simpleLogger.showShortLogName=false
org.slf4j.simpleLogger.levelInBrackets=false
org.slf4j.simpleLogger.levelInBrackets=true
# Default log level for all loggers
# Must be one of "trace", "debug", "info", "warn", "error" or "off"

View File

@ -5,11 +5,11 @@
org.slf4j.simpleLogger.logFile=System.err
org.slf4j.simpleLogger.showDateTime=false
org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd'T'HH:mm:ss.SSSXXX
org.slf4j.simpleLogger.showThreadName=true
org.slf4j.simpleLogger.showThreadName=false
org.slf4j.simpleLogger.showThreadId=false
org.slf4j.simpleLogger.showLogName=true
org.slf4j.simpleLogger.showLogName=false
org.slf4j.simpleLogger.showShortLogName=false
org.slf4j.simpleLogger.levelInBrackets=false
org.slf4j.simpleLogger.levelInBrackets=true
# Default log level for all loggers
# Must be one of "trace", "debug", "info", "warn", "error" or "off"

View File

@ -174,13 +174,13 @@ class BinaryDistributionIT extends AbstractBinaryDistributionTest {
result = PMDExecutor.runPMD(createTemporaryReportFile(), tempDir, "-d", srcDir, "-R", "src/test/resources/rulesets/sample-ruleset.xml");
result.assertExitCode(4);
result.assertErrorOutputContains("[main] INFO net.sourceforge.pmd.cli - Log level is at INFO");
result.assertNoErrorInReport("[DEBUG] Log level is at TRACE");
// now with debug
result = PMDExecutor.runPMD(createTemporaryReportFile(), tempDir, "-d", srcDir, "-R", "src/test/resources/rulesets/sample-ruleset.xml", "--debug");
result.assertExitCode(4);
result.assertErrorOutputContains("[main] INFO net.sourceforge.pmd.cli - Log level is at TRACE");
result.assertErrorOutputContains("[DEBUG] Log level is at TRACE");
}
@Test

View File

@ -5,11 +5,11 @@
org.slf4j.simpleLogger.logFile=System.err
org.slf4j.simpleLogger.showDateTime=false
org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd'T'HH:mm:ss.SSSXXX
org.slf4j.simpleLogger.showThreadName=true
org.slf4j.simpleLogger.showThreadName=false
org.slf4j.simpleLogger.showThreadId=false
org.slf4j.simpleLogger.showLogName=true
org.slf4j.simpleLogger.showLogName=false
org.slf4j.simpleLogger.showShortLogName=false
org.slf4j.simpleLogger.levelInBrackets=false
org.slf4j.simpleLogger.levelInBrackets=true
# Default log level for all loggers
# Must be one of "trace", "debug", "info", "warn", "error" or "off"