diff --git a/cmd/analyze.go b/cmd/analyze.go index 1ead970..bd9dff9 100644 --- a/cmd/analyze.go +++ b/cmd/analyze.go @@ -272,10 +272,10 @@ var versionedToolNames = map[string]map[int]string{ } var simpleToolAliases = map[string]string{ - "lizard": "Lizard", + "lizard": "Lizard", "opengrep": "Opengrep", - "pylint": "pylintpython3", - "trivy": "Trivy", + "pylint": "pylintpython3", + "trivy": "Trivy", } func getToolName(toolName string, version string) string { @@ -328,8 +328,15 @@ func checkIfConfigExistsAndIsNeeded(toolName string, cliLocalMode bool) error { // Check if the config file exists if _, err := os.Stat(toolConfigPath); os.IsNotExist(err) { - // Config file does not exist - create it if we have the means to do so - if (!cliLocalMode && initFlags.ApiToken != "") || cliLocalMode { + repoConfigPath := filepath.Join(config.Config.RepositoryDirectory(), configFileName) + if _, repoErr := os.Stat(repoConfigPath); repoErr == nil { + // Config not in .codacy/tools-configs/ - check if it exists in the repo root + logger.Info("Config file found in repository root for tool, skipping config creation", logrus.Fields{ + "tool": toolName, + "toolConfigPath": repoConfigPath, + }) + return nil + } else if (!cliLocalMode && initFlags.ApiToken != "") || cliLocalMode { if err := configsetup.CreateToolConfigurationFile(toolName, initFlags); err != nil { return fmt.Errorf("failed to create config file for tool %s: %w", toolName, err) } diff --git a/cmd/analyze_test.go b/cmd/analyze_test.go index d40703d..b25ffc3 100644 --- a/cmd/analyze_test.go +++ b/cmd/analyze_test.go @@ -332,49 +332,81 @@ func TestCheckIfConfigExistsAndIsNeeded(t *testing.T) { }() tests := []struct { - name string - toolName string - cliLocalMode bool - apiToken string - configFileExists bool - expectError bool - description string + name string + toolName string + cliLocalMode bool + apiToken string + configFileExists bool + repoRootConfigExists bool + expectError bool + expectConfigCreatedInToolsDir bool + description string }{ { - name: "tool_without_config_file", - toolName: "unsupported-tool", - cliLocalMode: false, - apiToken: "test-token", - configFileExists: false, - expectError: false, - description: "Tool that doesn't use config files should return without error", + name: "tool_without_config_file", + toolName: "unsupported-tool", + cliLocalMode: false, + apiToken: "test-token", + configFileExists: false, + repoRootConfigExists: false, + expectError: false, + expectConfigCreatedInToolsDir: false, + description: "Tool that doesn't use config files should return without error", }, { - name: "config_file_exists", - toolName: "eslint", - cliLocalMode: false, - apiToken: "test-token", - configFileExists: true, - expectError: false, - description: "When config file exists, should find it successfully", + name: "config_file_exists_in_tools_dir", + toolName: "eslint", + cliLocalMode: false, + apiToken: "test-token", + configFileExists: true, + repoRootConfigExists: false, + expectError: false, + expectConfigCreatedInToolsDir: true, + description: "When config file exists in tools-configs, should find it successfully", }, { - name: "remote_mode_without_token_no_config", - toolName: "eslint", - cliLocalMode: false, - apiToken: "", - configFileExists: false, - expectError: false, - description: "Remote mode without token should show appropriate message", + name: "remote_mode_without_token_no_config", + toolName: "eslint", + cliLocalMode: false, + apiToken: "", + configFileExists: false, + repoRootConfigExists: false, + expectError: false, + expectConfigCreatedInToolsDir: false, + description: "Remote mode without token and no config anywhere should not create defaults", }, { - name: "local_mode_no_config", - toolName: "eslint", - cliLocalMode: true, - apiToken: "", - configFileExists: false, - expectError: false, - description: "Local mode should create config file if tools-configs directory exists", + name: "local_mode_no_config", + toolName: "eslint", + cliLocalMode: true, + apiToken: "", + configFileExists: false, + repoRootConfigExists: false, + expectError: false, + expectConfigCreatedInToolsDir: true, + description: "Local mode with no config anywhere should create a default config in tools-configs", + }, + { + name: "local_mode_config_exists_in_repo_root", + toolName: "eslint", + cliLocalMode: true, + apiToken: "", + configFileExists: false, + repoRootConfigExists: true, + expectError: false, + expectConfigCreatedInToolsDir: false, + description: "Local mode should not create default config when user's own config exists in repo root", + }, + { + name: "remote_mode_with_token_config_exists_in_repo_root", + toolName: "eslint", + cliLocalMode: false, + apiToken: "test-token", + configFileExists: false, + repoRootConfigExists: true, + expectError: false, + expectConfigCreatedInToolsDir: false, + description: "Remote mode should not create default config when user's own config exists in repo root", }, } @@ -392,7 +424,7 @@ func TestCheckIfConfigExistsAndIsNeeded(t *testing.T) { // Mock config to use our temporary directory BEFORE creating files config.Config = *config.NewConfigType(tmpDir, tmpDir, tmpDir) - // Create config file if needed - using the same path logic as the function under test + // Create config file in tools-configs if needed if tt.configFileExists && constants.ToolConfigFileNames[tt.toolName] != "" { // Use config.Config.ToolsConfigDirectory() to get the exact same path the function will use toolsConfigDir := config.Config.ToolsConfigDirectory() @@ -408,6 +440,13 @@ func TestCheckIfConfigExistsAndIsNeeded(t *testing.T) { require.NoError(t, err, "Config file should exist at %s", configPath) } + // Create config file in repo root if needed + if tt.repoRootConfigExists && constants.ToolConfigFileNames[tt.toolName] != "" { + repoRootConfigPath := filepath.Join(config.Config.RepositoryDirectory(), constants.ToolConfigFileNames[tt.toolName]) + err = os.WriteFile(repoRootConfigPath, []byte("user's own config"), constants.DefaultFilePerms) + require.NoError(t, err) + } + // Setup initFlags initFlags = domain.InitFlags{ ApiToken: tt.apiToken, @@ -423,6 +462,30 @@ func TestCheckIfConfigExistsAndIsNeeded(t *testing.T) { // Execute the function err = checkIfConfigExistsAndIsNeeded(tt.toolName, tt.cliLocalMode) + // Verify results + if tt.expectError { + assert.Error(t, err, tt.description) + } else { + assert.NoError(t, err, tt.description) + } + // Verify whether a config was created in .codacy/tools-configs/ + if constants.ToolConfigFileNames[tt.toolName] != "" { + toolsConfigDir := config.Config.ToolsConfigDirectory() + generatedConfigPath := filepath.Join(toolsConfigDir, constants.ToolConfigFileNames[tt.toolName]) + _, statErr := os.Stat(generatedConfigPath) + configCreated := statErr == nil + + if tt.expectConfigCreatedInToolsDir { + assert.True(t, configCreated, + "%s: expected a config file to exist in tools-configs at %s", + tt.description, generatedConfigPath) + } else { + assert.True(t, os.IsNotExist(statErr), + "%s: expected NO config file to be created in tools-configs, but found one at %s", + tt.description, generatedConfigPath) + } + } + // Clean up any files that might have been created by the function under test if !tt.configFileExists && constants.ToolConfigFileNames[tt.toolName] != "" { toolsConfigDir := config.Config.ToolsConfigDirectory() @@ -431,13 +494,6 @@ func TestCheckIfConfigExistsAndIsNeeded(t *testing.T) { os.Remove(configPath) } } - - // Verify results - if tt.expectError { - assert.Error(t, err, tt.description) - } else { - assert.NoError(t, err, tt.description) - } }) } }