Skip to content

[on hold] Feature: Add config discovery command [PLUTO-1429] #134

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .cursor/rules/cursor.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ alwaysApply: true
- run go build after each code modification to see if app compiles
- remove dead unused code
- look for constants like file permissons in `constants` folder
- run go tests if you modified tests files
Copy link
Preview

Copilot AI Jun 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Grammar issue: change 'tests files' to 'test files' for clarity.

Suggested change
- run go tests if you modified tests files
- run go tests if you modified test files

Copilot uses AI. Check for mistakes.


## Code Style Guidelines
- **Imports**: Standard lib first, external packages second, internal last
Expand Down
134 changes: 124 additions & 10 deletions cmd/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,22 +47,97 @@

if cliLocalMode {
fmt.Println()
fmt.Println("ℹ️ No project token was specified, fetching codacy default configurations")
noTools := []domain.Tool{}
err := createConfigurationFiles(noTools, cliLocalMode)
fmt.Println("ℹ️ No project token was specified, detecting languages and configuring tools...")

// Create language detector and scan the project
detector := utils.NewLanguageDetector()
languages, err := detector.DetectLanguages(".")
if err != nil {
log.Fatalf("Failed to detect languages: %v", err)
}

// Get all available tools from the API
availableTools, err := codacyclient.GetToolsVersions()
if err != nil {
log.Fatalf("Failed to get tools versions: %v", err)
}

// Map tools by name for easier lookup
toolsByName := make(map[string]domain.Tool)
for _, tool := range availableTools {
toolsByName[strings.ToLower(tool.Name)] = tool
}

// Enable tools based on detected languages
var enabledTools []domain.Tool
toolsEnabled := make(map[string]bool)

// Always enable Trivy for security scanning
if trivyTool, ok := toolsByName["trivy"]; ok {
enabledTools = append(enabledTools, trivyTool)
toolsEnabled[trivyTool.Uuid] = true
}

// Enable tools based on detected languages
for langName := range languages {
for _, tool := range availableTools {
if !toolsEnabled[tool.Uuid] {
for _, supportedLang := range tool.Languages {
if strings.EqualFold(langName, supportedLang) {
enabledTools = append(enabledTools, tool)
toolsEnabled[tool.Uuid] = true
break
}
}
}
}
}

// Always enable Lizard for complexity analysis if any supported language is detected
if shouldEnableLizard(languages) {
if lizardTool, ok := toolsByName["lizard"]; ok && !toolsEnabled[lizardTool.Uuid] {
enabledTools = append(enabledTools, lizardTool)
toolsEnabled[lizardTool.Uuid] = true
}
}

// Create configuration files
err = createConfigurationFiles(enabledTools, cliLocalMode)
if err != nil {
log.Fatal(err)
}
// Create default configuration files
if err := buildDefaultConfigurationFiles(toolsConfigDir); err != nil {

// Create default configuration files for enabled tools
if err := buildDefaultConfigurationFiles(toolsConfigDir, enabledTools); err != nil {
log.Fatal(err)
}

// Print summary of detected languages and enabled tools
fmt.Println("\nDetected languages:")
for langName, info := range languages {
fmt.Printf(" - %s (%d files)\n", langName, len(info.Files))
}

fmt.Println("\nEnabled tools:")
// Create a map of supported tool UUIDs for quick lookup
supportedTools := make(map[string]bool)
for _, uuid := range AvailableTools {
supportedTools[uuid] = true
}

// Only show tools that are both enabled and supported by the CLI
for _, tool := range enabledTools {
if supportedTools[tool.Uuid] {
fmt.Printf(" - %s@%s\n", tool.Name, tool.Version)
}
}
} else {
err := buildRepositoryConfigurationFiles(initFlags.ApiToken)
if err != nil {
log.Fatal(err)
}
}

createGitIgnoreFile()
fmt.Println()
fmt.Println("✅ Successfully initialized Codacy configuration!")
Expand All @@ -74,6 +149,45 @@
},
}

// shouldEnableLizard checks if Lizard should be enabled based on detected languages
func shouldEnableLizard(languages map[string]*utils.LanguageInfo) bool {
lizardSupportedLangs := map[string]bool{
"C": true, "C++": true, "Java": true, "C#": true,
"JavaScript": true, "TypeScript": true, "Python": true,
"Ruby": true, "PHP": true, "Scala": true, "Go": true,
"Rust": true, "Kotlin": true, "Swift": true,
}

for lang := range languages {
if lizardSupportedLangs[lang] {
return true
}
}
return false
}

// toolNameFromUUID returns the short name for a tool UUID
func toolNameFromUUID(uuid string) string {

Check warning on line 170 in cmd/init.go

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

cmd/init.go#L170

Method toolNameFromUUID has a cyclomatic complexity of 8 (limit is 7)
switch uuid {
case ESLint:
return "eslint"
case Trivy:
return "trivy"
case PyLint:
return "pylint"
case PMD:
return "pmd"
case DartAnalyzer:
return "dartanalyzer"
case Semgrep:
return "semgrep"
case Lizard:
return "lizard"
default:
return "unknown"
}
}

Comment on lines +169 to +190
Copy link
Preview

Copilot AI Jun 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The toolNameFromUUID function is never called and can be removed to reduce dead code.

Suggested change
// toolNameFromUUID returns the short name for a tool UUID
func toolNameFromUUID(uuid string) string {
switch uuid {
case ESLint:
return "eslint"
case Trivy:
return "trivy"
case PyLint:
return "pylint"
case PMD:
return "pmd"
case DartAnalyzer:
return "dartanalyzer"
case Semgrep:
return "semgrep"
case Lizard:
return "lizard"
default:
return "unknown"
}
}

Copilot uses AI. Check for mistakes.

func createGitIgnoreFile() error {
gitIgnorePath := filepath.Join(config.Config.LocalCodacyDirectory(), ".gitignore")
gitIgnoreFile, err := os.Create(gitIgnorePath)
Expand Down Expand Up @@ -427,14 +541,14 @@
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)
// buildDefaultConfigurationFiles creates default configuration files for enabled tools
func buildDefaultConfigurationFiles(toolsConfigDir string, enabledTools []domain.Tool) error {

Check failure on line 545 in cmd/init.go

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

cmd/init.go#L545

Method buildDefaultConfigurationFiles has a cyclomatic complexity of 17 (limit is 10)
for _, tool := range enabledTools {
patternsConfig, err := codacyclient.GetDefaultToolPatternsConfig(initFlags, tool.Uuid)
if err != nil {
return fmt.Errorf("failed to get default tool patterns config: %w", err)
}
switch tool {
switch tool.Uuid {
case ESLint:
if err := tools.CreateEslintConfig(toolsConfigDir, patternsConfig); err != nil {
return fmt.Errorf("failed to create eslint config file: %v", err)
Expand Down
Loading
Loading