diff --git a/.gitignore b/.gitignore index 68457ff..f2b955a 100644 --- a/.gitignore +++ b/.gitignore @@ -27,8 +27,9 @@ go.work.sum # Codacy CLI cli-v2 codacy-cli -**/.codacy/logs/ -.codacy/ +.codacy/* +!.codacy/codacy.yaml +.github/instructions/ #Ignore cursor AI rules diff --git a/cmd/analyze.go b/cmd/analyze.go index 57b17e8..0fa7161 100644 --- a/cmd/analyze.go +++ b/cmd/analyze.go @@ -245,7 +245,7 @@ func loadsToolAndPatterns(toolName string, onlyEnabledPatterns bool) (domain.Too } var tool domain.Tool for _, t := range toolsResponse { - if t.ShortName == toolName { + if t.Name == toolName { tool = t break } @@ -264,19 +264,19 @@ func getToolName(toolName string, version string) string { if toolName == "eslint" { switch majorVersion { case 7: - return "eslint" + return "ESLint (deprecated)" case 8: - return "eslint-8" + return "ESLint" case 9: - return "eslint-9" + return "ESLint9" } } else { if toolName == "pmd" { switch majorVersion { case 6: - return "pmd" + return "PMD" case 7: - return "pmd-7" + return "PMD7" } } } @@ -347,14 +347,40 @@ func checkIfConfigExistsAndIsNeeded(toolName string, cliLocalMode bool) error { } func runToolByName(toolName string, workDirectory string, pathsToCheck []string, autoFix bool, outputFile string, outputFormat string, tool *plugins.ToolInfo, runtime *plugins.RuntimeInfo, cliLocalMode bool) error { - err := checkIfConfigExistsAndIsNeeded(toolName, cliLocalMode) - if err != nil { - return err + var t *domain.Tool + var usesConfigurationFile = false + + //Check if the user is using the repository configuration file + // If the user doesn't provide init flags, we skip fetching repository tools + if initFlags != (domain.InitFlags{}) { + // Get the tool name with the right version e.g. ESLint9, PMD7, etc. + toolRightVersion := getToolName(toolName, tool.Version) + + // Get the repository tools to access tool settings + repositoryTools, _ := codacyclient.GetRepositoryTools(initFlags) + + // Find the matching tool in repositoryTools + for i, tool := range repositoryTools { + if tool.Name == toolRightVersion { + t = &repositoryTools[i] + usesConfigurationFile = t.Settings.UsesConfigurationFile + break + } + } } + + // If the user is not using configuration file from repository, we check if there's a local one + if !usesConfigurationFile { + err := checkIfConfigExistsAndIsNeeded(toolName, cliLocalMode) + if err != nil { + return err + } + } + switch toolName { case "eslint": binaryPath := runtime.Binaries[tool.Runtime] - return tools.RunEslint(workDirectory, tool.InstallDir, binaryPath, pathsToCheck, autoFix, outputFile, outputFormat) + return tools.RunEslint(workDirectory, tool.InstallDir, binaryPath, pathsToCheck, autoFix, outputFile, outputFormat, usesConfigurationFile) case "trivy": binaryPath := tool.Binaries[toolName] return tools.RunTrivy(workDirectory, binaryPath, pathsToCheck, outputFile, outputFormat) diff --git a/cmd/analyze_test.go b/cmd/analyze_test.go index b4bd3aa..636ea55 100644 --- a/cmd/analyze_test.go +++ b/cmd/analyze_test.go @@ -64,14 +64,14 @@ func TestGetToolName(t *testing.T) { expected string }{ // ESLint cases - {"eslint v7", "eslint", "7.32.0", "eslint"}, - {"eslint v8", "eslint", "8.15.0", "eslint-8"}, - {"eslint v9", "eslint", "9.1.0", "eslint-9"}, + {"eslint v7", "eslint", "7.32.0", "ESLint (deprecated)"}, + {"eslint v8", "eslint", "8.15.0", "ESLint"}, + {"eslint v9", "eslint", "9.1.0", "ESLint9"}, {"eslint unknown version", "eslint", "10.0.0", "eslint"}, // PMD cases - {"pmd v6", "pmd", "6.55.0", "pmd"}, - {"pmd v7", "pmd", "7.0.0", "pmd-7"}, + {"pmd v6", "pmd", "6.55.0", "PMD"}, + {"pmd v7", "pmd", "7.0.0", "PMD7"}, {"pmd unknown version", "pmd", "8.0.0", "pmd"}, // Other tools should remain unchanged diff --git a/cmd/configsetup/repository_config.go b/cmd/configsetup/repository_config.go index 4511114..5b7099d 100644 --- a/cmd/configsetup/repository_config.go +++ b/cmd/configsetup/repository_config.go @@ -91,7 +91,6 @@ func createToolConfigurationFiles(tools []domain.Tool, flags domain.InitFlags) e } // CreateToolConfigurationFile creates a configuration file for a single tool -// CreateToolConfigurationFile generates a configuration file for a single tool. func CreateToolConfigurationFile(toolName string, flags domain.InitFlags) error { // Find the tool UUID by tool name toolUUID := getToolUUIDByName(toolName) diff --git a/tools/eslintRunner.go b/tools/eslintRunner.go index 840d5a9..07861f8 100644 --- a/tools/eslintRunner.go +++ b/tools/eslintRunner.go @@ -11,19 +11,21 @@ import ( // * Run from the root of the repo we want to analyse // * NODE_PATH="/node_modules" // * The local installed ESLint should have the @microsoft/eslint-formatter-sarif installed -func RunEslint(repositoryToAnalyseDirectory string, eslintInstallationDirectory string, nodeBinary string, pathsToCheck []string, autoFix bool, outputFile string, outputFormat string) error { +func RunEslint(repositoryToAnalyseDirectory string, eslintInstallationDirectory string, nodeBinary string, pathsToCheck []string, autoFix bool, outputFile string, outputFormat string, usesConfigurationFile bool) error { eslintInstallationNodeModules := filepath.Join(eslintInstallationDirectory, "node_modules") eslintJsPath := filepath.Join(eslintInstallationNodeModules, ".bin", "eslint") cmd := exec.Command(nodeBinary, eslintJsPath) // Add config file from tools-configs directory if it exists - if configFile, exists := ConfigFileExists(config.Config, "eslint.config.mjs"); exists { - // For Eslint compatibility with version 8. - // https://eslint.org/docs/v8.x/use/configure/configuration-files-new - cmd.Env = append(cmd.Env, "ESLINT_USE_FLAT_CONFIG=true") + if !usesConfigurationFile { + if configFile, exists := ConfigFileExists(config.Config, "eslint.config.mjs"); exists { + // For Eslint compatibility with version 8. + // https://eslint.org/docs/v8.x/use/configure/configuration-files-new + cmd.Env = append(cmd.Env, "ESLINT_USE_FLAT_CONFIG=true") - cmd.Args = append(cmd.Args, "-c", configFile) + cmd.Args = append(cmd.Args, "-c", configFile) + } } if autoFix { diff --git a/tools/lizard/lizardRunner.go b/tools/lizard/lizardRunner.go index af95e4a..58ea057 100644 --- a/tools/lizard/lizardRunner.go +++ b/tools/lizard/lizardRunner.go @@ -7,11 +7,12 @@ import ( "codacy/cli-v2/domain" "codacy/cli-v2/tools" "codacy/cli-v2/utils/logger" - "github.com/sirupsen/logrus" "encoding/json" "fmt" "os" "os/exec" + + "github.com/sirupsen/logrus" ) // RunLizard runs the Lizard tool and returns any issues found @@ -21,7 +22,6 @@ func RunLizard(workDirectory string, binary string, files []string, outputFile s var patterns []domain.PatternDefinition var errConfigs error - if exists { // Configuration exists, read from file patterns, errConfigs = ReadConfig(configFile) @@ -35,11 +35,10 @@ func RunLizard(workDirectory string, binary string, files []string, outputFile s return fmt.Errorf("failed to fetch default patterns: %v", errConfigs) } } + if len(patterns) == 0 { return fmt.Errorf("no valid patterns found in configuration") } - - // Construct base command with lizard module args := []string{"-m", "lizard", "-V"} @@ -50,23 +49,20 @@ func RunLizard(workDirectory string, binary string, files []string, outputFile s args = append(args, ".") } - // For non-SARIF output, let Lizard handle file output directly if outputFormat != "sarif" && outputFile != "" { args = append(args, "-o", outputFile) } - // Run the command cmd := exec.Command(binary, args...) cmd.Dir = workDirectory - var err error var stderr bytes.Buffer - + cmd.Stderr = &stderr - + // For SARIF output, we need to capture and parse the output if outputFormat == "sarif" { var stdout bytes.Buffer diff --git a/tools/runnerUtils_test.go b/tools/runnerUtils_test.go index 706548e..7b0b522 100644 --- a/tools/runnerUtils_test.go +++ b/tools/runnerUtils_test.go @@ -35,7 +35,7 @@ func TestConfigFileExistsInToolsConfigDirectory(t *testing.T) { "Config path should be correctly formed relative path") } -func TestConfigFileExistsInRepositoryDirectory(t *testing.T) { +func TestConfigFilePrefersToolsConfigDirectory(t *testing.T) { // Create a test directory structure tempDir := t.TempDir() repoDir := filepath.Join(tempDir, "src") @@ -49,19 +49,24 @@ func TestConfigFileExistsInRepositoryDirectory(t *testing.T) { err := os.MkdirAll(configDir, 0755) assert.NoError(t, err, "Failed to create test directory structure") - // Create a test config file on the repository directory - existingConfigFile := filepath.Join(repoDir, "existing-config.yaml") - err = os.WriteFile(existingConfigFile, []byte("test content"), 0644) - assert.NoError(t, err, "Failed to create test config file") + // Create a test config file in both locations + generatedConfigFile := filepath.Join(configDir, "some-config.yaml") + existingConfigFile := filepath.Join(repoDir, "some-config.yaml") - // Test case: The existing config file gets picked up - configPath, exists := ConfigFileExists(config, "existing-config.yaml") - assert.True(t, exists, "Config file should exist in tools config directory") - assert.Equal(t, filepath.Join(config.RepositoryDirectory(), "existing-config.yaml"), configPath, - "Config path should be correctly formed relative path") + err = os.WriteFile(generatedConfigFile, []byte("tools config content"), 0644) + assert.NoError(t, err, "Failed to create test config file in tools config directory") + + err = os.WriteFile(existingConfigFile, []byte("repository config content"), 0644) + assert.NoError(t, err, "Failed to create test config file in repository directory") + + // Test case: Config file in tools config directory is preferred + configPath, exists := ConfigFileExists(config, "some-config.yaml") + assert.True(t, exists, "Config file should exist") + assert.Equal(t, filepath.Join(config.ToolsConfigDirectory(), "some-config.yaml"), configPath, + "Config path should prefer tools config directory") } -func TestConfigFilePrefersToolsConfigDirectory(t *testing.T) { +func TestConfigFileExistsInRepositoryDirectory(t *testing.T) { // Create a test directory structure tempDir := t.TempDir() repoDir := filepath.Join(tempDir, "src") @@ -75,21 +80,16 @@ func TestConfigFilePrefersToolsConfigDirectory(t *testing.T) { err := os.MkdirAll(configDir, 0755) assert.NoError(t, err, "Failed to create test directory structure") - // Create a test config file in both locations - generatedConfigFile := filepath.Join(configDir, "some-config.yaml") - existingConfigFile := filepath.Join(repoDir, "some-config.yaml") - - err = os.WriteFile(generatedConfigFile, []byte("tools config content"), 0644) - assert.NoError(t, err, "Failed to create test config file in tools config directory") - - err = os.WriteFile(existingConfigFile, []byte("repository config content"), 0644) - assert.NoError(t, err, "Failed to create test config file in repository directory") + // Create a test config file on the repository directory + existingConfigFile := filepath.Join(repoDir, "existing-config.yaml") + err = os.WriteFile(existingConfigFile, []byte("test content"), 0644) + assert.NoError(t, err, "Failed to create test config file") - // Test case: Config file in tools config directory is preferred - configPath, exists := ConfigFileExists(config, "some-config.yaml") - assert.True(t, exists, "Config file should exist") - assert.Equal(t, filepath.Join(config.ToolsConfigDirectory(), "some-config.yaml"), configPath, - "Config path should prefer tools config directory") + // Test case: The existing config file gets picked up + configPath, exists := ConfigFileExists(config, "existing-config.yaml") + assert.True(t, exists, "Config file should exist in tools config directory") + assert.Equal(t, filepath.Join(config.RepositoryDirectory(), "existing-config.yaml"), configPath, + "Config path should be correctly formed relative path") } func TestConfigFileDoesNotExist(t *testing.T) {