From fc611e8634a944f03cc27685118d5cd8e20bfb2b Mon Sep 17 00:00:00 2001 From: franciscoazevedo Date: Mon, 7 Apr 2025 14:10:16 +0100 Subject: [PATCH 1/7] support use of codacy api token --- .codacy/cli-config.yaml | 2 +- .codacy/codacy.yaml | 2 +- cmd/init.go | 124 ++++++++++++++++++++++++++++++++++++++-- cmd/root.go | 1 + cmd/token.go | 21 +++++++ 5 files changed, 142 insertions(+), 8 deletions(-) create mode 100644 cmd/token.go diff --git a/.codacy/cli-config.yaml b/.codacy/cli-config.yaml index 6ae4b29d..644407e1 100644 --- a/.codacy/cli-config.yaml +++ b/.codacy/cli-config.yaml @@ -1 +1 @@ -mode: local \ No newline at end of file +mode: remote \ No newline at end of file diff --git a/.codacy/codacy.yaml b/.codacy/codacy.yaml index 820f105f..cc5bd6b6 100644 --- a/.codacy/codacy.yaml +++ b/.codacy/codacy.yaml @@ -2,7 +2,7 @@ runtimes: - node@22.2.0 - python@3.11.11 tools: - - eslint@9.3.0 + - eslint@8.57.0 - trivy@0.59.1 - pylint@3.3.6 - pmd@6.55.0 diff --git a/cmd/init.go b/cmd/init.go index ca09413a..cf254424 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -18,9 +18,11 @@ import ( const CodacyApiBase = "https://app.codacy.com" var codacyRepositoryToken string +var codacyApiToken string func init() { initCmd.Flags().StringVar(&codacyRepositoryToken, "repository-token", "", "optional codacy repository token, if defined configurations will be fetched from codacy") + initCmd.Flags().StringVar(&codacyApiToken, "codacy-api-token", "", "optional codacy api token, if defined configurations will be fetched from codacy") rootCmd.AddCommand(initCmd) } @@ -29,10 +31,9 @@ var initCmd = &cobra.Command{ Short: "Bootstraps project configuration", Long: "Bootstraps project configuration, creates codacy configuration file", Run: func(cmd *cobra.Command, args []string) { - config.Config.CreateLocalCodacyDir() - cliLocalMode := len(codacyRepositoryToken) == 0 + cliLocalMode := len(codacyRepositoryToken) == 0 && len(codacyApiToken) == 0 if cliLocalMode { fmt.Println() @@ -51,7 +52,15 @@ var initCmd = &cobra.Command{ if err != nil { log.Fatal(err) } - err = buildRepositoryConfigurationFiles(codacyRepositoryToken) + + var token Token + if len(codacyApiToken) > 0 { + token = ApiToken{value: codacyApiToken} + } else { + token = ProjectToken{value: codacyRepositoryToken} + } + + err = buildRepositoryConfigurationFiles(token) if err != nil { log.Fatal(err) } @@ -138,9 +147,112 @@ func cliConfigFileTemplate(cliLocalMode bool) string { return fmt.Sprintf(`mode: %s`, cliModeString) } -func buildRepositoryConfigurationFiles(token string) error { +func buildRepositoryConfigurationFiles(token Token) error { fmt.Println("Building repository configuration files ...") - fmt.Println("Fetching repository configuration from codacy ...") + switch token := token.(type) { + case ProjectToken: + return buildRepositoryConfigurationFilesFromRepositoryToken(token) + case ApiToken: + return buildRepositoryConfigurationFilesFromApiToken(token) + default: + return fmt.Errorf("unknown token type: %T", token) + } +} + +func buildRepositoryConfigurationFilesFromApiToken(token ApiToken) error { + fmt.Println("Fetching repository configuration from codacy using api token ...") + + // Ask for provider + var provider string + for { + fmt.Print("Enter provider (gh/bb/gl): ") + fmt.Scanln(&provider) + if provider == "gh" || provider == "bb" || provider == "gl" { + break + } + fmt.Println("Invalid provider. Please enter 'gh', 'bb', or 'gl'") + } + + // Ask for organization name + var organizationName string + fmt.Print("Enter remote organization name: ") + fmt.Scanln(&organizationName) + + // Ask for repository name + var repositoryName string + fmt.Print("Enter remote repository name: ") + fmt.Scanln(&repositoryName) + + var supportedTools = []string{ + "f8b29663-2cb2-498d-b923-a10c6a8c05cd", // ESLint + "2fd7fbe0-33f9-4ab3-ab73-e9b62404e2cb", // Trivy + "31677b6d-4ae0-4f56-8041-606a8d7a8e61", // Pylint + "9ed24812-b6ee-4a58-9004-0ed183c45b8f", // PMD + } + + client := &http.Client{ + Timeout: 10 * time.Second, + } + + for _, toolUuid := range supportedTools { + url := fmt.Sprintf("%s/api/v3/analysis/organizations/%s/%s/repositories/%s/tools/%s/patterns", + CodacyApiBase, + provider, + organizationName, + repositoryName, + toolUuid) + + // Create a new GET request + req, err := http.NewRequest("GET", url, nil) + if err != nil { + fmt.Println("Error:", err) + return err + } + + // Set the headers + req.Header.Set("api-token", token.Value()) + + // Send the request + resp, err := client.Do(req) + if err != nil { + fmt.Println("Error:", err) + return err + } + defer resp.Body.Close() + + if resp.StatusCode >= 400 { + return errors.New("failed to get repository's configuration from Codacy API") + } + + // Read the response body + body, err := io.ReadAll(resp.Body) + if err != nil { + fmt.Println("Error:", err) + return err + } + + var objmap map[string]json.RawMessage + err = json.Unmarshal(body, &objmap) + if err != nil { + fmt.Println("Error unmarshaling response:", err) + return err + } + + var apiToolConfigurations []CodacyToolConfiguration + err = json.Unmarshal(objmap["toolConfiguration"], &apiToolConfigurations) + if err != nil { + fmt.Println("Error unmarshaling tool configurations:", err) + return err + } + + // TODO: Process the response and create configuration files for each tool + } + + return nil +} + +func buildRepositoryConfigurationFilesFromRepositoryToken(token ProjectToken) error { + fmt.Println("Fetching repository configuration from codacy using repository token ...") // API call to fetch settings url := CodacyApiBase + "/2.0/project/analysis/configuration" @@ -157,7 +269,7 @@ func buildRepositoryConfigurationFiles(token string) error { } // Set the headers - req.Header.Set("project-token", token) + req.Header.Set("project-token", token.Value()) // Send the request resp, err := client.Do(req) diff --git a/cmd/root.go b/cmd/root.go index 5ff7a1e2..c2a9d284 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -48,6 +48,7 @@ func showWelcomeMessage() { fmt.Println() cyan.Println("Initialize your project with:") fmt.Println(" codacy-cli init --repository-token YOUR_TOKEN") + fmt.Println(" codacy-cli init --codacy-api-token YOUR_TOKEN") fmt.Println() fmt.Println("Or run without a token to use local configuration:") fmt.Println(" codacy-cli init") diff --git a/cmd/token.go b/cmd/token.go new file mode 100644 index 00000000..7eaa94f0 --- /dev/null +++ b/cmd/token.go @@ -0,0 +1,21 @@ +package cmd + +type Token interface { + Value() string +} + +type ProjectToken struct { + value string +} + +func (t ProjectToken) Value() string { + return t.value +} + +type ApiToken struct { + value string +} + +func (t ApiToken) Value() string { + return t.value +} From b8b9eae93da804fe50848dd733cef6c94c55b59f Mon Sep 17 00:00:00 2001 From: franciscoazevedo Date: Mon, 7 Apr 2025 18:07:58 +0100 Subject: [PATCH 2/7] refactor and using only codacy-api-token --- .codacy/cli-config.yaml | 1 - .codacy/codacy.yaml | 8 - cmd/init.go | 391 ++++++++------------------------- domain/patternConfiguration.go | 16 ++ {cmd => domain}/token.go | 10 +- domain/toolName.go | 9 + tools/eslintConfigCreator.go | 11 +- tools/getTools.go | 45 +++- tools/pmdConfigCreator.go | 11 +- tools/trivyConfigCreator.go | 15 +- 10 files changed, 180 insertions(+), 337 deletions(-) delete mode 100644 .codacy/cli-config.yaml delete mode 100644 .codacy/codacy.yaml create mode 100644 domain/patternConfiguration.go rename {cmd => domain}/token.go (57%) create mode 100644 domain/toolName.go diff --git a/.codacy/cli-config.yaml b/.codacy/cli-config.yaml deleted file mode 100644 index 644407e1..00000000 --- a/.codacy/cli-config.yaml +++ /dev/null @@ -1 +0,0 @@ -mode: remote \ No newline at end of file diff --git a/.codacy/codacy.yaml b/.codacy/codacy.yaml deleted file mode 100644 index cc5bd6b6..00000000 --- a/.codacy/codacy.yaml +++ /dev/null @@ -1,8 +0,0 @@ -runtimes: - - node@22.2.0 - - python@3.11.11 -tools: - - eslint@8.57.0 - - trivy@0.59.1 - - pylint@3.3.6 - - pmd@6.55.0 diff --git a/cmd/init.go b/cmd/init.go index cf254424..9fcf0851 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -2,6 +2,7 @@ package cmd import ( "codacy/cli-v2/config" + "codacy/cli-v2/domain" "codacy/cli-v2/tools" "encoding/json" "errors" @@ -19,10 +20,15 @@ const CodacyApiBase = "https://app.codacy.com" var codacyRepositoryToken string var codacyApiToken string +var remoteProvider string +var remoteOrganizationName string +var remoteRepositoryName string func init() { - initCmd.Flags().StringVar(&codacyRepositoryToken, "repository-token", "", "optional codacy repository token, if defined configurations will be fetched from codacy") initCmd.Flags().StringVar(&codacyApiToken, "codacy-api-token", "", "optional codacy api token, if defined configurations will be fetched from codacy") + initCmd.Flags().StringVar(&remoteProvider, "provider", "", "optional provider (gh/bb/gl), if defined configurations will be fetched from codacy") + initCmd.Flags().StringVar(&remoteOrganizationName, "organization", "", "optional remote organization name, if defined configurations will be fetched from codacy") + initCmd.Flags().StringVar(&remoteRepositoryName, "repository", "", "optional remote repository name, if defined configurations will be fetched from codacy") rootCmd.AddCommand(initCmd) } @@ -33,7 +39,7 @@ var initCmd = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { config.Config.CreateLocalCodacyDir() - cliLocalMode := len(codacyRepositoryToken) == 0 && len(codacyApiToken) == 0 + cliLocalMode := len(codacyApiToken) == 0 if cliLocalMode { fmt.Println() @@ -44,23 +50,8 @@ var initCmd = &cobra.Command{ log.Fatal(err) } } else { - apiTools, err := tools.GetTools() - if err != nil { - log.Fatal(err) - } - err = createConfigurationFiles(apiTools, cliLocalMode) - if err != nil { - log.Fatal(err) - } - - var token Token - if len(codacyApiToken) > 0 { - token = ApiToken{value: codacyApiToken} - } else { - token = ProjectToken{value: codacyRepositoryToken} - } - - err = buildRepositoryConfigurationFiles(token) + token := domain.NewApiToken(codacyApiToken) + err := buildRepositoryConfigurationFiles(token) if err != nil { log.Fatal(err) } @@ -110,16 +101,14 @@ func configFileTemplate(tools []tools.Tool) string { pmdVersion := "6.55.0" for _, tool := range tools { - if tool.Uuid == "f8b29663-2cb2-498d-b923-a10c6a8c05cd" { + switch tool.Uuid { + case string(ESLint): eslintVersion = tool.Version - } - if tool.Uuid == "2fd7fbe0-33f9-4ab3-ab73-e9b62404e2cb" { + case string(Trivy): trivyVersion = tool.Version - } - if tool.Uuid == "31677b6d-4ae0-4f56-8041-606a8d7a8e61" { + case string(PyLint): pylintVersion = tool.Version - } - if tool.Uuid == "9ed24812-b6ee-4a58-9004-0ed183c45b8f" { + case string(PMD): pmdVersion = tool.Version } } @@ -147,61 +136,42 @@ func cliConfigFileTemplate(cliLocalMode bool) string { return fmt.Sprintf(`mode: %s`, cliModeString) } -func buildRepositoryConfigurationFiles(token Token) error { +func buildRepositoryConfigurationFiles(token domain.Token) error { fmt.Println("Building repository configuration files ...") switch token := token.(type) { - case ProjectToken: - return buildRepositoryConfigurationFilesFromRepositoryToken(token) - case ApiToken: + case domain.ApiToken: return buildRepositoryConfigurationFilesFromApiToken(token) default: return fmt.Errorf("unknown token type: %T", token) } } -func buildRepositoryConfigurationFilesFromApiToken(token ApiToken) error { +func buildRepositoryConfigurationFilesFromApiToken(token domain.ApiToken) error { fmt.Println("Fetching repository configuration from codacy using api token ...") - // Ask for provider - var provider string - for { - fmt.Print("Enter provider (gh/bb/gl): ") - fmt.Scanln(&provider) - if provider == "gh" || provider == "bb" || provider == "gl" { - break - } - fmt.Println("Invalid provider. Please enter 'gh', 'bb', or 'gl'") + client := &http.Client{ + Timeout: 10 * time.Second, } - // Ask for organization name - var organizationName string - fmt.Print("Enter remote organization name: ") - fmt.Scanln(&organizationName) - - // Ask for repository name - var repositoryName string - fmt.Print("Enter remote repository name: ") - fmt.Scanln(&repositoryName) - - var supportedTools = []string{ - "f8b29663-2cb2-498d-b923-a10c6a8c05cd", // ESLint - "2fd7fbe0-33f9-4ab3-ab73-e9b62404e2cb", // Trivy - "31677b6d-4ae0-4f56-8041-606a8d7a8e61", // Pylint - "9ed24812-b6ee-4a58-9004-0ed183c45b8f", // PMD + apiTools, err := tools.GetRepositoryTools(token, remoteProvider, remoteOrganizationName, remoteRepositoryName) + if err != nil { + return err } - client := &http.Client{ - Timeout: 10 * time.Second, + err = createConfigurationFiles(apiTools, true) + if err != nil { + log.Fatal(err) } - for _, toolUuid := range supportedTools { - url := fmt.Sprintf("%s/api/v3/analysis/organizations/%s/%s/repositories/%s/tools/%s/patterns", + for _, tool := range apiTools { + url := fmt.Sprintf("%s/api/v3/analysis/organizations/%s/%s/repositories/%s/tools/%s/patterns?enabled=true", CodacyApiBase, - provider, - organizationName, - repositoryName, - toolUuid) + remoteProvider, + remoteOrganizationName, + remoteRepositoryName, + tool.Uuid) + fmt.Printf("url: %q\n", url) // Create a new GET request req, err := http.NewRequest("GET", url, nil) if err != nil { @@ -233,287 +203,97 @@ func buildRepositoryConfigurationFilesFromApiToken(token ApiToken) error { var objmap map[string]json.RawMessage err = json.Unmarshal(body, &objmap) - if err != nil { - fmt.Println("Error unmarshaling response:", err) - return err - } - var apiToolConfigurations []CodacyToolConfiguration - err = json.Unmarshal(objmap["toolConfiguration"], &apiToolConfigurations) if err != nil { - fmt.Println("Error unmarshaling tool configurations:", err) + fmt.Println("Error unmarshaling response:", err) return err } - // TODO: Process the response and create configuration files for each tool - } - - return nil -} - -func buildRepositoryConfigurationFilesFromRepositoryToken(token ProjectToken) error { - fmt.Println("Fetching repository configuration from codacy using repository token ...") + var apiToolConfigurations []domain.PatternConfiguration + err = json.Unmarshal(objmap["data"], &apiToolConfigurations) - // API call to fetch settings - url := CodacyApiBase + "/2.0/project/analysis/configuration" - - client := &http.Client{ - Timeout: 10 * time.Second, - } - - // Create a new GET request - req, err := http.NewRequest("GET", url, nil) - if err != nil { - fmt.Println("Error:", err) - return err - } - - // Set the headers - req.Header.Set("project-token", token.Value()) - - // Send the request - resp, err := client.Do(req) - if err != nil { - fmt.Println("Error:", err) - return err - } - defer resp.Body.Close() - - if resp.StatusCode >= 400 { - return errors.New("failed to get repository's configuration from Codacy API") - } - - // Read the response body - body, err := io.ReadAll(resp.Body) - if err != nil { - fmt.Println("Error:", err) - return err - } - - var objmap map[string]json.RawMessage - err = json.Unmarshal(body, &objmap) - if err != nil { - fmt.Println("Error unmarshaling response:", err) - return err - } - - var apiToolConfigurations []CodacyToolConfiguration - err = json.Unmarshal(objmap["toolConfiguration"], &apiToolConfigurations) - if err != nil { - fmt.Println("Error unmarshaling tool configurations:", err) - return err - } - - // ESLint configuration - eslintApiConfiguration := extractESLintConfiguration(apiToolConfigurations) - if eslintApiConfiguration != nil { - eslintDomainConfiguration := convertAPIToolConfigurationToDomain(*eslintApiConfiguration) - eslintConfigurationString := tools.CreateEslintConfig(eslintDomainConfiguration) - - eslintConfigFile, err := os.Create("eslint.config.mjs") - if err != nil { - return fmt.Errorf("failed to create eslint config file: %v", err) + for _, toolConfiguration := range apiToolConfigurations { + fmt.Println("tool configuration", toolConfiguration) } - 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") - } else { - err = createDefaultEslintConfigFile() - if err != nil { - return fmt.Errorf("failed to create default ESLint config: %v", err) - } - fmt.Println("Default ESLint configuration created") - } - - // Trivy configuration - trivyApiConfiguration := extractTrivyConfiguration(apiToolConfigurations) - if trivyApiConfiguration != nil { - err = createTrivyConfigFile(*trivyApiConfiguration) - 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() - if err != nil { - return fmt.Errorf("failed to create default Trivy config: %v", err) + fmt.Println("Error unmarshaling tool configurations:", err) + return err } - fmt.Println("Default Trivy configuration created") - } - // PMD configuration - pmdApiConfiguration := extractPMDConfiguration(apiToolConfigurations) - if pmdApiConfiguration != nil { - err = createPMDConfigFile(*pmdApiConfiguration) - 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() - if err != nil { - return fmt.Errorf("failed to create default PMD config: %v", err) - } - fmt.Println("Default PMD configuration created") + createToolFileConfigurations(tool, apiToolConfigurations) + // TODO: Process the response and create configuration files for each tool } return nil } -func convertAPIToolConfigurationToDomain(config CodacyToolConfiguration) tools.ToolConfiguration { - var patterns []tools.PatternConfiguration +// map tool uuid to tool name +func createToolFileConfigurations(tool tools.Tool, patternConfiguration []domain.PatternConfiguration) error { + switch tool.Uuid { + case string(ESLint): + if len(patternConfiguration) > 0 { + eslintConfigurationString := tools.CreateEslintConfig(patternConfiguration) - for _, pattern := range config.Patterns { - var parameters []tools.PatternParameterConfiguration + eslintConfigFile, err := os.Create("eslint.config.mjs") + if err != nil { + return fmt.Errorf("failed to create eslint config file: %v", err) + } + defer eslintConfigFile.Close() - for _, parameter := range pattern.Parameters { - parameters = append(parameters, tools.PatternParameterConfiguration{ - Name: parameter.Name, - Value: parameter.Value, - }) + _, 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") + } else { + err := createDefaultEslintConfigFile() + if err != nil { + return fmt.Errorf("failed to create default ESLint config: %v", err) + } + fmt.Println("Default ESLint configuration created") } - - patterns = append( - patterns, - tools.PatternConfiguration{ - PatternId: pattern.InternalId, - ParameterConfigurations: parameters, - }, - ) - } - - return tools.ToolConfiguration{ - PatternsConfiguration: patterns, - } -} - -func extractESLintConfiguration(toolConfigurations []CodacyToolConfiguration) *CodacyToolConfiguration { - - //ESLInt internal codacy uuid, to filter ot not ESLint tools - //"f8b29663-2cb2-498d-b923-a10c6a8c05cd" - - for _, toolConfiguration := range toolConfigurations { - if toolConfiguration.Uuid == "f8b29663-2cb2-498d-b923-a10c6a8c05cd" { - return &toolConfiguration + case string(Trivy): + if len(patternConfiguration) > 0 { + return createTrivyConfigFile(patternConfiguration) + } else { + return createDefaultTrivyConfigFile() } - } - - return nil -} - -// extractTrivyConfiguration extracts Trivy configuration from the Codacy API response -func extractTrivyConfiguration(toolConfigurations []CodacyToolConfiguration) *CodacyToolConfiguration { - // Trivy internal codacy uuid - const TrivyUUID = "2fd7fbe0-33f9-4ab3-ab73-e9b62404e2cb" - for _, toolConfiguration := range toolConfigurations { - if toolConfiguration.Uuid == TrivyUUID { - return &toolConfiguration + case string(PMD): + if len(patternConfiguration) > 0 { + return createPMDConfigFile(patternConfiguration) + } else { + return createDefaultPMDConfigFile() } - } - return nil -} - -// Add PMD-specific functions -func extractPMDConfiguration(toolConfigurations []CodacyToolConfiguration) *CodacyToolConfiguration { - const PMDUUID = "9ed24812-b6ee-4a58-9004-0ed183c45b8f" - for _, toolConfiguration := range toolConfigurations { - if toolConfiguration.Uuid == PMDUUID { - return &toolConfiguration - } } return nil } -func createPMDConfigFile(config CodacyToolConfiguration) error { - pmdDomainConfiguration := convertAPIToolConfigurationToDomain(config) - pmdConfigurationString := tools.CreatePmdConfig(pmdDomainConfiguration) +func createPMDConfigFile(config []domain.PatternConfiguration) error { + pmdConfigurationString := tools.CreatePmdConfig(config) return os.WriteFile("pmd-ruleset.xml", []byte(pmdConfigurationString), 0644) } func createDefaultPMDConfigFile() error { - emptyConfig := tools.ToolConfiguration{} - content := tools.CreatePmdConfig(emptyConfig) + content := tools.CreatePmdConfig([]domain.PatternConfiguration{}) return os.WriteFile("pmd-ruleset.xml", []byte(content), 0644) } -type CodacyToolConfiguration struct { - Uuid string `json:"uuid"` - IsEnabled bool `json:"isEnabled"` - Patterns []PatternConfiguration `json:"patterns"` -} - -type PatternConfiguration struct { - InternalId string `json:"internalId"` - Parameters []ParameterConfiguration `json:"parameters"` -} - -type ParameterConfiguration struct { - Name string `json:"name"` - Value string `json:"value"` -} - // createTrivyConfigFile creates a trivy.yaml configuration file based on the API configuration -func createTrivyConfigFile(config CodacyToolConfiguration) error { - // Convert CodacyToolConfiguration to tools.ToolConfiguration - trivyDomainConfiguration := convertAPIToolConfigurationForTrivy(config) +func createTrivyConfigFile(config []domain.PatternConfiguration) error { - // Use the shared CreateTrivyConfig function to generate the config content - trivyConfigurationString := tools.CreateTrivyConfig(trivyDomainConfiguration) + trivyConfigurationString := tools.CreateTrivyConfig(config) // Write to file return os.WriteFile("trivy.yaml", []byte(trivyConfigurationString), 0644) } -// convertAPIToolConfigurationForTrivy converts API tool configuration to domain model for Trivy -func convertAPIToolConfigurationForTrivy(config CodacyToolConfiguration) tools.ToolConfiguration { - var patterns []tools.PatternConfiguration - - // Only process if tool is enabled - if config.IsEnabled { - for _, pattern := range config.Patterns { - var parameters []tools.PatternParameterConfiguration - - // By default patterns are enabled - patternEnabled := true - - // Check if there's an explicit enabled parameter - for _, param := range pattern.Parameters { - if param.Name == "enabled" && param.Value == "false" { - patternEnabled = false - } - } - - // Add enabled parameter - parameters = append(parameters, tools.PatternParameterConfiguration{ - Name: "enabled", - Value: fmt.Sprintf("%t", patternEnabled), - }) - - patterns = append( - patterns, - tools.PatternConfiguration{ - PatternId: pattern.InternalId, - ParameterConfigurations: parameters, - }, - ) - } - } - - return tools.ToolConfiguration{ - PatternsConfiguration: patterns, - } -} - // createDefaultTrivyConfigFile creates a default trivy.yaml configuration file func createDefaultTrivyConfigFile() error { // Use empty tool configuration to get default settings - emptyConfig := tools.ToolConfiguration{} + emptyConfig := []domain.PatternConfiguration{} content := tools.CreateTrivyConfig(emptyConfig) // Write to file @@ -523,9 +303,18 @@ func createDefaultTrivyConfigFile() error { // createDefaultEslintConfigFile creates a default eslint.config.mjs configuration file func createDefaultEslintConfigFile() error { // Use empty tool configuration to get default settings - emptyConfig := tools.ToolConfiguration{} + emptyConfig := []domain.PatternConfiguration{} content := tools.CreateEslintConfig(emptyConfig) // Write to file return os.WriteFile("eslint.config.mjs", []byte(content), 0644) } + +type ToolUiid string + +const ( + ESLint ToolUiid = "f8b29663-2cb2-498d-b923-a10c6a8c05cd" + Trivy ToolUiid = "2fd7fbe0-33f9-4ab3-ab73-e9b62404e2cb" + PMD ToolUiid = "9ed24812-b6ee-4a58-9004-0ed183c45b8f" + PyLint ToolUiid = "31677b6d-4ae0-4f56-8041-606a8d7a8e61" +) diff --git a/domain/patternConfiguration.go b/domain/patternConfiguration.go new file mode 100644 index 00000000..039a9671 --- /dev/null +++ b/domain/patternConfiguration.go @@ -0,0 +1,16 @@ +package domain + +type ParameterConfiguration struct { + Name string `json:"name"` + Value string `json:"value"` +} + +type PatternDefinition struct { + Id string `json:"id"` + Parameters []ParameterConfiguration `json:"parameters"` +} + +type PatternConfiguration struct { + PatternDefinition PatternDefinition `json:"patternDefinition"` + Parameters []ParameterConfiguration +} diff --git a/cmd/token.go b/domain/token.go similarity index 57% rename from cmd/token.go rename to domain/token.go index 7eaa94f0..bb7b614c 100644 --- a/cmd/token.go +++ b/domain/token.go @@ -1,4 +1,4 @@ -package cmd +package domain type Token interface { Value() string @@ -12,6 +12,10 @@ func (t ProjectToken) Value() string { return t.value } +func NewProjectToken(value string) ProjectToken { + return ProjectToken{value: value} +} + type ApiToken struct { value string } @@ -19,3 +23,7 @@ type ApiToken struct { func (t ApiToken) Value() string { return t.value } + +func NewApiToken(value string) ApiToken { + return ApiToken{value: value} +} diff --git a/domain/toolName.go b/domain/toolName.go new file mode 100644 index 00000000..a0dbe51f --- /dev/null +++ b/domain/toolName.go @@ -0,0 +1,9 @@ +package domain + +type ToolName string + +const ( + ESLint ToolName = "eslint" + Trivy ToolName = "trivy" + PMD ToolName = "pmd" +) diff --git a/tools/eslintConfigCreator.go b/tools/eslintConfigCreator.go index f52bf776..72f1a3c4 100644 --- a/tools/eslintConfigCreator.go +++ b/tools/eslintConfigCreator.go @@ -1,6 +1,7 @@ package tools import ( + "codacy/cli-v2/domain" "encoding/json" "fmt" "strings" @@ -18,14 +19,14 @@ func quoteWhenIsNotJson(value string) string { } } -func CreateEslintConfig(configuration ToolConfiguration) string { +func CreateEslintConfig(configuration []domain.PatternConfiguration) string { result := `export default [ { rules: { ` - for _, patternConfiguration := range configuration.PatternsConfiguration { - rule := strings.TrimPrefix(patternConfiguration.PatternId, "ESLint8_") + for _, patternConfiguration := range configuration { + rule := strings.TrimPrefix(patternConfiguration.PatternDefinition.Id, "ESLint8_") fmt.Println("Rule:", rule) const tempstring = "TEMPORARYSTRING" @@ -35,7 +36,7 @@ func CreateEslintConfig(configuration ToolConfiguration) string { parametersString := "" - for _, parameter := range patternConfiguration.ParameterConfigurations { + for _, parameter := range patternConfiguration.Parameters { if parameter.Name == "unnamedParam" { parametersString += quoteWhenIsNotJson(parameter.Value) } @@ -43,7 +44,7 @@ func CreateEslintConfig(configuration ToolConfiguration) string { // build named parameters json object namedParametersString := "" - for _, parameter := range patternConfiguration.ParameterConfigurations { + for _, parameter := range patternConfiguration.Parameters { if parameter.Name != "unnamedParam" { if len(namedParametersString) == 0 { diff --git a/tools/getTools.go b/tools/getTools.go index fc269c8e..759ed3c2 100644 --- a/tools/getTools.go +++ b/tools/getTools.go @@ -1,6 +1,7 @@ package tools import ( + "codacy/cli-v2/domain" "encoding/json" "errors" "fmt" @@ -9,40 +10,62 @@ import ( "time" ) -func GetTools() ([]Tool, error) { +func GetRepositoryTools(apiToken domain.ApiToken, provider string, organization string, repository string) ([]Tool, error) { client := &http.Client{ Timeout: 10 * time.Second, } + url := fmt.Sprintf("https://api.codacy.com/api/v3/analysis/organizations/%s/%s/repositories/%s/tools", + provider, + organization, + repository) + + fmt.Println("url", url) // Create a new GET request - req, err := http.NewRequest("GET", "https://api.codacy.com/api/v3/tools", nil) + req, err := http.NewRequest("GET", url, nil) if err != nil { fmt.Println("Error:", err) return nil, err } + // Set the headers + req.Header.Set("api-token", apiToken.Value()) + // Send the request resp, err := client.Do(req) if err != nil { fmt.Println("Error:", err) return nil, err } + defer resp.Body.Close() + if resp.StatusCode != 200 { - return nil, errors.New("failed to get tools from Codacy API") + return nil, errors.New("failed to get repository tools from Codacy API") } // Read the response body body, err := io.ReadAll(resp.Body) - defer resp.Body.Close() if err != nil { fmt.Println("Error:", err) return nil, err } var response ToolsResponse - _ = json.Unmarshal(body, &response) + err = json.Unmarshal(body, &response) + if err != nil { + fmt.Println("Error unmarshaling response:", err) + return nil, err + } + + // Filter enabled tools + var enabledTools []Tool + for _, tool := range response.Data { + if tool.Settings.Enabled { + enabledTools = append(enabledTools, tool) + } + } - return response.Data, nil + return enabledTools, nil } type ToolsResponse struct { @@ -50,7 +73,11 @@ type ToolsResponse struct { } type Tool struct { - Uuid string `json:"uuid"` - Name string `json:"name"` - Version string `json:"version"` + Uuid string `json:"uuid"` + Name string `json:"name"` + Version string `json:"version"` + Settings struct { + Enabled bool `json:"isEnabled"` + UsesConfigFile bool `json:"hasConfigurationFile"` + } `json:"settings"` } diff --git a/tools/pmdConfigCreator.go b/tools/pmdConfigCreator.go index a32dc8d6..c349a89b 100644 --- a/tools/pmdConfigCreator.go +++ b/tools/pmdConfigCreator.go @@ -1,6 +1,7 @@ package tools import ( + "codacy/cli-v2/domain" _ "embed" "encoding/xml" "fmt" @@ -171,20 +172,20 @@ func ConvertToPMDRuleset(rules []Rule) (string, error) { } // CreatePmdConfig creates a PMD configuration from the provided tool configuration -func CreatePmdConfig(configuration ToolConfiguration) string { +func CreatePmdConfig(configuration []domain.PatternConfiguration) string { // If no patterns provided, return the default ruleset - if len(configuration.PatternsConfiguration) == 0 { + if len(configuration) == 0 { return defaultPMDRuleset } // Convert ToolConfiguration to our Rule format var rules []Rule - for _, pattern := range configuration.PatternsConfiguration { + for _, pattern := range configuration { // Check if pattern is enabled patternEnabled := true var parameters []Parameter - for _, param := range pattern.ParameterConfigurations { + for _, param := range pattern.Parameters { if param.Name == "enabled" && param.Value == "false" { patternEnabled = false break @@ -198,7 +199,7 @@ func CreatePmdConfig(configuration ToolConfiguration) string { } // Apply prefix to pattern ID if needed - patternID := pattern.PatternId + patternID := pattern.PatternDefinition.Id if !strings.HasPrefix(patternID, "PMD_") && !strings.Contains(patternID, "/") { patternID = prefixPatternID(patternID) } diff --git a/tools/trivyConfigCreator.go b/tools/trivyConfigCreator.go index 4d50bb80..36be28ba 100644 --- a/tools/trivyConfigCreator.go +++ b/tools/trivyConfigCreator.go @@ -1,12 +1,13 @@ package tools import ( + "codacy/cli-v2/domain" "fmt" "strings" ) // CreateTrivyConfig generates a Trivy configuration based on the tool configuration -func CreateTrivyConfig(config ToolConfiguration) string { +func CreateTrivyConfig(config []domain.PatternConfiguration) string { // Default settings - include all severities and scanners includeLow := true includeMedium := true @@ -15,28 +16,28 @@ func CreateTrivyConfig(config ToolConfiguration) string { includeSecret := true // Process patterns from Codacy API - for _, pattern := range config.PatternsConfiguration { + for _, pattern := range config { // Check if pattern is enabled patternEnabled := true - for _, param := range pattern.ParameterConfigurations { + for _, param := range pattern.Parameters { if param.Name == "enabled" && param.Value == "false" { patternEnabled = false } } // Map patterns to configurations - if pattern.PatternId == "Trivy_vulnerability_minor" { + if pattern.PatternDefinition.Id == "Trivy_vulnerability_minor" { includeLow = patternEnabled } - if pattern.PatternId == "Trivy_vulnerability_medium" { + if pattern.PatternDefinition.Id == "Trivy_vulnerability_medium" { includeMedium = patternEnabled } - if pattern.PatternId == "Trivy_vulnerability" { + if pattern.PatternDefinition.Id == "Trivy_vulnerability" { // This covers HIGH and CRITICAL includeHigh = patternEnabled includeCritical = patternEnabled } - if pattern.PatternId == "Trivy_secret" { + if pattern.PatternDefinition.Id == "Trivy_secret" { includeSecret = patternEnabled } } From ed511ef94b8128b5b37a2e0ad4a1ad361ac67cf2 Mon Sep 17 00:00:00 2001 From: franciscoazevedo Date: Tue, 8 Apr 2025 10:36:14 +0100 Subject: [PATCH 3/7] code review changes applied --- .codacy/cli-config.yaml | 1 + .codacy/codacy.yaml | 8 + cmd/init.go | 76 ++++------ domain/patternConfiguration.go | 3 +- domain/token.go | 29 ---- domain/toolName.go | 9 -- eslint.config.mjs | 10 ++ pmd-ruleset.xml | 101 +++++++++++++ tools/eslintConfigCreator.go | 1 - tools/eslintConfigCreator_test.go | 83 ++++++----- tools/getTools.go | 17 ++- tools/getTools_test.go | 19 --- tools/pmdConfigCreator_test.go | 82 +++++----- tools/trivyConfigCreator_test.go | 239 ++++++++++++++++-------------- trivy.yaml | 10 ++ 15 files changed, 384 insertions(+), 304 deletions(-) create mode 100644 .codacy/cli-config.yaml create mode 100644 .codacy/codacy.yaml delete mode 100644 domain/token.go delete mode 100644 domain/toolName.go create mode 100644 eslint.config.mjs create mode 100644 pmd-ruleset.xml delete mode 100644 tools/getTools_test.go create mode 100644 trivy.yaml diff --git a/.codacy/cli-config.yaml b/.codacy/cli-config.yaml new file mode 100644 index 00000000..6ae4b29d --- /dev/null +++ b/.codacy/cli-config.yaml @@ -0,0 +1 @@ +mode: local \ No newline at end of file diff --git a/.codacy/codacy.yaml b/.codacy/codacy.yaml new file mode 100644 index 00000000..9092e0a0 --- /dev/null +++ b/.codacy/codacy.yaml @@ -0,0 +1,8 @@ +runtimes: + - node@22.2.0 + - python@3.11.11 +tools: + - eslint@ + - trivy@ + - pylint@3.3.6 + - pmd@ diff --git a/cmd/init.go b/cmd/init.go index 9fcf0851..bec95ada 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -18,17 +18,16 @@ import ( const CodacyApiBase = "https://app.codacy.com" -var codacyRepositoryToken string var codacyApiToken string var remoteProvider string -var remoteOrganizationName string -var remoteRepositoryName string +var organization string +var remoteRepo string func init() { - initCmd.Flags().StringVar(&codacyApiToken, "codacy-api-token", "", "optional codacy api token, if defined configurations will be fetched from codacy") + initCmd.Flags().StringVar(&codacyApiToken, "api-token", "", "optional codacy api token, if defined configurations will be fetched from codacy") initCmd.Flags().StringVar(&remoteProvider, "provider", "", "optional provider (gh/bb/gl), if defined configurations will be fetched from codacy") - initCmd.Flags().StringVar(&remoteOrganizationName, "organization", "", "optional remote organization name, if defined configurations will be fetched from codacy") - initCmd.Flags().StringVar(&remoteRepositoryName, "repository", "", "optional remote repository name, if defined configurations will be fetched from codacy") + initCmd.Flags().StringVar(&organization, "organization", "", "optional remote organization name, if defined configurations will be fetched from codacy") + initCmd.Flags().StringVar(&remoteRepo, "repository", "", "optional remote repository name, if defined configurations will be fetched from codacy") rootCmd.AddCommand(initCmd) } @@ -50,8 +49,7 @@ var initCmd = &cobra.Command{ log.Fatal(err) } } else { - token := domain.NewApiToken(codacyApiToken) - err := buildRepositoryConfigurationFiles(token) + err := buildRepositoryConfigurationFiles(codacyApiToken) if err != nil { log.Fatal(err) } @@ -102,13 +100,13 @@ func configFileTemplate(tools []tools.Tool) string { for _, tool := range tools { switch tool.Uuid { - case string(ESLint): + case ESLint: eslintVersion = tool.Version - case string(Trivy): + case Trivy: trivyVersion = tool.Version - case string(PyLint): + case PyLint: pylintVersion = tool.Version - case string(PMD): + case PMD: pmdVersion = tool.Version } } @@ -136,24 +134,14 @@ func cliConfigFileTemplate(cliLocalMode bool) string { return fmt.Sprintf(`mode: %s`, cliModeString) } -func buildRepositoryConfigurationFiles(token domain.Token) error { - fmt.Println("Building repository configuration files ...") - switch token := token.(type) { - case domain.ApiToken: - return buildRepositoryConfigurationFilesFromApiToken(token) - default: - return fmt.Errorf("unknown token type: %T", token) - } -} - -func buildRepositoryConfigurationFilesFromApiToken(token domain.ApiToken) error { +func buildRepositoryConfigurationFiles(token string) error { fmt.Println("Fetching repository configuration from codacy using api token ...") client := &http.Client{ Timeout: 10 * time.Second, } - apiTools, err := tools.GetRepositoryTools(token, remoteProvider, remoteOrganizationName, remoteRepositoryName) + apiTools, err := tools.GetRepositoryTools(CodacyApiBase, token, remoteProvider, organization, remoteRepo) if err != nil { return err } @@ -167,11 +155,10 @@ func buildRepositoryConfigurationFilesFromApiToken(token domain.ApiToken) error url := fmt.Sprintf("%s/api/v3/analysis/organizations/%s/%s/repositories/%s/tools/%s/patterns?enabled=true", CodacyApiBase, remoteProvider, - remoteOrganizationName, - remoteRepositoryName, + organization, + remoteRepo, tool.Uuid) - fmt.Printf("url: %q\n", url) // Create a new GET request req, err := http.NewRequest("GET", url, nil) if err != nil { @@ -180,7 +167,7 @@ func buildRepositoryConfigurationFilesFromApiToken(token domain.ApiToken) error } // Set the headers - req.Header.Set("api-token", token.Value()) + req.Header.Set("api-token", token) // Send the request resp, err := client.Do(req) @@ -212,17 +199,12 @@ func buildRepositoryConfigurationFilesFromApiToken(token domain.ApiToken) error var apiToolConfigurations []domain.PatternConfiguration err = json.Unmarshal(objmap["data"], &apiToolConfigurations) - for _, toolConfiguration := range apiToolConfigurations { - fmt.Println("tool configuration", toolConfiguration) - } - if err != nil { fmt.Println("Error unmarshaling tool configurations:", err) return err } createToolFileConfigurations(tool, apiToolConfigurations) - // TODO: Process the response and create configuration files for each tool } return nil @@ -231,7 +213,7 @@ func buildRepositoryConfigurationFilesFromApiToken(token domain.ApiToken) error // map tool uuid to tool name func createToolFileConfigurations(tool tools.Tool, patternConfiguration []domain.PatternConfiguration) error { switch tool.Uuid { - case string(ESLint): + case ESLint: if len(patternConfiguration) > 0 { eslintConfigurationString := tools.CreateEslintConfig(patternConfiguration) @@ -253,20 +235,20 @@ func createToolFileConfigurations(tool tools.Tool, patternConfiguration []domain } fmt.Println("Default ESLint configuration created") } - case string(Trivy): + case Trivy: if len(patternConfiguration) > 0 { - return createTrivyConfigFile(patternConfiguration) + createTrivyConfigFile(patternConfiguration) } else { - return createDefaultTrivyConfigFile() + createDefaultTrivyConfigFile() } - - case string(PMD): + fmt.Println("Trivy configuration created based on Codacy settings") + case PMD: if len(patternConfiguration) > 0 { - return createPMDConfigFile(patternConfiguration) + createPMDConfigFile(patternConfiguration) } else { - return createDefaultPMDConfigFile() + createDefaultPMDConfigFile() } - + fmt.Println("PMD configuration created based on Codacy settings") } return nil } @@ -310,11 +292,9 @@ func createDefaultEslintConfigFile() error { return os.WriteFile("eslint.config.mjs", []byte(content), 0644) } -type ToolUiid string - const ( - ESLint ToolUiid = "f8b29663-2cb2-498d-b923-a10c6a8c05cd" - Trivy ToolUiid = "2fd7fbe0-33f9-4ab3-ab73-e9b62404e2cb" - PMD ToolUiid = "9ed24812-b6ee-4a58-9004-0ed183c45b8f" - PyLint ToolUiid = "31677b6d-4ae0-4f56-8041-606a8d7a8e61" + ESLint string = "f8b29663-2cb2-498d-b923-a10c6a8c05cd" + Trivy string = "2fd7fbe0-33f9-4ab3-ab73-e9b62404e2cb" + PMD string = "9ed24812-b6ee-4a58-9004-0ed183c45b8f" + PyLint string = "31677b6d-4ae0-4f56-8041-606a8d7a8e61" ) diff --git a/domain/patternConfiguration.go b/domain/patternConfiguration.go index 039a9671..efec1ef1 100644 --- a/domain/patternConfiguration.go +++ b/domain/patternConfiguration.go @@ -6,8 +6,7 @@ type ParameterConfiguration struct { } type PatternDefinition struct { - Id string `json:"id"` - Parameters []ParameterConfiguration `json:"parameters"` + Id string `json:"id"` } type PatternConfiguration struct { diff --git a/domain/token.go b/domain/token.go deleted file mode 100644 index bb7b614c..00000000 --- a/domain/token.go +++ /dev/null @@ -1,29 +0,0 @@ -package domain - -type Token interface { - Value() string -} - -type ProjectToken struct { - value string -} - -func (t ProjectToken) Value() string { - return t.value -} - -func NewProjectToken(value string) ProjectToken { - return ProjectToken{value: value} -} - -type ApiToken struct { - value string -} - -func (t ApiToken) Value() string { - return t.value -} - -func NewApiToken(value string) ApiToken { - return ApiToken{value: value} -} diff --git a/domain/toolName.go b/domain/toolName.go deleted file mode 100644 index a0dbe51f..00000000 --- a/domain/toolName.go +++ /dev/null @@ -1,9 +0,0 @@ -package domain - -type ToolName string - -const ( - ESLint ToolName = "eslint" - Trivy ToolName = "trivy" - PMD ToolName = "pmd" -) diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 00000000..3be26f47 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,10 @@ +export default [ + { + rules: { + "@angular-eslint/component-class-suffix": "error", + "@angular-eslint/no-async-lifecycle-method": "error", + "@angular-eslint/no-attribute-decorator": "error", + "unicorn/better-regex": "error", + } + } +]; \ No newline at end of file diff --git a/pmd-ruleset.xml b/pmd-ruleset.xml new file mode 100644 index 00000000..48f981f4 --- /dev/null +++ b/pmd-ruleset.xml @@ -0,0 +1,101 @@ + + + + Default PMD ruleset with commonly used rules + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tools/eslintConfigCreator.go b/tools/eslintConfigCreator.go index 72f1a3c4..6b3f472b 100644 --- a/tools/eslintConfigCreator.go +++ b/tools/eslintConfigCreator.go @@ -27,7 +27,6 @@ func CreateEslintConfig(configuration []domain.PatternConfiguration) string { for _, patternConfiguration := range configuration { rule := strings.TrimPrefix(patternConfiguration.PatternDefinition.Id, "ESLint8_") - fmt.Println("Rule:", rule) const tempstring = "TEMPORARYSTRING" rule = strings.ReplaceAll(rule, "__", tempstring) diff --git a/tools/eslintConfigCreator_test.go b/tools/eslintConfigCreator_test.go index 809d50ce..d1739b8c 100644 --- a/tools/eslintConfigCreator_test.go +++ b/tools/eslintConfigCreator_test.go @@ -1,19 +1,20 @@ package tools import ( + "codacy/cli-v2/domain" "testing" "github.com/stretchr/testify/assert" ) -func testConfig(t *testing.T, configuration ToolConfiguration, expected string) { +func testConfig(t *testing.T, configuration []domain.PatternConfiguration, expected string) { actual := CreateEslintConfig(configuration) assert.Equal(t, expected, actual) } func TestCreateEslintConfigEmptyConfig(t *testing.T) { testConfig(t, - ToolConfiguration{}, + []domain.PatternConfiguration{}, `export default [ { rules: { @@ -24,10 +25,10 @@ func TestCreateEslintConfigEmptyConfig(t *testing.T) { func TestCreateEslintConfigConfig1(t *testing.T) { testConfig(t, - ToolConfiguration{ - PatternsConfiguration: []PatternConfiguration{ - { - PatternId: "ESLint8_semi", + []domain.PatternConfiguration{ + { + PatternDefinition: domain.PatternDefinition{ + Id: "ESLint8_semi", }, }, }, @@ -42,15 +43,15 @@ func TestCreateEslintConfigConfig1(t *testing.T) { func TestCreateEslintConfigUnnamedParam(t *testing.T) { testConfig(t, - ToolConfiguration{ - PatternsConfiguration: []PatternConfiguration{ - { - PatternId: "ESLint8_semi", - ParameterConfigurations: []PatternParameterConfiguration{ - { - Name: "unnamedParam", - Value: "never", - }, + []domain.PatternConfiguration{ + { + PatternDefinition: domain.PatternDefinition{ + Id: "ESLint8_semi", + }, + Parameters: []domain.ParameterConfiguration{ + { + Name: "unnamedParam", + Value: "never", }, }, }, @@ -66,15 +67,15 @@ func TestCreateEslintConfigUnnamedParam(t *testing.T) { func TestCreateEslintConfigNamedParam(t *testing.T) { testConfig(t, - ToolConfiguration{ - PatternsConfiguration: []PatternConfiguration{ - { - PatternId: "consistent-return", - ParameterConfigurations: []PatternParameterConfiguration{ - { - Name: "treatUndefinedAsUnspecified", - Value: "false", - }, + []domain.PatternConfiguration{ + { + PatternDefinition: domain.PatternDefinition{ + Id: "consistent-return", + }, + Parameters: []domain.ParameterConfiguration{ + { + Name: "treatUndefinedAsUnspecified", + Value: "false", }, }, }, @@ -90,19 +91,19 @@ func TestCreateEslintConfigNamedParam(t *testing.T) { func TestCreateEslintConfigUnnamedAndNamedParam(t *testing.T) { testConfig(t, - ToolConfiguration{ - PatternsConfiguration: []PatternConfiguration{ - { - PatternId: "consistent-return", - ParameterConfigurations: []PatternParameterConfiguration{ - { - Name: "treatUndefinedAsUnspecified", - Value: "false", - }, - { - Name: "unnamedParam", - Value: "foo", - }, + []domain.PatternConfiguration{ + { + PatternDefinition: domain.PatternDefinition{ + Id: "consistent-return", + }, + Parameters: []domain.ParameterConfiguration{ + { + Name: "treatUndefinedAsUnspecified", + Value: "false", + }, + { + Name: "unnamedParam", + Value: "foo", }, }, }, @@ -118,10 +119,10 @@ func TestCreateEslintConfigUnnamedAndNamedParam(t *testing.T) { func TestCreateEslintConfigSupportPlugins(t *testing.T) { testConfig(t, - ToolConfiguration{ - PatternsConfiguration: []PatternConfiguration{ - { - PatternId: "plugin/consistent-return", + []domain.PatternConfiguration{ + { + PatternDefinition: domain.PatternDefinition{ + Id: "plugin/consistent-return", }, }, }, diff --git a/tools/getTools.go b/tools/getTools.go index 759ed3c2..6433c563 100644 --- a/tools/getTools.go +++ b/tools/getTools.go @@ -1,26 +1,27 @@ package tools import ( - "codacy/cli-v2/domain" + "codacy/cli-v2/config" "encoding/json" "errors" "fmt" "io" "net/http" + "strings" "time" ) -func GetRepositoryTools(apiToken domain.ApiToken, provider string, organization string, repository string) ([]Tool, error) { +func GetRepositoryTools(codacyBase string, apiToken string, provider string, organization string, repository string) ([]Tool, error) { client := &http.Client{ Timeout: 10 * time.Second, } - url := fmt.Sprintf("https://api.codacy.com/api/v3/analysis/organizations/%s/%s/repositories/%s/tools", + url := fmt.Sprintf("%s/api/v3/analysis/organizations/%s/%s/repositories/%s/tools", + codacyBase, provider, organization, repository) - fmt.Println("url", url) // Create a new GET request req, err := http.NewRequest("GET", url, nil) if err != nil { @@ -29,7 +30,7 @@ func GetRepositoryTools(apiToken domain.ApiToken, provider string, organization } // Set the headers - req.Header.Set("api-token", apiToken.Value()) + req.Header.Set("api-token", apiToken) // Send the request resp, err := client.Do(req) @@ -57,11 +58,15 @@ func GetRepositoryTools(apiToken domain.ApiToken, provider string, organization return nil, err } + cliSupportedTools := config.Config.Tools() + // Filter enabled tools var enabledTools []Tool for _, tool := range response.Data { if tool.Settings.Enabled { - enabledTools = append(enabledTools, tool) + if _, exists := cliSupportedTools[strings.ToLower(tool.Name)]; exists { + enabledTools = append(enabledTools, tool) + } } } diff --git a/tools/getTools_test.go b/tools/getTools_test.go deleted file mode 100644 index a1ba85cc..00000000 --- a/tools/getTools_test.go +++ /dev/null @@ -1,19 +0,0 @@ -package tools - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestGetTools(t *testing.T) { - obtained, err := GetTools() - - assert.Nil(t, err) - - assert.Contains(t, obtained, Tool{ - Uuid: "f8b29663-2cb2-498d-b923-a10c6a8c05cd", - Name: "ESLint", - Version: "8.57.0", - }) -} diff --git a/tools/pmdConfigCreator_test.go b/tools/pmdConfigCreator_test.go index b53175b0..12004c89 100644 --- a/tools/pmdConfigCreator_test.go +++ b/tools/pmdConfigCreator_test.go @@ -7,6 +7,8 @@ import ( "strings" "testing" + "codacy/cli-v2/domain" + "github.com/stretchr/testify/assert" ) @@ -37,37 +39,41 @@ type PMDProperty struct { func TestCreatePmdConfig(t *testing.T) { // Setup test configuration with patterns - config := ToolConfiguration{ - PatternsConfiguration: []PatternConfiguration{ - { - PatternId: "java/codestyle/AtLeastOneConstructor", - ParameterConfigurations: []PatternParameterConfiguration{ - { - Name: "enabled", - Value: "true", - }, + config := []domain.PatternConfiguration{ + { + PatternDefinition: domain.PatternDefinition{ + Id: "java/codestyle/AtLeastOneConstructor", + }, + Parameters: []domain.ParameterConfiguration{ + { + Name: "enabled", + Value: "true", }, }, - { - PatternId: "java/design/UnusedPrivateField", - ParameterConfigurations: []PatternParameterConfiguration{ - { - Name: "enabled", - Value: "true", - }, + }, + { + PatternDefinition: domain.PatternDefinition{ + Id: "java/design/UnusedPrivateField", + }, + Parameters: []domain.ParameterConfiguration{ + { + Name: "enabled", + Value: "true", }, }, - { - PatternId: "java/design/LoosePackageCoupling", - ParameterConfigurations: []PatternParameterConfiguration{ - { - Name: "enabled", - Value: "true", - }, - { - Name: "packages", - Value: "java.util,java.io", - }, + }, + { + PatternDefinition: domain.PatternDefinition{ + Id: "java/design/LoosePackageCoupling", + }, + Parameters: []domain.ParameterConfiguration{ + { + Name: "enabled", + Value: "true", + }, + { + Name: "packages", + Value: "java.util,java.io", }, }, }, @@ -105,15 +111,15 @@ func TestCreatePmdConfig(t *testing.T) { } func TestCreatePmdConfigWithDisabledRules(t *testing.T) { - config := ToolConfiguration{ - PatternsConfiguration: []PatternConfiguration{ - { - PatternId: "java/codestyle/AtLeastOneConstructor", - ParameterConfigurations: []PatternParameterConfiguration{ - { - Name: "enabled", - Value: "false", - }, + config := []domain.PatternConfiguration{ + { + PatternDefinition: domain.PatternDefinition{ + Id: "java/codestyle/AtLeastOneConstructor", + }, + Parameters: []domain.ParameterConfiguration{ + { + Name: "enabled", + Value: "false", }, }, }, @@ -163,9 +169,7 @@ func TestCreatePmdConfigEmpty(t *testing.T) { } defer os.Chdir(cwd) - config := ToolConfiguration{ - PatternsConfiguration: []PatternConfiguration{}, - } + config := []domain.PatternConfiguration{} obtainedConfig := CreatePmdConfig(config) diff --git a/tools/trivyConfigCreator_test.go b/tools/trivyConfigCreator_test.go index f238056e..3c8d78d4 100644 --- a/tools/trivyConfigCreator_test.go +++ b/tools/trivyConfigCreator_test.go @@ -1,19 +1,20 @@ package tools import ( + "codacy/cli-v2/domain" "testing" "github.com/stretchr/testify/assert" ) -func testTrivyConfig(t *testing.T, configuration ToolConfiguration, expected string) { +func testTrivyConfig(t *testing.T, configuration []domain.PatternConfiguration, expected string) { actual := CreateTrivyConfig(configuration) assert.Equal(t, expected, actual) } func TestCreateTrivyConfigEmptyConfig(t *testing.T) { testTrivyConfig(t, - ToolConfiguration{}, + []domain.PatternConfiguration{}, `severity: - LOW - MEDIUM @@ -29,42 +30,48 @@ scan: func TestCreateTrivyConfigAllEnabled(t *testing.T) { testTrivyConfig(t, - ToolConfiguration{ - PatternsConfiguration: []PatternConfiguration{ - { - PatternId: "Trivy_vulnerability_minor", - ParameterConfigurations: []PatternParameterConfiguration{ - { - Name: "enabled", - Value: "true", - }, + []domain.PatternConfiguration{ + { + PatternDefinition: domain.PatternDefinition{ + Id: "Trivy_vulnerability_minor", + }, + Parameters: []domain.ParameterConfiguration{ + { + Name: "enabled", + Value: "true", }, }, - { - PatternId: "Trivy_vulnerability_medium", - ParameterConfigurations: []PatternParameterConfiguration{ - { - Name: "enabled", - Value: "true", - }, + }, + { + PatternDefinition: domain.PatternDefinition{ + Id: "Trivy_vulnerability_medium", + }, + Parameters: []domain.ParameterConfiguration{ + { + Name: "enabled", + Value: "true", }, }, - { - PatternId: "Trivy_vulnerability", - ParameterConfigurations: []PatternParameterConfiguration{ - { - Name: "enabled", - Value: "true", - }, + }, + { + PatternDefinition: domain.PatternDefinition{ + Id: "Trivy_vulnerability", + }, + Parameters: []domain.ParameterConfiguration{ + { + Name: "enabled", + Value: "true", }, }, - { - PatternId: "Trivy_secret", - ParameterConfigurations: []PatternParameterConfiguration{ - { - Name: "enabled", - Value: "true", - }, + }, + { + PatternDefinition: domain.PatternDefinition{ + Id: "Trivy_secret", + }, + Parameters: []domain.ParameterConfiguration{ + { + Name: "enabled", + Value: "true", }, }, }, @@ -84,15 +91,15 @@ scan: func TestCreateTrivyConfigNoLow(t *testing.T) { testTrivyConfig(t, - ToolConfiguration{ - PatternsConfiguration: []PatternConfiguration{ - { - PatternId: "Trivy_vulnerability_minor", - ParameterConfigurations: []PatternParameterConfiguration{ - { - Name: "enabled", - Value: "false", - }, + []domain.PatternConfiguration{ + { + PatternDefinition: domain.PatternDefinition{ + Id: "Trivy_vulnerability_minor", + }, + Parameters: []domain.ParameterConfiguration{ + { + Name: "enabled", + Value: "false", }, }, }, @@ -111,33 +118,37 @@ scan: func TestCreateTrivyConfigOnlyHigh(t *testing.T) { testTrivyConfig(t, - ToolConfiguration{ - PatternsConfiguration: []PatternConfiguration{ - { - PatternId: "Trivy_vulnerability_minor", - ParameterConfigurations: []PatternParameterConfiguration{ - { - Name: "enabled", - Value: "false", - }, + []domain.PatternConfiguration{ + { + PatternDefinition: domain.PatternDefinition{ + Id: "Trivy_vulnerability_minor", + }, + Parameters: []domain.ParameterConfiguration{ + { + Name: "enabled", + Value: "false", }, }, - { - PatternId: "Trivy_vulnerability_medium", - ParameterConfigurations: []PatternParameterConfiguration{ - { - Name: "enabled", - Value: "false", - }, + }, + { + PatternDefinition: domain.PatternDefinition{ + Id: "Trivy_vulnerability_medium", + }, + Parameters: []domain.ParameterConfiguration{ + { + Name: "enabled", + Value: "false", }, }, - { - PatternId: "Trivy_secret", - ParameterConfigurations: []PatternParameterConfiguration{ - { - Name: "enabled", - Value: "false", - }, + }, + { + PatternDefinition: domain.PatternDefinition{ + Id: "Trivy_secret", + }, + Parameters: []domain.ParameterConfiguration{ + { + Name: "enabled", + Value: "false", }, }, }, @@ -154,33 +165,37 @@ scan: func TestCreateTrivyConfigNoVulnerabilities(t *testing.T) { testTrivyConfig(t, - ToolConfiguration{ - PatternsConfiguration: []PatternConfiguration{ - { - PatternId: "Trivy_vulnerability_minor", - ParameterConfigurations: []PatternParameterConfiguration{ - { - Name: "enabled", - Value: "false", - }, + []domain.PatternConfiguration{ + { + PatternDefinition: domain.PatternDefinition{ + Id: "Trivy_vulnerability_minor", + }, + Parameters: []domain.ParameterConfiguration{ + { + Name: "enabled", + Value: "false", }, }, - { - PatternId: "Trivy_vulnerability_medium", - ParameterConfigurations: []PatternParameterConfiguration{ - { - Name: "enabled", - Value: "false", - }, + }, + { + PatternDefinition: domain.PatternDefinition{ + Id: "Trivy_vulnerability_medium", + }, + Parameters: []domain.ParameterConfiguration{ + { + Name: "enabled", + Value: "false", }, }, - { - PatternId: "Trivy_vulnerability", - ParameterConfigurations: []PatternParameterConfiguration{ - { - Name: "enabled", - Value: "false", - }, + }, + { + PatternDefinition: domain.PatternDefinition{ + Id: "Trivy_vulnerability", + }, + Parameters: []domain.ParameterConfiguration{ + { + Name: "enabled", + Value: "false", }, }, }, @@ -196,33 +211,37 @@ scan: func TestCreateTrivyConfigOnlySecretsLow(t *testing.T) { testTrivyConfig(t, - ToolConfiguration{ - PatternsConfiguration: []PatternConfiguration{ - { - PatternId: "Trivy_vulnerability_minor", - ParameterConfigurations: []PatternParameterConfiguration{ - { - Name: "enabled", - Value: "true", - }, + []domain.PatternConfiguration{ + { + PatternDefinition: domain.PatternDefinition{ + Id: "Trivy_vulnerability_minor", + }, + Parameters: []domain.ParameterConfiguration{ + { + Name: "enabled", + Value: "true", }, }, - { - PatternId: "Trivy_vulnerability_medium", - ParameterConfigurations: []PatternParameterConfiguration{ - { - Name: "enabled", - Value: "false", - }, + }, + { + PatternDefinition: domain.PatternDefinition{ + Id: "Trivy_vulnerability_medium", + }, + Parameters: []domain.ParameterConfiguration{ + { + Name: "enabled", + Value: "false", }, }, - { - PatternId: "Trivy_vulnerability", - ParameterConfigurations: []PatternParameterConfiguration{ - { - Name: "enabled", - Value: "false", - }, + }, + { + PatternDefinition: domain.PatternDefinition{ + Id: "Trivy_vulnerability", + }, + Parameters: []domain.ParameterConfiguration{ + { + Name: "enabled", + Value: "false", }, }, }, diff --git a/trivy.yaml b/trivy.yaml new file mode 100644 index 00000000..c785541c --- /dev/null +++ b/trivy.yaml @@ -0,0 +1,10 @@ +severity: + - LOW + - MEDIUM + - HIGH + - CRITICAL + +scan: + scanners: + - vuln + - secret From 26ba75cc7232a5919e9ee427065de01267903dff Mon Sep 17 00:00:00 2001 From: franciscoazevedo Date: Wed, 9 Apr 2025 09:54:32 +0100 Subject: [PATCH 4/7] merged main with success --- .codacy/codacy.yaml | 8 ------- eslint.config.mjs | 10 --------- tools/getTools.go | 51 ++++++++++++++++++++++++++++++++++++++++++++- trivy.yaml | 10 --------- 4 files changed, 50 insertions(+), 29 deletions(-) delete mode 100644 .codacy/codacy.yaml delete mode 100644 eslint.config.mjs delete mode 100644 trivy.yaml diff --git a/.codacy/codacy.yaml b/.codacy/codacy.yaml deleted file mode 100644 index 9092e0a0..00000000 --- a/.codacy/codacy.yaml +++ /dev/null @@ -1,8 +0,0 @@ -runtimes: - - node@22.2.0 - - python@3.11.11 -tools: - - eslint@ - - trivy@ - - pylint@3.3.6 - - pmd@ diff --git a/eslint.config.mjs b/eslint.config.mjs deleted file mode 100644 index 3be26f47..00000000 --- a/eslint.config.mjs +++ /dev/null @@ -1,10 +0,0 @@ -export default [ - { - rules: { - "@angular-eslint/component-class-suffix": "error", - "@angular-eslint/no-async-lifecycle-method": "error", - "@angular-eslint/no-attribute-decorator": "error", - "unicorn/better-regex": "error", - } - } -]; \ No newline at end of file diff --git a/tools/getTools.go b/tools/getTools.go index 6433c563..1ed84d58 100644 --- a/tools/getTools.go +++ b/tools/getTools.go @@ -11,6 +11,55 @@ import ( "time" ) +func enrichToolsWithVersion(tools []Tool) ([]Tool, error) { + client := &http.Client{ + Timeout: 10 * time.Second, + } + + // Create a new GET request + req, err := http.NewRequest("GET", "https://api.codacy.com/api/v3/tools", nil) + if err != nil { + return nil, fmt.Errorf("failed to create request: %w", err) + } + + // Send the request + resp, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("failed to send request: %w", err) + } + defer resp.Body.Close() + + if resp.StatusCode != 200 { + return nil, fmt.Errorf("failed to get tools from Codacy API: status code %d", resp.StatusCode) + } + + // Read the response body + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("failed to read response body: %w", err) + } + + var response ToolsResponse + if err := json.Unmarshal(body, &response); err != nil { + return nil, fmt.Errorf("failed to unmarshal response: %w", err) + } + + // Create a map of tool UUIDs to versions + versionMap := make(map[string]string) + for _, tool := range response.Data { + versionMap[tool.Uuid] = tool.Version + } + + // Enrich the input tools with versions + for i, tool := range tools { + if version, exists := versionMap[tool.Uuid]; exists { + tools[i].Version = version + } + } + + return tools, nil +} + func GetRepositoryTools(codacyBase string, apiToken string, provider string, organization string, repository string) ([]Tool, error) { client := &http.Client{ Timeout: 10 * time.Second, @@ -70,7 +119,7 @@ func GetRepositoryTools(codacyBase string, apiToken string, provider string, org } } - return enabledTools, nil + return enrichToolsWithVersion(enabledTools) } type ToolsResponse struct { diff --git a/trivy.yaml b/trivy.yaml deleted file mode 100644 index c785541c..00000000 --- a/trivy.yaml +++ /dev/null @@ -1,10 +0,0 @@ -severity: - - LOW - - MEDIUM - - HIGH - - CRITICAL - -scan: - scanners: - - vuln - - secret From b3932f58d6f34507c1907e68ca08f1af1282197e Mon Sep 17 00:00:00 2001 From: franciscoazevedo Date: Wed, 9 Apr 2025 10:06:08 +0100 Subject: [PATCH 5/7] merge issues fixed --- .codacy/codacy.yaml | 8 ++++++++ cmd/init.go | 32 ++++++++++++++++++-------------- 2 files changed, 26 insertions(+), 14 deletions(-) create mode 100644 .codacy/codacy.yaml diff --git a/.codacy/codacy.yaml b/.codacy/codacy.yaml new file mode 100644 index 00000000..cc5bd6b6 --- /dev/null +++ b/.codacy/codacy.yaml @@ -0,0 +1,8 @@ +runtimes: + - node@22.2.0 + - python@3.11.11 +tools: + - eslint@8.57.0 + - trivy@0.59.1 + - pylint@3.3.6 + - pmd@6.55.0 diff --git a/cmd/init.go b/cmd/init.go index cd023c51..9b649a1b 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -20,16 +20,20 @@ import ( const CodacyApiBase = "https://app.codacy.com" -var codacyApiToken string -var remoteProvider string -var organization string -var remoteRepo string +type InitFlags struct { + apiToken string + provider string + organization string + repository string +} + +var initFlags InitFlags func init() { - initCmd.Flags().StringVar(&codacyApiToken, "api-token", "", "optional codacy api token, if defined configurations will be fetched from codacy") - initCmd.Flags().StringVar(&remoteProvider, "provider", "", "optional provider (gh/bb/gl), if defined configurations will be fetched from codacy") - initCmd.Flags().StringVar(&organization, "organization", "", "optional remote organization name, if defined configurations will be fetched from codacy") - initCmd.Flags().StringVar(&remoteRepo, "repository", "", "optional remote repository name, if defined configurations will be fetched from codacy") + initCmd.Flags().StringVar(&initFlags.apiToken, "api-token", "", "optional codacy api token, if defined configurations will be fetched from codacy") + initCmd.Flags().StringVar(&initFlags.provider, "provider", "", "optional provider (gh/bb/gl), if defined configurations will be fetched from codacy") + initCmd.Flags().StringVar(&initFlags.organization, "organization", "", "optional remote organization name, if defined configurations will be fetched from codacy") + initCmd.Flags().StringVar(&initFlags.repository, "repository", "", "optional remote repository name, if defined configurations will be fetched from codacy") rootCmd.AddCommand(initCmd) } @@ -40,7 +44,7 @@ var initCmd = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { config.Config.CreateLocalCodacyDir() - cliLocalMode := len(codacyApiToken) == 0 + cliLocalMode := len(initFlags.apiToken) == 0 if cliLocalMode { fmt.Println() @@ -51,7 +55,7 @@ var initCmd = &cobra.Command{ log.Fatal(err) } } else { - err := buildRepositoryConfigurationFiles(codacyApiToken) + err := buildRepositoryConfigurationFiles(initFlags.apiToken) if err != nil { log.Fatal(err) } @@ -171,7 +175,7 @@ func buildRepositoryConfigurationFiles(token string) error { Timeout: 10 * time.Second, } - apiTools, err := tools.GetRepositoryTools(CodacyApiBase, token, remoteProvider, organization, remoteRepo) + apiTools, err := tools.GetRepositoryTools(CodacyApiBase, token, initFlags.provider, initFlags.organization, initFlags.repository) if err != nil { return err } @@ -184,9 +188,9 @@ func buildRepositoryConfigurationFiles(token string) error { for _, tool := range apiTools { url := fmt.Sprintf("%s/api/v3/analysis/organizations/%s/%s/repositories/%s/tools/%s/patterns?enabled=true", CodacyApiBase, - remoteProvider, - organization, - remoteRepo, + initFlags.provider, + initFlags.organization, + initFlags.repository, tool.Uuid) // Create a new GET request From b4bfe46f741001b9c935675c851a5bb069a87dc1 Mon Sep 17 00:00:00 2001 From: franciscoazevedo Date: Wed, 9 Apr 2025 11:32:00 +0100 Subject: [PATCH 6/7] fixed problem with initializition due to tools processing --- .codacy/codacy.yaml | 2 +- cmd/init.go | 31 +++++++++++++++++++--------- plugins/tool-utils.go | 31 ++++++++++++++++++++++++++++ plugins/tool-utils_test.go | 42 ++++++++++++++++++++++++++++++++++++++ tools/getTools.go | 9 +++++--- 5 files changed, 101 insertions(+), 14 deletions(-) diff --git a/.codacy/codacy.yaml b/.codacy/codacy.yaml index cc5bd6b6..820f105f 100644 --- a/.codacy/codacy.yaml +++ b/.codacy/codacy.yaml @@ -2,7 +2,7 @@ runtimes: - node@22.2.0 - python@3.11.11 tools: - - eslint@8.57.0 + - eslint@9.3.0 - trivy@0.59.1 - pylint@3.3.6 - pmd@6.55.0 diff --git a/cmd/init.go b/cmd/init.go index 9b649a1b..91957442 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -42,7 +42,16 @@ var initCmd = &cobra.Command{ Short: "Bootstraps project configuration", Long: "Bootstraps project configuration, creates codacy configuration file", Run: func(cmd *cobra.Command, args []string) { - config.Config.CreateLocalCodacyDir() + // Create local codacy directory first + if err := config.Config.CreateLocalCodacyDir(); err != nil { + log.Fatalf("Failed to create local codacy directory: %v", err) + } + + // Create tools-configs directory + toolsConfigDir := config.Config.ToolsConfigDirectory() + if err := os.MkdirAll(toolsConfigDir, 0777); err != nil { + log.Fatalf("Failed to create tools-configs directory: %v", err) + } cliLocalMode := len(initFlags.apiToken) == 0 @@ -93,25 +102,27 @@ cli-config.yaml func createConfigurationFiles(tools []tools.Tool, cliLocalMode bool) error { configFile, err := os.Create(config.Config.ProjectConfigFile()) - defer configFile.Close() if err != nil { - log.Fatal(err) + return fmt.Errorf("failed to create project config file: %w", err) } + defer configFile.Close() - _, err = configFile.WriteString(configFileTemplate(tools)) + configContent := configFileTemplate(tools) + _, err = configFile.WriteString(configContent) if err != nil { - log.Fatal(err) + return fmt.Errorf("failed to write project config file: %w", err) } cliConfigFile, err := os.Create(config.Config.CliConfigFile()) - defer cliConfigFile.Close() if err != nil { - log.Fatal(err) + return fmt.Errorf("failed to create CLI config file: %w", err) } + defer cliConfigFile.Close() - _, err = cliConfigFile.WriteString(cliConfigFileTemplate(cliLocalMode)) + cliConfigContent := cliConfigFileTemplate(cliLocalMode) + _, err = cliConfigFile.WriteString(cliConfigContent) if err != nil { - log.Fatal(err) + return fmt.Errorf("failed to write CLI config file: %w", err) } return nil @@ -162,7 +173,7 @@ func cliConfigFileTemplate(cliLocalMode bool) string { } func buildRepositoryConfigurationFiles(token string) error { - fmt.Println("Fetching repository configuration from codacy using api token ...") + fmt.Println("Fetching repository configuration from codacy ...") toolsConfigDir := config.Config.ToolsConfigDirectory() diff --git a/plugins/tool-utils.go b/plugins/tool-utils.go index b2731d62..c96c8b87 100644 --- a/plugins/tool-utils.go +++ b/plugins/tool-utils.go @@ -294,3 +294,34 @@ func getDownloadURL(urlTemplate string, fileName string, version string, mappedA return buf.String() } + +// GetSupportedTools returns a map of supported tool names based on the tools folder +func GetSupportedTools() (map[string]struct{}, error) { + supportedTools := make(map[string]struct{}) + + // Read all directories in the tools folder + entries, err := toolsFS.ReadDir("tools") + if err != nil { + return nil, fmt.Errorf("failed to read tools directory: %w", err) + } + + // For each directory, check if it has a plugin.yaml file + for _, entry := range entries { + if !entry.IsDir() { + continue + } + + toolName := entry.Name() + pluginPath := filepath.Join("tools", toolName, "plugin.yaml") + + // Check if plugin.yaml exists + _, err := toolsFS.ReadFile(pluginPath) + if err != nil { + continue // Skip if no plugin.yaml + } + + supportedTools[toolName] = struct{}{} + } + + return supportedTools, nil +} diff --git a/plugins/tool-utils_test.go b/plugins/tool-utils_test.go index a2e90caf..1b931b22 100644 --- a/plugins/tool-utils_test.go +++ b/plugins/tool-utils_test.go @@ -152,3 +152,45 @@ func TestProcessToolsWithDownload(t *testing.T) { } assert.Contains(t, trivyInfo.DownloadURL, expectedArch) } + +func TestGetSupportedTools(t *testing.T) { + tests := []struct { + name string + expectedTools []string + expectedError bool + }{ + { + name: "should return supported tools", + expectedTools: []string{ + "eslint", + "pmd", + "pylint", + "trivy", + }, + expectedError: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + supportedTools, err := GetSupportedTools() + + if tt.expectedError { + assert.Error(t, err) + return + } + + assert.NoError(t, err) + assert.NotNil(t, supportedTools) + + // Check that all expected tools are supported + for _, expectedTool := range tt.expectedTools { + _, exists := supportedTools[expectedTool] + assert.True(t, exists, "tool %s should be supported", expectedTool) + } + + // Check that we have exactly the expected number of tools + assert.Equal(t, len(tt.expectedTools), len(supportedTools), "number of supported tools should match") + }) + } +} diff --git a/tools/getTools.go b/tools/getTools.go index 1ed84d58..5b376fe0 100644 --- a/tools/getTools.go +++ b/tools/getTools.go @@ -1,7 +1,7 @@ package tools import ( - "codacy/cli-v2/config" + "codacy/cli-v2/plugins" "encoding/json" "errors" "fmt" @@ -107,13 +107,16 @@ func GetRepositoryTools(codacyBase string, apiToken string, provider string, org return nil, err } - cliSupportedTools := config.Config.Tools() + supportedTools, err := plugins.GetSupportedTools() + if err != nil { + return nil, err + } // Filter enabled tools var enabledTools []Tool for _, tool := range response.Data { if tool.Settings.Enabled { - if _, exists := cliSupportedTools[strings.ToLower(tool.Name)]; exists { + if _, exists := supportedTools[strings.ToLower(tool.Name)]; exists { enabledTools = append(enabledTools, tool) } } From eff63cfab46ad0039d04178f89db2a17e5cc45c7 Mon Sep 17 00:00:00 2001 From: franciscoazevedo Date: Wed, 9 Apr 2025 11:40:37 +0100 Subject: [PATCH 7/7] deleted pmd-ruleset --- pmd-ruleset.xml | 101 ------------------------------------------------ 1 file changed, 101 deletions(-) delete mode 100644 pmd-ruleset.xml diff --git a/pmd-ruleset.xml b/pmd-ruleset.xml deleted file mode 100644 index 48f981f4..00000000 --- a/pmd-ruleset.xml +++ /dev/null @@ -1,101 +0,0 @@ - - - - Default PMD ruleset with commonly used rules - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file