Skip to content

Commit de783d7

Browse files
committed
pmd configuration
1 parent 8bc9720 commit de783d7

File tree

8 files changed

+316
-14
lines changed

8 files changed

+316
-14
lines changed

.codacy/codacy.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@ runtimes:
44
tools:
55
66
7-
87
8+

cmd/init.go

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,15 +55,22 @@ var initCmd = &cobra.Command{
5555
}
5656

5757
func createConfigurationFile(tools []tools.Tool) error {
58+
configPath := config.Config.ProjectConfigFile()
59+
fmt.Printf("Creating config file at: %s\n", configPath) // Debug log
5860

59-
configFile, err := os.Create(config.Config.ProjectConfigFile())
60-
defer configFile.Close()
61+
configFile, err := os.Create(configPath)
6162
if err != nil {
63+
fmt.Printf("Error creating file: %v\n", err) // Debug log
6264
log.Fatal(err)
6365
}
66+
defer configFile.Close()
6467

65-
_, err = configFile.WriteString(configFileTemplate(tools))
68+
content := configFileTemplate(tools)
69+
fmt.Printf("Writing config content:\n%s\n", content) // Debug log
70+
71+
_, err = configFile.WriteString(content)
6672
if err != nil {
73+
fmt.Printf("Error writing content: %v\n", err) // Debug log
6774
log.Fatal(err)
6875
}
6976

@@ -76,6 +83,7 @@ func configFileTemplate(tools []tools.Tool) string {
7683
eslintVersion := "9.3.0"
7784
trivyVersion := "0.59.1" // Latest stable version
7885
pylintVersion := "3.3.6"
86+
pmdVersion := "7.12.0"
7987

8088
for _, tool := range tools {
8189
if tool.Uuid == "f8b29663-2cb2-498d-b923-a10c6a8c05cd" {
@@ -87,16 +95,21 @@ func configFileTemplate(tools []tools.Tool) string {
8795
if tool.Uuid == "31677b6d-4ae0-4f56-8041-606a8d7a8e61" {
8896
pylintVersion = tool.Version
8997
}
98+
if tool.Uuid == "9ed24812-b6ee-4a58-9004-0ed183c45b8f" {
99+
pmdVersion = tool.Version
100+
}
90101
}
91102

103+
//todo correct java version
92104
return fmt.Sprintf(`runtimes:
93105
94106
95107
tools:
96108
- eslint@%s
97109
- trivy@%s
98110
- pylint@%s
99-
`, eslintVersion, trivyVersion, pylintVersion)
111+
- pmd@%s
112+
`, eslintVersion, trivyVersion, pylintVersion, pmdVersion)
100113
}
101114

102115
func buildRepositoryConfigurationFiles(token string) error {
@@ -181,6 +194,22 @@ func buildRepositoryConfigurationFiles(token string) error {
181194
fmt.Println("Default Trivy configuration created")
182195
}
183196

197+
// Add PMD configuration handling
198+
pmdApiConfiguration := extractPMDConfiguration(apiToolConfigurations)
199+
if pmdApiConfiguration != nil {
200+
err = createPMDConfigFile(*pmdApiConfiguration)
201+
if err != nil {
202+
log.Fatal(err)
203+
}
204+
fmt.Println("PMD configuration created based on Codacy settings")
205+
} else {
206+
err = createDefaultPMDConfigFile()
207+
if err != nil {
208+
log.Fatal(err)
209+
}
210+
fmt.Println("Default PMD configuration created")
211+
}
212+
184213
return nil
185214
}
186215

@@ -239,6 +268,29 @@ func extractTrivyConfiguration(toolConfigurations []CodacyToolConfiguration) *Co
239268
return nil
240269
}
241270

271+
// Add PMD-specific functions
272+
func extractPMDConfiguration(toolConfigurations []CodacyToolConfiguration) *CodacyToolConfiguration {
273+
const PMDUUID = "9ed24812-b6ee-4a58-9004-0ed183c45b8f"
274+
for _, toolConfiguration := range toolConfigurations {
275+
if toolConfiguration.Uuid == PMDUUID {
276+
return &toolConfiguration
277+
}
278+
}
279+
return nil
280+
}
281+
282+
func createPMDConfigFile(config CodacyToolConfiguration) error {
283+
pmdDomainConfiguration := convertAPIToolConfigurationToDomain(config)
284+
pmdConfigurationString := tools.CreatePmdConfig(pmdDomainConfiguration)
285+
return os.WriteFile("pmd-ruleset.xml", []byte(pmdConfigurationString), 0644)
286+
}
287+
288+
func createDefaultPMDConfigFile() error {
289+
emptyConfig := tools.ToolConfiguration{}
290+
content := tools.CreatePmdConfig(emptyConfig)
291+
return os.WriteFile("pmd-ruleset.xml", []byte(content), 0644)
292+
}
293+
242294
type CodacyToolConfiguration struct {
243295
Uuid string `json:"uuid"`
244296
IsEnabled bool `json:"isEnabled"`

plugins/tools/pmd/default-ruleset.xml

Whitespace-only changes.

plugins/tools/pmd/plugin.yaml

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,17 @@ download:
77
default: .zip
88
binaries:
99
- name: pmd
10-
path: pmd-bin-{{.Version}}/bin/pmd
10+
path: pmd-bin-{{.Version}}/bin/pmd
11+
runtime: java
12+
runtime_binaries:
13+
package_manager: java
14+
execution: java
15+
default_rules:
16+
- category: java/codestyle
17+
rule: AtLeastOneConstructor
18+
- category: java/design
19+
rule: UnusedPrivateField
20+
- category: java/codestyle
21+
rule: ShortVariable
22+
- category: java/documentation
23+
rule: CommentRequired

tools/pmdConfigCreator.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package tools
2+
3+
import (
4+
"fmt"
5+
"log"
6+
"os"
7+
"path/filepath"
8+
"strings"
9+
)
10+
11+
func CreatePmdConfig(configuration ToolConfiguration) string {
12+
// If no patterns provided, return the default ruleset from file
13+
if len(configuration.PatternsConfiguration) == 0 {
14+
defaultRulesetPath := filepath.Join("plugins", "tools", "pmd", "default-ruleset.xml")
15+
defaultRuleset, err := os.ReadFile(defaultRulesetPath)
16+
if err != nil {
17+
log.Printf("Failed to read default PMD ruleset: %v", err)
18+
return "" // Or handle error as needed
19+
}
20+
return string(defaultRuleset)
21+
}
22+
23+
var contentBuilder strings.Builder
24+
25+
// Write XML header and ruleset opening with correct name
26+
contentBuilder.WriteString("<?xml version=\"1.0\"?>\n")
27+
contentBuilder.WriteString("<ruleset name=\"Codacy PMD Ruleset\"\n")
28+
contentBuilder.WriteString(" xmlns=\"http://pmd.sourceforge.net/ruleset/2.0.0\"\n")
29+
contentBuilder.WriteString(" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n")
30+
contentBuilder.WriteString(" xsi:schemaLocation=\"http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd\">\n\n")
31+
32+
// Process patterns from configuration
33+
for _, pattern := range configuration.PatternsConfiguration {
34+
// Check if pattern is enabled
35+
patternEnabled := true
36+
var properties []string
37+
38+
for _, param := range pattern.ParamenterConfigurations {
39+
if param.Name == "enabled" && param.Value == "false" {
40+
patternEnabled = false
41+
break
42+
} else if param.Name != "enabled" {
43+
// Store non-enabled parameters for properties
44+
properties = append(properties, fmt.Sprintf(" <property name=\"%s\" value=\"%s\"/>", param.Name, param.Value))
45+
}
46+
}
47+
48+
if patternEnabled {
49+
// Convert pattern ID to correct PMD path format
50+
// e.g., "java/codestyle/AtLeastOneConstructor" -> "category/java/codestyle.xml/AtLeastOneConstructor"
51+
parts := strings.Split(pattern.PatternId, "/")
52+
if len(parts) >= 2 {
53+
category := parts[len(parts)-2]
54+
rule := parts[len(parts)-1]
55+
contentBuilder.WriteString(fmt.Sprintf(" <rule ref=\"category/java/%s.xml/%s\"", category, rule))
56+
57+
// Add properties if any exist
58+
if len(properties) > 0 {
59+
contentBuilder.WriteString(">\n <properties>\n")
60+
for _, prop := range properties {
61+
contentBuilder.WriteString(prop + "\n")
62+
}
63+
contentBuilder.WriteString(" </properties>\n </rule>\n")
64+
} else {
65+
contentBuilder.WriteString("/>\n")
66+
}
67+
}
68+
}
69+
}
70+
71+
contentBuilder.WriteString("</ruleset>")
72+
return contentBuilder.String()
73+
}

tools/pmdConfigCreator_test.go

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
package tools
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
"strings"
7+
"testing"
8+
9+
"github.com/stretchr/testify/assert"
10+
)
11+
12+
func TestCreatePmdConfig(t *testing.T) {
13+
// Setup test configuration with patterns
14+
config := ToolConfiguration{
15+
PatternsConfiguration: []PatternConfiguration{
16+
{
17+
PatternId: "java/codestyle/AtLeastOneConstructor",
18+
ParamenterConfigurations: []PatternParameterConfiguration{
19+
{
20+
Name: "enabled",
21+
Value: "true",
22+
},
23+
},
24+
},
25+
{
26+
PatternId: "java/design/UnusedPrivateField",
27+
ParamenterConfigurations: []PatternParameterConfiguration{
28+
{
29+
Name: "enabled",
30+
Value: "true",
31+
},
32+
},
33+
},
34+
{
35+
PatternId: "java/codestyle/ShortVariable",
36+
ParamenterConfigurations: []PatternParameterConfiguration{
37+
{
38+
Name: "enabled",
39+
Value: "true",
40+
},
41+
},
42+
},
43+
{
44+
PatternId: "java/documentation/CommentRequired",
45+
ParamenterConfigurations: []PatternParameterConfiguration{
46+
{
47+
Name: "enabled",
48+
Value: "true",
49+
},
50+
},
51+
},
52+
{
53+
PatternId: "java/design/LoosePackageCoupling",
54+
ParamenterConfigurations: []PatternParameterConfiguration{
55+
{
56+
Name: "enabled",
57+
Value: "true",
58+
},
59+
{
60+
Name: "packages",
61+
Value: "java.util,java.io",
62+
},
63+
},
64+
},
65+
},
66+
}
67+
68+
// Generate PMD config
69+
pmdConfigurationString := CreatePmdConfig(config)
70+
71+
// Compare with expected config from test file
72+
expectedConfigFile := filepath.Join("testdata", "repositories", "pmd", "expected-ruleset.xml")
73+
expectedConfigBytes, err := os.ReadFile(expectedConfigFile)
74+
if err != nil {
75+
t.Fatalf("Failed to read expected config file: %v", err)
76+
}
77+
expectedConfig := strings.TrimSpace(string(expectedConfigBytes))
78+
79+
assert.Equal(t, expectedConfig, pmdConfigurationString)
80+
}
81+
82+
func TestCreatePmdConfigWithDisabledRules(t *testing.T) {
83+
// Test configuration with disabled rules
84+
config := ToolConfiguration{
85+
PatternsConfiguration: []PatternConfiguration{
86+
{
87+
PatternId: "java/codestyle/AtLeastOneConstructor",
88+
ParamenterConfigurations: []PatternParameterConfiguration{
89+
{
90+
Name: "enabled",
91+
Value: "false",
92+
},
93+
},
94+
},
95+
},
96+
}
97+
98+
obtainedConfig := CreatePmdConfig(config)
99+
100+
// Verify disabled rules are not included
101+
assert.NotContains(t, obtainedConfig, "AtLeastOneConstructor",
102+
"Disabled rule should not appear in config")
103+
}
104+
105+
func TestCreatePmdConfigEmpty(t *testing.T) {
106+
// Save current working directory
107+
cwd, err := os.Getwd()
108+
if err != nil {
109+
t.Fatalf("Failed to get current directory: %v", err)
110+
}
111+
112+
// Create a temporary test directory structure
113+
tmpDir := t.TempDir()
114+
err = os.MkdirAll(filepath.Join(tmpDir, "plugins", "tools", "pmd"), 0755)
115+
if err != nil {
116+
t.Fatalf("Failed to create test directory structure: %v", err)
117+
}
118+
119+
// Copy the default ruleset to the test location
120+
defaultRuleset := `<?xml version="1.0"?>
121+
<ruleset name="Codacy PMD Ruleset"
122+
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
123+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
124+
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd">
125+
<!-- Default rules here -->
126+
</ruleset>`
127+
128+
err = os.WriteFile(filepath.Join(tmpDir, "plugins", "tools", "pmd", "default-ruleset.xml"), []byte(defaultRuleset), 0644)
129+
if err != nil {
130+
t.Fatalf("Failed to write test ruleset: %v", err)
131+
}
132+
133+
// Change to temp directory for test
134+
err = os.Chdir(tmpDir)
135+
if err != nil {
136+
t.Fatalf("Failed to change directory: %v", err)
137+
}
138+
// Restore working directory after test
139+
defer os.Chdir(cwd)
140+
141+
// Run the test
142+
config := ToolConfiguration{
143+
PatternsConfiguration: []PatternConfiguration{},
144+
}
145+
146+
obtainedConfig := CreatePmdConfig(config)
147+
148+
// Verify the output contains basic XML structure
149+
assert.Contains(t, obtainedConfig, "<?xml version=\"1.0\"?>")
150+
assert.Contains(t, obtainedConfig, "<ruleset")
151+
assert.Contains(t, obtainedConfig, "</ruleset>")
152+
}

0 commit comments

Comments
 (0)