Skip to content
2 changes: 1 addition & 1 deletion .codacy/codacy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ tools:
- pylint@3.3.7
- revive@1.11.0
- semgrep@1.78.0
- trivy@0.66.0
- trivy@0.66.0
5 changes: 3 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
37 changes: 30 additions & 7 deletions cmd/analyze.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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"
}
}
}
Expand Down Expand Up @@ -351,10 +351,33 @@ func runToolByName(toolName string, workDirectory string, pathsToCheck []string,
if err != nil {
return err
}

var t *domain.Tool
var usesConfigurationFile = false

// 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
}
}
}

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)
Expand Down
10 changes: 5 additions & 5 deletions cmd/analyze_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
14 changes: 8 additions & 6 deletions tools/eslintRunner.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,21 @@ import (
// * Run from the root of the repo we want to analyse
// * NODE_PATH="<the installed eslint 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 {
Expand Down
14 changes: 4 additions & 10 deletions tools/lizard/lizardRunner.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -20,8 +21,6 @@ func RunLizard(workDirectory string, binary string, files []string, outputFile s
configFile, exists := tools.ConfigFileExists(config.Config, "lizard.yaml")
var patterns []domain.PatternDefinition
var errConfigs error


if exists {
// Configuration exists, read from file
patterns, errConfigs = ReadConfig(configFile)
Expand All @@ -38,8 +37,6 @@ func RunLizard(workDirectory string, binary string, files []string, outputFile s
if len(patterns) == 0 {
return fmt.Errorf("no valid patterns found in configuration")
}


// Construct base command with lizard module
args := []string{"-m", "lizard", "-V"}

Expand All @@ -50,23 +47,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
Expand Down
6 changes: 3 additions & 3 deletions tools/runnerUtils.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ func ConfigFileExists(conf config.ConfigType, fileNames ...string) (string, bool
generatedConfigFile := filepath.Join(conf.ToolsConfigDirectory(), fileName)
existingConfigFile := filepath.Join(conf.RepositoryDirectory(), fileName)

if _, err := os.Stat(generatedConfigFile); err == nil {
return generatedConfigFile, true
} else if _, err := os.Stat(existingConfigFile); err == nil {
if _, err := os.Stat(existingConfigFile); err == nil {
return existingConfigFile, true
} else if _, err := os.Stat(generatedConfigFile); err == nil {
return generatedConfigFile, true
}
}

Expand Down
31 changes: 0 additions & 31 deletions tools/runnerUtils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,37 +61,6 @@ func TestConfigFileExistsInRepositoryDirectory(t *testing.T) {
"Config path should be correctly formed relative path")
}

func TestConfigFilePrefersToolsConfigDirectory(t *testing.T) {
// Create a test directory structure
tempDir := t.TempDir()
repoDir := filepath.Join(tempDir, "src")
repositoryCache := filepath.Join(repoDir, ".codacy")

// Create configuration
config := *config.NewConfigType(repoDir, repositoryCache, "unused-global-cache")

// Create .codacy/tools-configs directory
configDir := filepath.Join(repoDir, ".codacy", "tools-configs")
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")

// 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 TestConfigFileDoesNotExist(t *testing.T) {
// Create a test directory structure
tempDir := t.TempDir()
Expand Down