Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[cli] Add exit code for processing errors #4991

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
29 changes: 19 additions & 10 deletions docs/pages/pmd/userdocs/cli_reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ tags: [userdocs]
keywords: [command, line, options, help, formats, renderers]
permalink: pmd_userdocs_cli_reference.html
author: Tom Copeland <[email protected]>, Xavier Le Vourch <[email protected]>, Juan Martín Sotuyo Dodero <[email protected]>
last_updated: May 2024 (7.2.0)
---


Expand All @@ -19,7 +20,6 @@ The tool comes with a rather extensive help text, simply running with `--help`!
<th>Default value</th>
<th>Applies to</th>
</tr>

{% include custom/cli_option_row.html options="--rulesets,-R"
option_arg="refs"
description="Path to a ruleset xml file. The path may reference
Expand Down Expand Up @@ -55,7 +55,6 @@ The tool comes with a rather extensive help text, simply running with `--help`!
(\":\" on Linux, \";\" on Windows) is used to separate the entries.
Alternatively, a single `file:` URL
to a text file containing path elements on consecutive lines can be specified.

<p>See also [Providing the auxiliary classpath](pmd_languages_java.html#providing-the-auxiliary-classpath).</p>"
languages="Java"
%}
Expand All @@ -80,10 +79,15 @@ The tool comes with a rather extensive help text, simply running with `--help`!
The valid values are the standard character sets of `java.nio.charset.Charset`."
default="UTF-8"
%}
{% include custom/cli_option_row.html options="--[no-]fail-on-error"
description="Specifies whether PMD exits with non-zero status if recoverable errors occurred.
By default PMD exits with status 5 if recoverable errors occurred (whether there are violations or not).
Disable this option with `--no-fail-on-error` to exit with 0 instead. In any case, a report with the found violations will be written."
%}
{% include custom/cli_option_row.html options="--[no-]fail-on-violation"
description="Specifies whether PMD exits with non-zero status if violations are found.
By default PMD exits with status 4 if violations are found.
Disable this feature with `--no-fail-on-violation` to exit with 0 instead and just output the report."
Disable this feature with `--no-fail-on-violation` to exit with 0 instead. In any case a report with the found violations will be written."
%}
{% include custom/cli_option_row.html options="--file-list"
option_arg="filepath"
Expand All @@ -98,9 +102,7 @@ The tool comes with a rather extensive help text, simply running with `--help`!
by extension is disabled and PMD tries to parse all files with
the given language `&lt;lang&gt;`. Parsing errors are ignored and unparsable files
are skipped.

<p>Use `--use-version` to specify the language version to use, if it is not the default.</p>

<p>This option allows to use the xml language for files, that don't
use xml as extension. See [example](#analyze-other-xml-formats) below.</p>"
%}
Expand All @@ -125,7 +127,7 @@ The tool comes with a rather extensive help text, simply running with `--help`!
{% include custom/cli_option_row.html options="--minimum-priority"
option_arg="priority"
description="Rule priority threshold; rules with lower priority than configured here won't be used.
Valid values (case insensitive): High, Medium_High, Medium, Medium_Low, Low.
Valid values (case-insensitive): High, Medium_High, Medium, Medium_Low, Low.
An integer between 1 (High) and 5 (Low) is also supported. See [Configuring rules](pmd_userdocs_configuring_rules.html)
on how to override priorities in custom rulesets."
default="Low"
Expand All @@ -141,7 +143,7 @@ The tool comes with a rather extensive help text, simply running with `--help`!
description="Enables / disable progress bar indicator of live analysis progress. This ie enabled by default."
%}
{% include custom/cli_option_row.html options="--property,-P"
option_arg="name>=<value"
option_arg="name&gt;=&lt;value"
description="Specifies a property for the report renderer. The option can be specified several times.
<p>Using `--help` will provide a complete list of supported properties for each report format</p>"
%}
Expand Down Expand Up @@ -208,16 +210,23 @@ Or you can set the environment variable `CLASSPATH` before starting PMD, e.g.

## Exit Status

Please note that if PMD detects any violations, it will exit with status 4 (since 5.3).
Please note that if PMD detects any violations, it will exit with status 4 (since 5.3) or 5 (since 7.2.0).
This behavior has been introduced to ease PMD integration into scripts or hooks, such as SVN hooks.

<table>
<tr><td>0</td><td>Everything is fine, no violations found.</td></tr>
<tr><td>0</td><td>Everything is fine, no violations found and no recoverable error occurred.</td></tr>
<tr><td>1</td><td>PMD exited with an exception.</td></tr>
<tr><td>2</td><td>Usage error. Command-line parameters are invalid or missing.</td></tr>
<tr><td>4</td><td>At least one violation has been detected, unless <code>--no-fail-on-violation</code> is set.</td></tr>
<tr><td>4</td><td>At least one violation has been detected, unless <code>--no-fail-on-violation</code> is set.<p>Since PMD 5.3.</p></td></tr>
<tr><td>5</td><td>At least one recoverable error has occurred. There might be additionally zero or more violations detected.
To ignore recoverable errors, use <code>--no-fail-on-error</code>.<p>Since PMD 7.2.0.</p></td></tr>
</table>

{%include note.html content="If PMD exits with 5, then PMD had either trouble parsing one or more files or a rule failed with an exception.
That means, that either no violations for the entire file or for that rule are reported. These cases can be considered as false-negatives.
In any case, the root cause should be investigated. If it's a problem in PMD itself, please create a bug report. Recoverable errors
are usually part of the generated PMD report." %}

## Logging

PMD internally uses [slf4j](https://www.slf4j.org/) and ships with slf4j-simple as the logging implementation.
Expand Down
33 changes: 25 additions & 8 deletions docs/pages/pmd/userdocs/cpd/cpd.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ tags: [cpd, userdocs]
summary: "Learn how to use CPD, the copy-paste detector shipped with PMD."
permalink: pmd_userdocs_cpd.html
author: Tom Copeland <[email protected]>
last_updated: August 2023 (7.0.0)
last_updated: May 2024 (7.2.0)
---

## Overview
Expand Down Expand Up @@ -132,8 +132,8 @@ exactly identical.
description="Don't scan subdirectories. By default, subdirectories are considered."
%}
{% include custom/cli_option_row.html options="--skip-lexical-errors"
description="Skip files which can't be tokenized due to invalid characters instead of aborting CPD.
By default, CPD analysis is stopped on the first error."
description="<span class='label label-primary'>Deprecated</span> Skip files which can't be tokenized due to invalid characters instead of aborting CPD.
By default, CPD analysis is stopped on the first error. This is deprecated. Use `--fail-on-error` instead."
%}
{% include custom/cli_option_row.html options="--format,-f"
option_arg="format"
Expand All @@ -150,6 +150,11 @@ exactly identical.
If the root path is mentioned (e.g. \"/\" or \"C:\\\"), then the paths will be rendered
as absolute."
%}
{% include custom/cli_option_row.html options="--[no-]fail-on-error"
description="Specifies whether CPD exits with non-zero status if recoverable errors occurred.
By default CPD exits with status 5 if recoverable errors occurred (whether there are duplications or not).
Disable this option with `--no-fail-on-error` to exit with 0 instead. In any case, a report with the found duplications will be written."
%}
{% include custom/cli_option_row.html options="--[no-]fail-on-violation"
description="Specifies whether CPD exits with non-zero status if violations are found.
By default CPD exits with status 4 if violations are found.
Expand Down Expand Up @@ -279,16 +284,22 @@ If you specify a source directory but don't want to scan the sub-directories, yo

### Exit status

Please note that if CPD detects duplicated source code, it will exit with status 4 (since 5.0).
Please note that if CPD detects duplicated source code, it will exit with status 4 (since 5.0) or 5 (since 7.2.0).
This behavior has been introduced to ease CPD integration into scripts or hooks, such as SVN hooks.

<table>
<tr><td>0</td><td>Everything is fine, no code duplications found.</td></tr>
<tr><td>0</td><td>Everything is fine, no code duplications found and no recoverable errors occurred.</td></tr>
<tr><td>1</td><td>CPD exited with an exception.</td></tr>
<tr><td>2</td><td>Usage error. Command-line parameters are invalid or missing.</td></tr>
<tr><td>4</td><td>At least one code duplication has been detected unless <code>--no-fail-on-violation</code> is set.</td></tr>
<tr><td>4</td><td>At least one code duplication has been detected unless <code>--no-fail-on-violation</code> is set.<p>Since PMD 5.0.</p></td></tr>
<tr><td>5</td><td>At least one recoverable error has occurred. There might be additionally zero or more duplications detected.
To ignore recoverable errors, use <code>--no-fail-on-error</code>.<p>Since PMD 7.2.0.</p></td></tr>
</table>

{%include note.html content="If PMD exits with 5, then PMD had trouble lexing one or more files.
That means, that no duplications for the entire file are reported. This can be considered as false-negative.
In any case, the root cause should be investigated. If it's a problem in PMD itself, please create a bug report." %}

## Logging

PMD internally uses [slf4j](https://www.slf4j.org/) and ships with slf4j-simple as the logging implementation.
Expand Down Expand Up @@ -390,6 +401,10 @@ Andy Glover wrote an Ant task for CPD; here's how to use it:
keep their encoding.<br />
If not specified, CPD uses the system default encoding."
%}
{% include custom/cli_option_row.html options="failOnError"
description="Whether to fail the build if any errors occurred while processing the files. Since PMD 7.2.0."
default="true"
%}
{% include custom/cli_option_row.html options="format"
description="The format of the report (e.g. `csv`, `text`, `xml`)."
default="text"
Expand Down Expand Up @@ -424,8 +439,10 @@ Andy Glover wrote an Ant task for CPD; here's how to use it:
default="false"
%}
{% include custom/cli_option_row.html options="skipLexicalErrors"
description="Skip files which can't be tokenized due to invalid characters instead of aborting CPD."
default="false"
description="<span class='label label-primary'>Deprecated</span> Skip files which can't be tokenized
due to invalid characters instead of aborting CPD. This parameter is deprecated and
ignored since PMD 7.2.0. It is now by default true. Use `failOnError` instead to fail the build."
default="true"
%}
{% include custom/cli_option_row.html options="skipBlocks"
description="Enables or disabled skipping of blocks like a pre-processor. See also option skipBlocksPattern."
Expand Down
5 changes: 3 additions & 2 deletions docs/pages/pmd/userdocs/tools/ant.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ author: >
David Dixon-Peugh <[email protected]>,
Tom Copeland <[email protected]>,
Xavier Le Vourch <[email protected]>
last_updated: May 2024 (7.2.0)
---

## PMD
Expand Down Expand Up @@ -63,8 +64,8 @@ The examples below won't repeat this taskdef element, as this is always required
<td>Yes, unless the ruleset nested element is used</td>
</tr>
<tr>
<td>failonerror</td>
<td>Whether or not to fail the build if any errors occur while processing the files</td>
<td>failOnError</td>
<td>Whether or not to fail the build if any recoverable errors occurred while analyzing files.</td>
<td>No</td>
</tr>
<tr>
Expand Down
35 changes: 34 additions & 1 deletion docs/pages/release_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ Since this release, PMD will also expose any getter returning a collection of an
```

### 🐛 Fixed Issues
* cli
* [#2827](https://github.com/pmd/pmd/issues/2827): \[cli] Consider processing errors in exit status
* core
* [#4467](https://github.com/pmd/pmd/issues/4467): \[core] Expose collections from getters as XPath sequence attributes
* [#4978](https://github.com/pmd/pmd/issues/4978): \[core] Referenced Rulesets do not emit details on validation errors
Expand All @@ -49,10 +51,41 @@ Since this release, PMD will also expose any getter returning a collection of an

### 🚨 API Changes

#### CLI

* New exit code 5 introduced. PMD and CPD will exit now by default with exit code 5, if any recoverable error
(e.g. parsing exception, lexing exception or rule exception) occurred. PMD will still create a report with
all detected violations or duplications if recoverable errors occurred. Such errors mean, that the report
might be incomplete, as either violations or duplications for an entire file or for a specific rule are missing.
These cases can be considered as false-negatives.

In any case, the root cause should be investigated. If it's a problem in PMD itself, please create a bug report.

* New CLI parameter `--no-fail-on-error` to ignore such errors and not exit with code 5. By default,
a build with errors will now fail and with that parameter, the previous behavior can be restored.
This parameter is available for both PMD and CPD.

* The CLI parameter `--skip-lexical-errors` is deprecated. By default, lexical errors are skipped but the
build is failed. Use the new parameter `--[no-]fail-on-error` instead to control whether to fail the build or not.

##### Ant

* CPDTask has a new parameter `failOnError`. It controls, whether to fail the build if any recoverable error occurred.
By default, the build will fail. CPD will still create a report with all detected duplications, but the report might
be incomplete.
* The parameter `skipLexicalError` in CPDTask is deprecated and ignored. Lexical errors are now always skipped.
Use the new parameter `failOnError` instead to control whether to fail the build or not.

#### Deprecated API
adangel marked this conversation as resolved.
Show resolved Hide resolved

* pmd-ant
* {% jdoc !!ant::ant.CPDTask#setSkipLexicalErrors(boolean) %}: Use {% jdoc ant::ant.CPDTask#setFailOnError(boolean) %}
instead to control, whether to ignore errors or fail the build.
* pmd-core
* {% jdoc !!core::cpd.CPDConfiguration#isSkipLexicalErrors() %} and {% jdoc core::cpd.CPDConfiguration#setSkipLexicalErrors(boolean) %}:
Use {%jdoc core::AbstractConfiguration#setFailOnError(boolean) %} to control whether to ignore errors or fail the build.
* pmd-java
* {% jdoc !!java::lang.java.ast.ASTResource#getStableName() %} and the corresponding attribute `@StableName`
* {% jdoc !!java::lang.java.ast.ASTResource#getStableName() %} and the corresponding attribute `@StableName`.

### ✨ External Contributions

Expand Down
35 changes: 33 additions & 2 deletions pmd-ant/src/main/java/net/sourceforge/pmd/ant/CPDTask.java
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,15 @@ public class CPDTask extends Task {
private boolean ignoreIdentifiers;
private boolean ignoreAnnotations;
private boolean ignoreUsings;
@Deprecated
private boolean skipLexicalErrors;
private boolean skipDuplicateFiles;
private boolean skipBlocks = true;
private String skipBlocksPattern;
private File outputFile;
private String encoding = System.getProperty("file.encoding");
private List<FileSet> filesets = new ArrayList<>();
private boolean failOnError = true;

@Override
public void execute() throws BuildException {
Expand All @@ -99,7 +101,15 @@ public void execute() throws BuildException {
config.setOnlyRecognizeLanguage(config.getLanguageRegistry().getLanguageById(language));
config.setSourceEncoding(Charset.forName(encoding));
config.setSkipDuplicates(skipDuplicateFiles);
config.setSkipLexicalErrors(skipLexicalErrors);

if (skipLexicalErrors) {
log("skipLexicalErrors is deprecated since 7.2.0 and the property is ignored. "
+ "Lexical errors are now skipped by default and the build is failed. "
+ "Use failOnError=\"false\" to not fail the build.", Project.MSG_WARN);
}

// implicitly enable skipLexicalErrors, so that we can fail the build at the end. A report is created in any case.
config.setSkipLexicalErrors(true);

config.setIgnoreAnnotations(ignoreAnnotations);
config.setIgnoreLiterals(ignoreLiterals);
Expand All @@ -118,12 +128,20 @@ public void execute() throws BuildException {
long timeTaken = System.currentTimeMillis() - start;
log("Done analyzing code; that took " + timeTaken + " milliseconds");

int errors = config.getReporter().numErrors();
if (errors > 0) {
String message = String.format("There were %d recovered errors during analysis.", errors);
if (failOnError) {
throw new BuildException(message + " Ignore these with failOnError=\"true\".");
} else {
log(message + " Not failing build, because failOnError=\"false\".", Project.MSG_WARN);
}
}
}
} catch (IOException ioe) {
log(ioe.toString(), Project.MSG_ERR);
throw new BuildException("IOException during task execution", ioe);
} catch (ReportException re) {
re.printStackTrace();
log(re.toString(), Project.MSG_ERR);
throw new BuildException("ReportException during task execution", re);
} finally {
Expand Down Expand Up @@ -220,6 +238,10 @@ public void setIgnoreUsings(boolean value) {
this.ignoreUsings = value;
}

/**
* @deprecated Use {@link #setFailOnError(boolean)} instead.
*/
@Deprecated
public void setSkipLexicalErrors(boolean skipLexicalErrors) {
this.skipLexicalErrors = skipLexicalErrors;
}
Expand Down Expand Up @@ -252,6 +274,15 @@ public void setSkipBlocksPattern(String skipBlocksPattern) {
this.skipBlocksPattern = skipBlocksPattern;
}

/**
* Whether to fail the build if any recoverable errors occurred while processing the files.
*
* @since 7.2.0
*/
public void setFailOnError(boolean failOnError) {
this.failOnError = failOnError;
}

public static class FormatAttribute extends EnumeratedAttribute {
private static final String[] FORMATS = new String[] { XML_FORMAT, TEXT_FORMAT, CSV_FORMAT };

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ private void doTask() {
pmd.performAnalysis();
stats = reportStatsListener.getResult();
if (failOnError && pmd.getReporter().numErrors() > 0) {
throw new BuildException("Some errors occurred while running PMD");
throw new BuildException("Some recoverable errors occurred while running PMD");
}
}

Expand Down