From 6056a9a2da85e9a868de3f9ce287598f78b7f5f1 Mon Sep 17 00:00:00 2001 From: franciscoazevedo Date: Tue, 20 May 2025 16:37:50 +0100 Subject: [PATCH 1/2] WIP - uniformize init for all tools --- cmd/init.go | 165 ++++++---------------------- tools/eslintConfigCreator.go | 16 ++- tools/eslintConfigCreator_test.go | 20 +++- tools/lizard/lizardConfigCreator.go | 8 +- 4 files changed, 72 insertions(+), 137 deletions(-) diff --git a/cmd/init.go b/cmd/init.go index cbf2c29e..1d5a1939 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -299,86 +299,47 @@ func createToolFileConfigurations(tool domain.Tool, patternConfiguration []domai toolsConfigDir := config.Config.ToolsConfigDirectory() switch tool.Uuid { case ESLint: - if len(patternConfiguration) > 0 { - eslintConfigurationString := tools.CreateEslintConfig(patternConfiguration) - - eslintConfigFile, err := os.Create(filepath.Join(toolsConfigDir, "eslint.config.mjs")) - if err != nil { - return fmt.Errorf("failed to create eslint config file: %v", err) - } - defer eslintConfigFile.Close() - - _, err = eslintConfigFile.WriteString(eslintConfigurationString) - if err != nil { - return fmt.Errorf("failed to write eslint config: %v", err) - } - fmt.Println("ESLint configuration created based on Codacy settings. Ignoring plugin rules. ESLint plugins are not supported yet.") - } else { - err := createDefaultEslintConfigFile(toolsConfigDir) - if err != nil { - return fmt.Errorf("failed to create default ESLint config: %v", err) - } - fmt.Println("Default ESLint configuration created") + err := tools.CreateEslintConfig(toolsConfigDir, patternConfiguration) + if err != nil { + return fmt.Errorf("failed to write eslint config: %v", err) } + fmt.Println("ESLint configuration created based on Codacy settings. Ignoring plugin rules. ESLint plugins are not supported yet.") case Trivy: - if len(patternConfiguration) > 0 { - err := createTrivyConfigFile(patternConfiguration, toolsConfigDir) - if err != nil { - return fmt.Errorf("failed to create Trivy config: %v", err) - } - fmt.Println("Trivy configuration created based on Codacy settings") - } else { - err := createDefaultTrivyConfigFile(toolsConfigDir) - if err != nil { - return fmt.Errorf("failed to create default Trivy config: %v", err) - } + err := createTrivyConfigFile(patternConfiguration, toolsConfigDir) + if err != nil { + return fmt.Errorf("failed to create Trivy config: %v", err) } + fmt.Println("Trivy configuration created based on Codacy settings") case PMD: - if len(patternConfiguration) > 0 { - err := createPMDConfigFile(patternConfiguration, toolsConfigDir) - if err != nil { - return fmt.Errorf("failed to create PMD config: %v", err) - } - - fmt.Println("PMD configuration created based on Codacy settings") - } else { - err := createDefaultPMDConfigFile(toolsConfigDir) - if err != nil { - return fmt.Errorf("failed to create default PMD config: %v", err) - } + err := createPMDConfigFile(patternConfiguration, toolsConfigDir) + if err != nil { + return fmt.Errorf("failed to create PMD config: %v", err) } - + fmt.Println("PMD configuration created based on Codacy settings") case PyLint: - if len(patternConfiguration) > 0 { - err := createPylintConfigFile(patternConfiguration, toolsConfigDir) - if err != nil { - return fmt.Errorf("failed to create Pylint config: %v", err) - } - fmt.Println("Pylint configuration created based on Codacy settings") - } else { - err := createDefaultPylintConfigFile(toolsConfigDir) - if err != nil { - return fmt.Errorf("failed to create default Pylint config: %v", err) - } + err := createPylintConfigFile(patternConfiguration, toolsConfigDir) + if err != nil { + return fmt.Errorf("failed to create Pylint config: %v", err) } + fmt.Println("Pylint configuration created based on Codacy settings") case DartAnalyzer: - if len(patternConfiguration) > 0 { - err := createDartAnalyzerConfigFile(patternConfiguration, toolsConfigDir) - if err != nil { - return fmt.Errorf("failed to create Dart Analyzer config: %v", err) - } - fmt.Println("Dart configuration created based on Codacy settings") + err := createDartAnalyzerConfigFile(patternConfiguration, toolsConfigDir) + if err != nil { + return fmt.Errorf("failed to create Dart Analyzer config: %v", err) } + fmt.Println("Dart configuration created based on Codacy settings") case Semgrep: - if len(patternConfiguration) > 0 { - err := createSemgrepConfigFile(patternConfiguration, toolsConfigDir) - if err != nil { - return fmt.Errorf("failed to create Semgrep config: %v", err) - } - fmt.Println("Semgrep configuration created based on Codacy settings") + err := createSemgrepConfigFile(patternConfiguration, toolsConfigDir) + if err != nil { + return fmt.Errorf("failed to create Semgrep config: %v", err) } + fmt.Println("Semgrep configuration created based on Codacy settings") case Lizard: - createLizardConfigFile(toolsConfigDir, patternConfiguration) + err := createLizardConfigFile(toolsConfigDir, patternConfiguration) + if err != nil { + return fmt.Errorf("failed to create Lizard config: %v", err) + } + fmt.Println("Lizard configuration created based on Codacy settings") } return nil } @@ -388,21 +349,11 @@ func createPMDConfigFile(config []domain.PatternConfiguration, toolsConfigDir st return os.WriteFile(filepath.Join(toolsConfigDir, "ruleset.xml"), []byte(pmdConfigurationString), utils.DefaultFilePerms) } -func createDefaultPMDConfigFile(toolsConfigDir string) error { - content := tools.CreatePmdConfig([]domain.PatternConfiguration{}) - return os.WriteFile(filepath.Join(toolsConfigDir, "ruleset.xml"), []byte(content), utils.DefaultFilePerms) -} - func createPylintConfigFile(config []domain.PatternConfiguration, toolsConfigDir string) error { pylintConfigurationString := pylint.GeneratePylintRC(config) return os.WriteFile(filepath.Join(toolsConfigDir, "pylint.rc"), []byte(pylintConfigurationString), utils.DefaultFilePerms) } -func createDefaultPylintConfigFile(toolsConfigDir string) error { - pylintConfigurationString := pylint.GeneratePylintRCDefault() - return os.WriteFile(filepath.Join(toolsConfigDir, "pylint.rc"), []byte(pylintConfigurationString), utils.DefaultFilePerms) -} - // createTrivyConfigFile creates a trivy.yaml configuration file based on the API configuration func createTrivyConfigFile(config []domain.PatternConfiguration, toolsConfigDir string) error { @@ -418,27 +369,6 @@ func createDartAnalyzerConfigFile(config []domain.PatternConfiguration, toolsCon return os.WriteFile(filepath.Join(toolsConfigDir, "analysis_options.yaml"), []byte(dartAnalyzerConfigurationString), utils.DefaultFilePerms) } -// createDefaultTrivyConfigFile creates a default trivy.yaml configuration file -func createDefaultTrivyConfigFile(toolsConfigDir string) error { - // Use empty tool configuration to get default settings - emptyConfig := []domain.PatternConfiguration{} - content := tools.CreateTrivyConfig(emptyConfig) - - // Write to file - return os.WriteFile(filepath.Join(toolsConfigDir, "trivy.yaml"), []byte(content), utils.DefaultFilePerms) -} - -// createDefaultEslintConfigFile creates a default eslint.config.mjs configuration file -func createDefaultEslintConfigFile(toolsConfigDir string) error { - // Use empty tool configuration to get default settings - // - emptyConfig := []domain.PatternConfiguration{} - content := tools.CreateEslintConfig(emptyConfig) - - // Write to file - return os.WriteFile(filepath.Join(toolsConfigDir, "eslint.config.mjs"), []byte(content), utils.DefaultFilePerms) -} - // SemgrepRulesFile represents the structure of the rules.yaml file type SemgrepRulesFile struct { Rules []map[string]interface{} `yaml:"rules"` @@ -485,35 +415,20 @@ func cleanConfigDirectory(toolsConfigDir string) error { } func createLizardConfigFile(toolsConfigDir string, patternConfiguration []domain.PatternConfiguration) error { - var patterns []domain.PatternDefinition + patterns := make([]domain.PatternDefinition, len(patternConfiguration)) + for i, pattern := range patternConfiguration { + patterns[i] = pattern.PatternDefinition - if len(patternConfiguration) == 0 { - patternsConfig, err := codacyclient.GetDefaultToolPatternsConfig(initFlags, Lizard) - if err != nil { - return err - } - patterns = make([]domain.PatternDefinition, len(patternsConfig)) - for i, pattern := range patternsConfig { - patterns[i] = pattern.PatternDefinition - } - } else { - patterns = make([]domain.PatternDefinition, len(patternConfiguration)) - for i, pattern := range patternConfiguration { - patterns[i] = pattern.PatternDefinition - } } - - content, err := lizard.CreateLizardConfig(patterns) + err := lizard.CreateLizardConfig(toolsConfigDir, patterns) if err != nil { return fmt.Errorf("failed to create Lizard configuration: %w", err) } - - return os.WriteFile(filepath.Join(toolsConfigDir, "lizard.yaml"), []byte(content), utils.DefaultFilePerms) + return nil } // buildDefaultConfigurationFiles creates default configuration files for all tools func buildDefaultConfigurationFiles(toolsConfigDir string) error { - for _, tool := range AvailableTools { patternsConfig, err := codacyclient.GetDefaultToolPatternsConfig(initFlags, tool) if err != nil { @@ -521,18 +436,9 @@ func buildDefaultConfigurationFiles(toolsConfigDir string) error { } switch tool { case ESLint: - eslintConfigurationString := tools.CreateEslintConfig(patternsConfig) - - eslintConfigFile, err := os.Create(filepath.Join(toolsConfigDir, "eslint.config.mjs")) - if err != nil { + if err := tools.CreateEslintConfig(toolsConfigDir, patternsConfig); err != nil { return fmt.Errorf("failed to create eslint config file: %v", err) } - defer eslintConfigFile.Close() - - _, err = eslintConfigFile.WriteString(eslintConfigurationString) - if err != nil { - return fmt.Errorf("failed to write eslint config: %v", err) - } case Trivy: if err := createTrivyConfigFile(patternsConfig, toolsConfigDir); err != nil { return fmt.Errorf("failed to create default Trivy configuration: %w", err) @@ -559,7 +465,6 @@ func buildDefaultConfigurationFiles(toolsConfigDir string) error { } } } - return nil } diff --git a/tools/eslintConfigCreator.go b/tools/eslintConfigCreator.go index 7074f7b2..1e1ee156 100644 --- a/tools/eslintConfigCreator.go +++ b/tools/eslintConfigCreator.go @@ -4,6 +4,8 @@ import ( "codacy/cli-v2/domain" "encoding/json" "fmt" + "os" + "path/filepath" "strings" ) @@ -19,7 +21,7 @@ func quoteWhenIsNotJson(value string) string { } } -func CreateEslintConfig(configuration []domain.PatternConfiguration) string { +func CreateEslintConfig(toolsConfigDir string, configuration []domain.PatternConfiguration) error { result := `export default [ { rules: { @@ -139,6 +141,16 @@ func CreateEslintConfig(configuration []domain.PatternConfiguration) string { result += ` } } ];` + eslintConfigFile, err := os.Create(filepath.Join(toolsConfigDir, "eslint.config.mjs")) + if err != nil { + return fmt.Errorf("failed to create eslint config file: %v", err) + } + defer eslintConfigFile.Close() + + _, err = eslintConfigFile.WriteString(result) + if err != nil { + return fmt.Errorf("failed to write eslint config: %v", err) + } - return result + return nil } diff --git a/tools/eslintConfigCreator_test.go b/tools/eslintConfigCreator_test.go index df57e389..11e631f4 100644 --- a/tools/eslintConfigCreator_test.go +++ b/tools/eslintConfigCreator_test.go @@ -2,14 +2,30 @@ package tools import ( "codacy/cli-v2/domain" + "os" + "path/filepath" "testing" "github.com/stretchr/testify/assert" ) func testConfig(t *testing.T, configuration []domain.PatternConfiguration, expected string) { - actual := CreateEslintConfig(configuration) - assert.Equal(t, expected, actual) + // Create a temporary directory for the config file + tempDir, err := os.MkdirTemp("", "eslint-config-test") + assert.NoError(t, err) + defer os.RemoveAll(tempDir) + + // Call the function with the temporary directory + err = CreateEslintConfig(tempDir, configuration) + assert.NoError(t, err) + + // Read the generated file + configPath := filepath.Join(tempDir, "eslint.config.mjs") + content, err := os.ReadFile(configPath) + assert.NoError(t, err) + + // Compare the content + assert.Equal(t, expected, string(content)) } func TestCreateEslintConfigEmptyConfig(t *testing.T) { diff --git a/tools/lizard/lizardConfigCreator.go b/tools/lizard/lizardConfigCreator.go index cca50ce0..35c2fd97 100644 --- a/tools/lizard/lizardConfigCreator.go +++ b/tools/lizard/lizardConfigCreator.go @@ -2,8 +2,10 @@ package lizard import ( "codacy/cli-v2/domain" + "codacy/cli-v2/utils" "fmt" "os" + "path/filepath" "strconv" "strings" @@ -11,7 +13,7 @@ import ( ) // CreateLizardConfig generates a Lizard configuration file content based on the API configuration -func CreateLizardConfig(patterns []domain.PatternDefinition) (string, error) { +func CreateLizardConfig(toolsConfigDir string, patterns []domain.PatternDefinition) error { patternInfo := make(map[string]map[string]interface{}) for _, pattern := range patterns { @@ -45,10 +47,10 @@ func CreateLizardConfig(patterns []domain.PatternDefinition) (string, error) { yamlData, err := yaml.Marshal(config) if err != nil { - return "", fmt.Errorf("failed to marshal YAML: %w", err) + return fmt.Errorf("failed to marshal YAML: %w", err) } - return string(yamlData), nil + return os.WriteFile(filepath.Join(toolsConfigDir, "lizard.yaml"), yamlData, utils.DefaultFilePerms) } // getThresholdFromParams extracts the threshold value from the parameters From bab1ed515c85a7a7ac788aad6dbc146ae8c7cfff Mon Sep 17 00:00:00 2001 From: franciscoazevedo Date: Tue, 20 May 2025 17:19:58 +0100 Subject: [PATCH 2/2] refactor on eslintConfigCreator --- tools/eslintConfigCreator.go | 156 ++++++++++++++++++++--------------- 1 file changed, 88 insertions(+), 68 deletions(-) diff --git a/tools/eslintConfigCreator.go b/tools/eslintConfigCreator.go index 1e1ee156..39888165 100644 --- a/tools/eslintConfigCreator.go +++ b/tools/eslintConfigCreator.go @@ -21,6 +21,88 @@ func quoteWhenIsNotJson(value string) string { } } +func removePlugins(rule string) (string, bool) { + // Handle plugin rules (those containing _) + if strings.Contains(rule, "_") { + parts := strings.Split(rule, "_") + if len(parts) >= 2 { + // First part is the plugin name, last part is the rule name + plugin := parts[0] + ruleName := parts[len(parts)-1] + + // Handle scoped packages + if strings.HasPrefix(plugin, "@") { + rule = fmt.Sprintf("%s/%s", plugin, ruleName) + } else { + // For non-scoped packages, add eslint-plugin- prefix + rule = fmt.Sprintf("%s/%s", "eslint-plugin-"+plugin, ruleName) + } + } + } + + // Skip any rule that contains a plugin (contains "/") + if strings.Contains(rule, "/") { + return rule, true + } + + return rule, false +} + +func buildNamedParametersString(parameters []domain.ParameterConfiguration, patternDefinition domain.PatternDefinition) string { + namedParametersString := "" + for _, parameter := range parameters { + if parameter.Name != "unnamedParam" { + paramValue := parameter.Value + + // If value is empty, look for default in pattern definition + if paramValue == "" { + for _, paramDef := range patternDefinition.Parameters { + if paramDef.Name == parameter.Name && paramDef.Default != "" { + paramValue = paramDef.Default + break + } + } + } + + // Skip only if both value and default are empty + if paramValue == "" { + continue + } + + if len(namedParametersString) == 0 { + namedParametersString += "{" + } else { + namedParametersString += ", " + } + + if paramValue == "true" || paramValue == "false" { + namedParametersString += fmt.Sprintf("\"%s\": %s", parameter.Name, paramValue) + } else { + namedParametersString += fmt.Sprintf("\"%s\": %s", parameter.Name, quoteWhenIsNotJson(paramValue)) + } + } + } + if len(namedParametersString) > 0 { + namedParametersString += "}" + } + return namedParametersString +} + +func writeFile(filePath string, content string) error { + file, err := os.Create(filePath) + if err != nil { + return fmt.Errorf("failed to create file: %v", err) + } + defer file.Close() + + _, err = file.WriteString(content) + if err != nil { + return fmt.Errorf("failed to write content: %v", err) + } + + return nil +} + func CreateEslintConfig(toolsConfigDir string, configuration []domain.PatternConfiguration) error { result := `export default [ { @@ -30,26 +112,8 @@ func CreateEslintConfig(toolsConfigDir string, configuration []domain.PatternCon for _, patternConfiguration := range configuration { rule := strings.TrimPrefix(patternConfiguration.PatternDefinition.Id, "ESLint8_") - // Handle plugin rules (those containing _) - if strings.Contains(rule, "_") { - parts := strings.Split(rule, "_") - if len(parts) >= 2 { - // First part is the plugin name, last part is the rule name - plugin := parts[0] - ruleName := parts[len(parts)-1] - - // Handle scoped packages - if strings.HasPrefix(plugin, "@") { - rule = fmt.Sprintf("%s/%s", plugin, ruleName) - } else { - // For non-scoped packages, add eslint-plugin- prefix - rule = fmt.Sprintf("%s/%s", "eslint-plugin-"+plugin, ruleName) - } - } - } - - // Skip any rule that contains a plugin (contains "/") - if strings.Contains(rule, "/") { + rule, skipRule := removePlugins(rule) + if skipRule { continue } @@ -83,43 +147,8 @@ func CreateEslintConfig(toolsConfigDir string, configuration []domain.PatternCon parametersString += quoteWhenIsNotJson(defaultUnnamedParamValue) } - // build named parameters json object - namedParametersString := "" - for _, parameter := range patternConfiguration.Parameters { - if parameter.Name != "unnamedParam" { - paramValue := parameter.Value - - // If value is empty, look for default in pattern definition - if paramValue == "" { - for _, paramDef := range patternConfiguration.PatternDefinition.Parameters { - if paramDef.Name == parameter.Name && paramDef.Default != "" { - paramValue = paramDef.Default - break - } - } - } - - // Skip only if both value and default are empty - if paramValue == "" { - continue - } - - if len(namedParametersString) == 0 { - namedParametersString += "{" - } else { - namedParametersString += ", " - } - - if paramValue == "true" || paramValue == "false" { - namedParametersString += fmt.Sprintf("\"%s\": %s", parameter.Name, paramValue) - } else { - namedParametersString += fmt.Sprintf("\"%s\": %s", parameter.Name, quoteWhenIsNotJson(paramValue)) - } - } - } - if len(namedParametersString) > 0 { - namedParametersString += "}" - } + // Use the new helper method to build named parameters JSON object + namedParametersString := buildNamedParametersString(patternConfiguration.Parameters, patternConfiguration.PatternDefinition) if parametersString != "" && namedParametersString != "" { parametersString = fmt.Sprintf("%s, %s", parametersString, namedParametersString) @@ -141,16 +170,7 @@ func CreateEslintConfig(toolsConfigDir string, configuration []domain.PatternCon result += ` } } ];` - eslintConfigFile, err := os.Create(filepath.Join(toolsConfigDir, "eslint.config.mjs")) - if err != nil { - return fmt.Errorf("failed to create eslint config file: %v", err) - } - defer eslintConfigFile.Close() - - _, err = eslintConfigFile.WriteString(result) - if err != nil { - return fmt.Errorf("failed to write eslint config: %v", err) - } - return nil + eslintConfigFilePath := filepath.Join(toolsConfigDir, "eslint.config.mjs") + return writeFile(eslintConfigFilePath, result) }