Skip to content
Merged
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
2 changes: 1 addition & 1 deletion cmd/analyze.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ func runPmdAnalysis(workDirectory string, pathsToCheck []string, outputFile stri
pmd := config.Config.Tools()["pmd"]
pmdBinary := pmd.Binaries["pmd"]

err := tools.RunPmd(workDirectory, pmdBinary, pathsToCheck, outputFile, outputFormat, "")
err := tools.RunPmd(workDirectory, pmdBinary, pathsToCheck, outputFile, outputFormat, config.Config)
if err != nil {
log.Fatalf("Error running PMD: %v", err)
}
Expand Down
4 changes: 2 additions & 2 deletions cmd/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -327,12 +327,12 @@ func createToolFileConfigurations(tool tools.Tool, patternConfiguration []domain

func createPMDConfigFile(config []domain.PatternConfiguration, toolsConfigDir string) error {
pmdConfigurationString := tools.CreatePmdConfig(config)
return os.WriteFile(filepath.Join(toolsConfigDir, "pmd-ruleset.xml"), []byte(pmdConfigurationString), utils.DefaultFilePerms)
return os.WriteFile(filepath.Join(toolsConfigDir, "ruleset.xml"), []byte(pmdConfigurationString), utils.DefaultFilePerms)
}

func createDefaultPMDConfigFile(toolsConfigDir string) error {
content := tools.CreatePmdConfig([]domain.PatternConfiguration{})
return os.WriteFile(filepath.Join(toolsConfigDir, "pmd-ruleset.xml"), []byte(content), utils.DefaultFilePerms)
return os.WriteFile(filepath.Join(toolsConfigDir, "ruleset.xml"), []byte(content), utils.DefaultFilePerms)

}

Expand Down
49 changes: 39 additions & 10 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
)

type ConfigType struct {
homePath string
repositoryDirectory string

globalCacheDirectory string
runtimesDirectory string
toolsDirectory string
Expand All @@ -24,8 +25,8 @@
tools map[string]*plugins.ToolInfo
}

func (c *ConfigType) HomePath() string {
return c.homePath
func (c *ConfigType) RepositoryDirectory() string {

Check notice on line 28 in config/config.go

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

config/config.go#L28

exported method ConfigType.RepositoryDirectory should have comment or be unexported
return c.repositoryDirectory
}

func (c *ConfigType) CodacyDirectory() string {
Expand Down Expand Up @@ -98,15 +99,42 @@
return c.toolsConfigDirectory
}

func (c *ConfigType) setupCodacyPaths() {
c.globalCacheDirectory = filepath.Join(c.homePath, ".cache", "codacy")
func NewConfigType(repositoryDirectory string, repositoryCache string, globalCache string) *ConfigType {

Check notice on line 102 in config/config.go

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

config/config.go#L102

exported function NewConfigType should have comment or be unexported
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Created a builder, so it can used both on the Global initialization and tests

c := &ConfigType{}

c.repositoryDirectory = repositoryDirectory
c.localCodacyDirectory = repositoryCache
c.globalCacheDirectory = globalCache

c.runtimesDirectory = filepath.Join(c.globalCacheDirectory, "runtimes")
c.toolsDirectory = filepath.Join(c.globalCacheDirectory, "tools")
c.localCodacyDirectory = ".codacy"
c.toolsConfigDirectory = filepath.Join(c.localCodacyDirectory, "tools-configs")

c.projectConfigFile = filepath.Join(c.localCodacyDirectory, "codacy.yaml")
c.cliConfigFile = filepath.Join(c.localCodacyDirectory, "cli-config.yaml")

c.runtimes = make(map[string]*plugins.RuntimeInfo)
c.tools = make(map[string]*plugins.ToolInfo)
return c
}

// TODO: Consider not having a global config and instead pass the config object around
Copy link
Contributor Author

Choose a reason for hiding this comment

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

For now, let's keep it, but it is now easier to remove, as it only depends at the NewConfigType method to generate the values for the config

func setupGlobalConfig(repositoryDirectory string, repositoryCache string, globalCache string) {
newConfig := NewConfigType(repositoryDirectory, repositoryCache, globalCache)

Config.repositoryDirectory = newConfig.repositoryDirectory
Config.localCodacyDirectory = newConfig.localCodacyDirectory
Config.globalCacheDirectory = newConfig.globalCacheDirectory

Config.runtimesDirectory = newConfig.runtimesDirectory
Config.toolsDirectory = newConfig.toolsDirectory
Config.toolsConfigDirectory = newConfig.toolsConfigDirectory

Config.projectConfigFile = newConfig.projectConfigFile
Config.cliConfigFile = newConfig.cliConfigFile

Config.runtimes = newConfig.runtimes
Config.tools = newConfig.tools
}

func (c *ConfigType) CreateCodacyDirs() error {
Expand Down Expand Up @@ -136,11 +164,12 @@
if err != nil {
log.Fatal(err)
}
Config.homePath = homePath
// Repository directory is the current working directory
repositoryDirectory := ""
repositoryCache := ".codacy"
globalCache := filepath.Join(homePath, ".cache", "codacy")

Config.setupCodacyPaths()
Config.runtimes = make(map[string]*plugins.RuntimeInfo)
Config.tools = make(map[string]*plugins.ToolInfo)
setupGlobalConfig(repositoryDirectory, repositoryCache, globalCache)
}

// IsRuntimeInstalled checks if a runtime is already installed
Expand Down
7 changes: 4 additions & 3 deletions tools/eslintRunner.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ func RunEslint(repositoryToAnalyseDirectory string, eslintInstallationDirectory
// https://eslint.org/docs/v8.x/use/configure/configuration-files-new
cmd.Env = append(cmd.Env, "ESLINT_USE_FLAT_CONFIG=true")

// Add config file from tools-configs directory
configFile := filepath.Join(config.Config.ToolsConfigDirectory(), "eslint.config.mjs")
cmd.Args = append(cmd.Args, "-c", configFile)
// Add config file from tools-configs directory if it exists
if configFile, exists := ConfigFileExists(config.Config, "eslint.config.mjs"); exists {
cmd.Args = append(cmd.Args, "-c", configFile)
}

if autoFix {
cmd.Args = append(cmd.Args, "--fix")
Expand Down
10 changes: 3 additions & 7 deletions tools/pmdRunner.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
"codacy/cli-v2/config"
"os"
"os/exec"
"path/filepath"
"strings"
)

Expand All @@ -20,15 +19,12 @@
//
// Returns:
// - error: nil if analysis succeeds or violations found, error otherwise
func RunPmd(repositoryToAnalyseDirectory string, pmdBinary string, pathsToCheck []string, outputFile string, outputFormat string, rulesetFile string) error {
func RunPmd(repositoryToAnalyseDirectory string, pmdBinary string, pathsToCheck []string, outputFile string, outputFormat string, config config.ConfigType) error {

Check warning on line 22 in tools/pmdRunner.go

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tools/pmdRunner.go#L22

Method RunPmd has 6 parameters (limit is 5)

Check warning on line 22 in tools/pmdRunner.go

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tools/pmdRunner.go#L22

Method RunPmd has a cyclomatic complexity of 8 (limit is 7)
cmd := exec.Command(pmdBinary, "pmd")

// Add config file from tools-configs directory if not specified
if rulesetFile == "" {
configFile := filepath.Join(config.Config.ToolsConfigDirectory(), "pmd-ruleset.xml")
// Add config file from tools-configs directory if it exists
if configFile, exists := ConfigFileExists(config, "ruleset.xml"); exists {
cmd.Args = append(cmd.Args, "-R", configFile)
} else {
cmd.Args = append(cmd.Args, "-R", rulesetFile)
}

// Add source directories (comma-separated list for PMD)
Expand Down
12 changes: 9 additions & 3 deletions tools/pmdRunner_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package tools

import (
"codacy/cli-v2/config"
"encoding/json"
"log"
"os"
Expand Down Expand Up @@ -51,19 +52,24 @@ func TestRunPmdToFile(t *testing.T) {

// Use the correct path relative to tools directory
testDirectory := filepath.Join(currentDirectory, "testdata", "repositories", "pmd")
repositoryCache := filepath.Join(testDirectory, ".codacy")
globalCache := filepath.Join(homeDirectory, ".cache/codacy")

tempResultFile := filepath.Join(os.TempDir(), "pmd.sarif")
defer os.Remove(tempResultFile)

config := *config.NewConfigType(testDirectory, repositoryCache, globalCache)

// Use absolute paths
repositoryToAnalyze := testDirectory
// Use the standard ruleset file for testing the PMD runner functionality
rulesetFile := filepath.Join(testDirectory, "pmd-ruleset.xml")
//rulesetFile := filepath.Join(testDirectory, "ruleset.xml")

// Use the same path as defined in plugin.yaml
pmdBinary := filepath.Join(homeDirectory, ".cache/codacy/tools/[email protected]/pmd-bin-6.55.0/bin/run.sh")
pmdBinary := filepath.Join(globalCache, "tools/[email protected]/pmd-bin-6.55.0/bin/run.sh")

// Run PMD
err = RunPmd(repositoryToAnalyze, pmdBinary, nil, tempResultFile, "sarif", rulesetFile)
err = RunPmd(repositoryToAnalyze, pmdBinary, nil, tempResultFile, "sarif", config)
if err != nil {
t.Fatalf("Failed to run pmd: %v", err)
}
Expand Down
8 changes: 5 additions & 3 deletions tools/pylintRunner.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package tools

import (
"codacy/cli-v2/config"
"codacy/cli-v2/plugins"
"codacy/cli-v2/utils"
"fmt"
Expand All @@ -19,9 +20,10 @@ func RunPylint(workDirectory string, toolInfo *plugins.ToolInfo, files []string,
// Always use JSON output format since we'll convert to SARIF if needed
args = append(args, "--output-format=json")

// Use the configuration file from .codacy/tools-configs/pylintrc
configPath := filepath.Join(workDirectory, ".codacy", "tools-configs", "pylint.rc")
args = append(args, fmt.Sprintf("--rcfile=%s", configPath))
// Check if a config file exists in the expected location and use it if present
if configFile, exists := ConfigFileExists(config.Config, "pylint.rc"); exists {
args = append(args, fmt.Sprintf("--rcfile=%s", configFile))
}

// Create a temporary file for JSON output if we need to convert to SARIF
var tempFile string
Expand Down
30 changes: 30 additions & 0 deletions tools/runnerUtils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package tools

import (
"codacy/cli-v2/config"
"os"
"path/filepath"
)

// ConfigFileExists checks if a specific configuration file exists in the .codacy/tools-configs/
// or on the root of the repository directory.
//
// Parameters:
// - conf: The configuration object containing the tools config directory
// - fileName: The configuration file name to check for
//
// Returns:
// - string: The relative path to the configuration file (for cmd args)
// - bool: True if the file exists, false otherwise
func ConfigFileExists(conf config.ConfigType, fileName string) (string, bool) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Each tool runner should now depend on this when generating its command to pass a configuration file

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 {
return existingConfigFile, true
}

return "", false
}
108 changes: 108 additions & 0 deletions tools/runnerUtils_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package tools

import (
"codacy/cli-v2/config"
"os"
"path/filepath"
"testing"

"github.com/stretchr/testify/assert"
)

func TestConfigFileExistsInToolsConfigDirectory(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)

Check warning on line 23 in tools/runnerUtils_test.go

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tools/runnerUtils_test.go#L23

Detected file permissions that are set to more than `0600` (user/owner can read and write). Setting file permissions to higher than `0600` is most likely unnecessary and violates the principle of least privilege.

Check warning on line 23 in tools/runnerUtils_test.go

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tools/runnerUtils_test.go#L23

The application was found setting directory permissions to overly permissive values.
assert.NoError(t, err, "Failed to create test directory structure")

// Create a test config file on the configDir
generatedConfigFile := filepath.Join(configDir, "generated-config.yaml")
err = os.WriteFile(generatedConfigFile, []byte("test content"), 0644)

Check warning on line 28 in tools/runnerUtils_test.go

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tools/runnerUtils_test.go#L28

The application was found setting file permissions to overly permissive values.
assert.NoError(t, err, "Failed to create test config file")

// Test case: Config file exists in tools config directory
configPath, exists := ConfigFileExists(config, "generated-config.yaml")
assert.True(t, exists, "Config file should exist in tools config directory")
assert.Equal(t, filepath.Join(config.ToolsConfigDirectory(), "generated-config.yaml"), configPath,
"Config path should be correctly formed relative path")
}

func TestConfigFileExistsInRepositoryDirectory(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 on the repository directory
existingConfigFile := filepath.Join(repoDir, "existing-config.yaml")
err = os.WriteFile(existingConfigFile, []byte("test content"), 0644)

Check warning on line 54 in tools/runnerUtils_test.go

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tools/runnerUtils_test.go#L54

The application was found setting file permissions to overly permissive values.
assert.NoError(t, err, "Failed to create test config file")

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

Check warning on line 82 in tools/runnerUtils_test.go

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tools/runnerUtils_test.go#L82

The application was found setting file permissions to overly permissive values.
assert.NoError(t, err, "Failed to create test config file in tools config directory")

err = os.WriteFile(existingConfigFile, []byte("repository config content"), 0644)

Check warning on line 85 in tools/runnerUtils_test.go

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tools/runnerUtils_test.go#L85

The application was found setting file permissions to overly permissive values.
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()
repoDir := filepath.Join(tempDir, "src")
repositoryCache := filepath.Join(repoDir, ".codacy")

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

// Test case: Config file does not exist
configPath, exists := ConfigFileExists(config, "non-existent-config.yaml")
assert.False(t, exists, "Config file should not exist")
assert.Equal(t, "", configPath, "Config path should be empty for non-existent file")
}
8 changes: 4 additions & 4 deletions tools/trivyRunner.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ import (
"codacy/cli-v2/config"
"os"
"os/exec"
"path/filepath"
)

// RunTrivy executes Trivy vulnerability scanner with the specified options
func RunTrivy(repositoryToAnalyseDirectory string, trivyBinary string, pathsToCheck []string, outputFile string, outputFormat string) error {
cmd := exec.Command(trivyBinary, "fs")

// Add config file from tools-configs directory
configFile := filepath.Join(config.Config.ToolsConfigDirectory(), "trivy.yaml")
cmd.Args = append(cmd.Args, "--config", configFile)
// Add config file from tools-configs directory if it exists
if configFile, exists := ConfigFileExists(config.Config, "trivy.yaml"); exists {
cmd.Args = append(cmd.Args, "--config", configFile)
}

// Add format options
if outputFile != "" {
Expand Down