Skip to content

Commit bf83373

Browse files
feat: pmd running
1 parent b0f7eee commit bf83373

File tree

7 files changed

+295
-7
lines changed

7 files changed

+295
-7
lines changed

.codacy/codacy.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
runtimes:
22
33
tools:
4-
- eslint@9.3.0
4+
- eslint@8.57.0
55
66

cmd/analyze.go

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"log"
1010
"net/http"
1111
"os"
12+
"os/exec"
1213

1314
"github.com/spf13/cobra"
1415
)
@@ -20,6 +21,7 @@ var outputFormat string
2021
var sarifPath string
2122
var commitUuid string
2223
var projectToken string
24+
var pmdRulesetFile string
2325

2426
type Sarif struct {
2527
Runs []struct {
@@ -95,6 +97,7 @@ func init() {
9597
analyzeCmd.Flags().StringVarP(&toolToAnalyze, "tool", "t", "", "Which tool to run analysis with")
9698
analyzeCmd.Flags().StringVar(&outputFormat, "format", "", "Output format (use 'sarif' for SARIF format)")
9799
analyzeCmd.Flags().BoolVar(&autoFix, "fix", false, "Apply auto fix to your issues when available")
100+
analyzeCmd.Flags().StringVar(&pmdRulesetFile, "rulesets", "", "Path to PMD ruleset file")
98101
rootCmd.AddCommand(analyzeCmd)
99102
}
100103

@@ -207,7 +210,32 @@ func runPmdAnalysis(workDirectory string, pathsToCheck []string, outputFile stri
207210
pmd := config.Config.Tools()["pmd"]
208211
pmdBinary := pmd.Binaries["pmd"]
209212

210-
err := tools.RunPmd(workDirectory, pmdBinary, pathsToCheck, outputFile, outputFormat)
213+
// Build PMD command arguments
214+
args := []string{"check"}
215+
216+
// Add ruleset file if provided
217+
if pmdRulesetFile != "" {
218+
args = append(args, "--rulesets", pmdRulesetFile)
219+
}
220+
221+
// Add format options
222+
if outputFormat == "sarif" {
223+
args = append(args, "--format", "sarif")
224+
}
225+
226+
if outputFile != "" {
227+
args = append(args, "--report-file", outputFile)
228+
}
229+
230+
// Add directory to scan
231+
args = append(args, "--dir", workDirectory)
232+
233+
cmd := exec.Command(pmdBinary, args...)
234+
cmd.Dir = workDirectory
235+
cmd.Stderr = os.Stderr
236+
cmd.Stdout = os.Stdout
237+
238+
err := cmd.Run()
211239
if err != nil {
212240
log.Fatalf("Error running PMD: %v", err)
213241
}

tools/pmdRunner.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,14 @@ import (
66
)
77

88
// RunPmd executes PMD static code analyzer with the specified options
9-
func RunPmd(repositoryToAnalyseDirectory string, pmdBinary string, pathsToCheck []string, outputFile string, outputFormat string) error {
9+
func RunPmd(repositoryToAnalyseDirectory string, pmdBinary string, pathsToCheck []string, outputFile string, outputFormat string, rulesetFile string) error {
1010
cmd := exec.Command(pmdBinary, "check")
1111

12+
// Add ruleset file if provided
13+
if rulesetFile != "" {
14+
cmd.Args = append(cmd.Args, "--rulesets", rulesetFile)
15+
}
16+
1217
// Add format options
1318
if outputFormat == "sarif" {
1419
cmd.Args = append(cmd.Args, "--format", "sarif")

tools/pmdRunner_test.go

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
"log"
66
"os"
7+
"os/exec"
78
"path/filepath"
89
"strings"
910
"testing"
@@ -21,17 +22,23 @@ func TestRunPmdToFile(t *testing.T) {
2122
log.Fatal(err.Error())
2223
}
2324

24-
testDirectory := "testdata/repositories/pmd"
25+
// Get absolute paths for test files
26+
testDirectory := filepath.Join(currentDirectory, "testdata/repositories/pmd")
2527
tempResultFile := filepath.Join(os.TempDir(), "pmd.sarif")
2628
defer os.Remove(tempResultFile)
2729

28-
repositoryToAnalyze := filepath.Join(testDirectory, "src")
30+
// Use absolute paths for repository and ruleset
31+
repositoryToAnalyze := testDirectory
32+
rulesetFile := filepath.Join(testDirectory, "pmd-ruleset.xml")
2933

3034
pmdBinary := filepath.Join(homeDirectory, ".cache/codacy/tools/[email protected]/pmd-bin-7.12.0/bin/pmd")
3135

32-
err = RunPmd(repositoryToAnalyze, pmdBinary, nil, tempResultFile, "sarif")
36+
err = RunPmd(repositoryToAnalyze, pmdBinary, nil, tempResultFile, "sarif", rulesetFile)
37+
// PMD returns exit status 4 when violations are found, which is expected in our test
3338
if err != nil {
34-
t.Fatalf("Failed to run PMD: %v", err)
39+
if exitErr, ok := err.(*exec.ExitError); ok && exitErr.ExitCode() != 4 {
40+
t.Fatalf("Failed to run PMD: %v", err)
41+
}
3542
}
3643

3744
// Check if the output file was created
@@ -43,6 +50,7 @@ func TestRunPmdToFile(t *testing.T) {
4350
filePrefix := "file://" + currentDirectory + "/"
4451
fmt.Println(filePrefix)
4552
actualSarif := strings.ReplaceAll(obtainedSarif, filePrefix, "")
53+
actualSarif = strings.TrimSpace(actualSarif)
4654

4755
// Read the expected SARIF
4856
expectedSarifFile := filepath.Join(testDirectory, "expected.sarif")
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
2+
/**
3+
* This class demonstrates various PMD rule violations.
4+
* It is used for testing PMD's rule detection capabilities.
5+
*
6+
* @author Codacy Test Team
7+
* @version 1.0
8+
*/
9+
10+
package com.codacy.test;
11+
12+
/**
13+
* A class that demonstrates PMD rule violations.
14+
*/
15+
public class RulesBreaker {
16+
17+
// Breaking naming convention rules
18+
private int x;
19+
20+
}
Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
{
2+
"$schema": "https://json.schemastore.org/sarif-2.1.0.json",
3+
"version": "2.1.0",
4+
"runs": [
5+
{
6+
"tool": {
7+
"driver": {
8+
"name": "PMD",
9+
"version": "7.12.0",
10+
"informationUri": "https://docs.pmd-code.org/latest/",
11+
"rules": [
12+
{
13+
"id": "AtLeastOneConstructor",
14+
"shortDescription": {
15+
"text": "Each class should declare at least one constructor"
16+
},
17+
"fullDescription": {
18+
"text": "\n\nEach non-static class should declare at least one constructor.\nClasses with solely static members are ignored, refer to [UseUtilityClassRule](pmd_rules_java_design.html#useutilityclass) to detect those.\n\n "
19+
},
20+
"helpUri": "https://docs.pmd-code.org/pmd-doc-7.12.0/pmd_rules_java_codestyle.html#atleastoneconstructor",
21+
"help": {
22+
"text": "\n\nEach non-static class should declare at least one constructor.\nClasses with solely static members are ignored, refer to [UseUtilityClassRule](pmd_rules_java_design.html#useutilityclass) to detect those.\n\n "
23+
},
24+
"properties": {
25+
"ruleset": "Code Style",
26+
"priority": 3,
27+
"tags": [
28+
"Code Style"
29+
]
30+
}
31+
},
32+
{
33+
"id": "UnusedPrivateField",
34+
"shortDescription": {
35+
"text": "Avoid unused private fields such as 'x'."
36+
},
37+
"fullDescription": {
38+
"text": "\nDetects when a private field is declared and/or assigned a value, but not used.\n\nSince PMD 6.50.0 private fields are ignored, if the fields are annotated with any annotation or the\nenclosing class has any annotation. Annotations often enable a framework (such as dependency injection, mocking\nor e.g. Lombok) which use the fields by reflection or other means. This usage can't be detected by static code analysis.\nPreviously these frameworks where explicitly allowed by listing their annotations in the property\n\"ignoredAnnotations\", but that turned out to be prone of false positive for any not explicitly considered framework.\n "
39+
},
40+
"helpUri": "https://docs.pmd-code.org/pmd-doc-7.12.0/pmd_rules_java_bestpractices.html#unusedprivatefield",
41+
"help": {
42+
"text": "\nDetects when a private field is declared and/or assigned a value, but not used.\n\nSince PMD 6.50.0 private fields are ignored, if the fields are annotated with any annotation or the\nenclosing class has any annotation. Annotations often enable a framework (such as dependency injection, mocking\nor e.g. Lombok) which use the fields by reflection or other means. This usage can't be detected by static code analysis.\nPreviously these frameworks where explicitly allowed by listing their annotations in the property\n\"ignoredAnnotations\", but that turned out to be prone of false positive for any not explicitly considered framework.\n "
43+
},
44+
"properties": {
45+
"ruleset": "Best Practices",
46+
"priority": 3,
47+
"tags": [
48+
"Best Practices"
49+
]
50+
}
51+
},
52+
{
53+
"id": "ShortVariable",
54+
"shortDescription": {
55+
"text": "Avoid variables with short names like x"
56+
},
57+
"fullDescription": {
58+
"text": "\nFields, local variables, enum constant names or parameter names that are very short are not helpful to the reader.\n "
59+
},
60+
"helpUri": "https://docs.pmd-code.org/pmd-doc-7.12.0/pmd_rules_java_codestyle.html#shortvariable",
61+
"help": {
62+
"text": "\nFields, local variables, enum constant names or parameter names that are very short are not helpful to the reader.\n "
63+
},
64+
"properties": {
65+
"ruleset": "Code Style",
66+
"priority": 3,
67+
"tags": [
68+
"Code Style"
69+
]
70+
}
71+
},
72+
{
73+
"id": "CommentRequired",
74+
"shortDescription": {
75+
"text": "Field comments are required"
76+
},
77+
"fullDescription": {
78+
"text": "\nDenotes whether javadoc (formal) comments are required (or unwanted) for specific language elements.\n "
79+
},
80+
"helpUri": "https://docs.pmd-code.org/pmd-doc-7.12.0/pmd_rules_java_documentation.html#commentrequired",
81+
"help": {
82+
"text": "\nDenotes whether javadoc (formal) comments are required (or unwanted) for specific language elements.\n "
83+
},
84+
"properties": {
85+
"ruleset": "Documentation",
86+
"priority": 3,
87+
"tags": [
88+
"Documentation"
89+
]
90+
}
91+
}
92+
]
93+
}
94+
},
95+
"results": [
96+
{
97+
"ruleId": "AtLeastOneConstructor",
98+
"ruleIndex": 0,
99+
"message": {
100+
"text": "Each class should declare at least one constructor"
101+
},
102+
"locations": [
103+
{
104+
"physicalLocation": {
105+
"artifactLocation": {
106+
"uri": "testdata/repositories/pmd/RulesBreaker.java"
107+
},
108+
"region": {
109+
"startLine": 15,
110+
"startColumn": 8,
111+
"endLine": 15,
112+
"endColumn": 13
113+
}
114+
}
115+
}
116+
]
117+
},
118+
{
119+
"ruleId": "UnusedPrivateField",
120+
"ruleIndex": 1,
121+
"message": {
122+
"text": "Avoid unused private fields such as 'x'."
123+
},
124+
"locations": [
125+
{
126+
"physicalLocation": {
127+
"artifactLocation": {
128+
"uri": "testdata/repositories/pmd/RulesBreaker.java"
129+
},
130+
"region": {
131+
"startLine": 18,
132+
"startColumn": 17,
133+
"endLine": 18,
134+
"endColumn": 18
135+
}
136+
}
137+
}
138+
]
139+
},
140+
{
141+
"ruleId": "ShortVariable",
142+
"ruleIndex": 2,
143+
"message": {
144+
"text": "Avoid variables with short names like x"
145+
},
146+
"locations": [
147+
{
148+
"physicalLocation": {
149+
"artifactLocation": {
150+
"uri": "testdata/repositories/pmd/RulesBreaker.java"
151+
},
152+
"region": {
153+
"startLine": 18,
154+
"startColumn": 17,
155+
"endLine": 18,
156+
"endColumn": 18
157+
}
158+
}
159+
}
160+
]
161+
},
162+
{
163+
"ruleId": "CommentRequired",
164+
"ruleIndex": 3,
165+
"message": {
166+
"text": "Field comments are required"
167+
},
168+
"locations": [
169+
{
170+
"physicalLocation": {
171+
"artifactLocation": {
172+
"uri": "testdata/repositories/pmd/RulesBreaker.java"
173+
},
174+
"region": {
175+
"startLine": 18,
176+
"startColumn": 17,
177+
"endLine": 18,
178+
"endColumn": 18
179+
}
180+
}
181+
}
182+
]
183+
}
184+
],
185+
"invocations": [
186+
{
187+
"executionSuccessful": false,
188+
"toolConfigurationNotifications": [
189+
{
190+
"associatedRule": {
191+
"id": "LoosePackageCoupling"
192+
},
193+
"message": {
194+
"text": "No packages or classes specified"
195+
}
196+
}
197+
],
198+
"toolExecutionNotifications": []
199+
}
200+
]
201+
}
202+
]
203+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?xml version="1.0"?>
2+
<ruleset name="Codacy PMD Ruleset"
3+
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_xml_schema.xsd"
6+
xsi:noNamespaceSchemaLocation="https://pmd.sourceforge.io/ruleset_xml_schema.xsd">
7+
8+
<description>
9+
Basic PMD ruleset for Codacy analysis
10+
</description>
11+
12+
<!-- Import basic rules -->
13+
<rule ref="category/java/bestpractices.xml">
14+
<exclude name="GuardLogStatement"/>
15+
</rule>
16+
<rule ref="category/java/codestyle.xml"/>
17+
<rule ref="category/java/design.xml"/>
18+
<rule ref="category/java/documentation.xml"/>
19+
<rule ref="category/java/errorprone.xml"/>
20+
<rule ref="category/java/multithreading.xml"/>
21+
<rule ref="category/java/performance.xml"/>
22+
<rule ref="category/java/security.xml"/>
23+
24+
</ruleset>

0 commit comments

Comments
 (0)