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
101 changes: 71 additions & 30 deletions cmd/init.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cmd

import (
codacyclient "codacy/cli-v2/codacy-client"
"codacy/cli-v2/config"
"codacy/cli-v2/domain"
"codacy/cli-v2/tools"
Expand All @@ -23,20 +24,13 @@ import (

const CodacyApiBase = "https://app.codacy.com"

type InitFlags struct {
apiToken string
provider string
organization string
repository string
}

var initFlags InitFlags
var initFlags domain.InitFlags

func init() {
initCmd.Flags().StringVar(&initFlags.apiToken, "api-token", "", "optional codacy api token, if defined configurations will be fetched from codacy")
initCmd.Flags().StringVar(&initFlags.provider, "provider", "", "provider (gh/bb/gl) to fetch configurations from codacy, required when api-token is provided")
initCmd.Flags().StringVar(&initFlags.organization, "organization", "", "remote organization name to fetch configurations from codacy, required when api-token is provided")
initCmd.Flags().StringVar(&initFlags.repository, "repository", "", "remote repository name to fetch configurations from codacy, required when api-token is provided")
initCmd.Flags().StringVar(&initFlags.ApiToken, "api-token", "", "optional codacy api token, if defined configurations will be fetched from codacy")
initCmd.Flags().StringVar(&initFlags.Provider, "provider", "", "provider (gh/bb/gl) to fetch configurations from codacy, required when api-token is provided")
initCmd.Flags().StringVar(&initFlags.Organization, "organization", "", "remote organization name to fetch configurations from codacy, required when api-token is provided")
initCmd.Flags().StringVar(&initFlags.Repository, "repository", "", "remote repository name to fetch configurations from codacy, required when api-token is provided")
rootCmd.AddCommand(initCmd)
}

Expand All @@ -56,11 +50,11 @@ var initCmd = &cobra.Command{
log.Fatalf("Failed to create tools-configs directory: %v", err)
}

cliLocalMode := len(initFlags.apiToken) == 0
cliLocalMode := len(initFlags.ApiToken) == 0

if cliLocalMode {
fmt.Println()
fmt.Println("ℹ️ No project token was specified, skipping fetch configurations")
fmt.Println("ℹ️ No project token was specified, fetching codacy default configurations")
noTools := []tools.Tool{}
err := createConfigurationFiles(noTools, cliLocalMode)
if err != nil {
Expand All @@ -71,7 +65,7 @@ var initCmd = &cobra.Command{
log.Fatal(err)
}
} else {
err := buildRepositoryConfigurationFiles(initFlags.apiToken)
err := buildRepositoryConfigurationFiles(initFlags.ApiToken)
if err != nil {
log.Fatal(err)
}
Expand Down Expand Up @@ -265,7 +259,7 @@ func buildRepositoryConfigurationFiles(token string) error {
Timeout: 10 * time.Second,
}

apiTools, err := tools.GetRepositoryTools(CodacyApiBase, token, initFlags.provider, initFlags.organization, initFlags.repository)
apiTools, err := tools.GetRepositoryTools(CodacyApiBase, token, initFlags.Provider, initFlags.Organization, initFlags.Repository)
if err != nil {
return err
}
Expand All @@ -282,7 +276,7 @@ func buildRepositoryConfigurationFiles(token string) error {
}

// Generate languages configuration based on API tools response
if err := tools.CreateLanguagesConfigFile(apiTools, toolsConfigDir, uuidToName, token, initFlags.provider, initFlags.organization, initFlags.repository); err != nil {
if err := tools.CreateLanguagesConfigFile(apiTools, toolsConfigDir, uuidToName, token, initFlags.Provider, initFlags.Organization, initFlags.Repository); err != nil {
return fmt.Errorf("failed to create languages configuration file: %w", err)
}

Expand All @@ -300,9 +294,9 @@ func buildRepositoryConfigurationFiles(token string) error {

url := fmt.Sprintf("%s/api/v3/analysis/organizations/%s/%s/repositories/%s/tools/%s/patterns?enabled=true&limit=1000",
CodacyApiBase,
initFlags.provider,
initFlags.organization,
initFlags.repository,
initFlags.Provider,
initFlags.Organization,
initFlags.Repository,
tool.Uuid)

// Create a new GET request
Expand Down Expand Up @@ -493,6 +487,7 @@ func createDefaultTrivyConfigFile(toolsConfigDir string) error {
// createDefaultEslintConfigFile creates a default eslint.config.mjs configuration file
func createDefaultEslintConfigFile(toolsConfigDir string) error {
// Use empty tool configuration to get default settings
//
emptyConfig := []domain.PatternConfiguration{}
content := tools.CreateEslintConfig(emptyConfig)

Expand Down Expand Up @@ -560,7 +555,6 @@ func createLizardConfigFile(toolsConfigDir string, patternConfiguration []domain
patterns[i] = pattern.PatternDefinition
}

fmt.Println("Lizard configuration created based on Codacy settings")
}

content, err := lizard.CreateLizardConfig(patterns)
Expand All @@ -573,16 +567,52 @@ func createLizardConfigFile(toolsConfigDir string, patternConfiguration []domain

// buildDefaultConfigurationFiles creates default configuration files for all tools
func buildDefaultConfigurationFiles(toolsConfigDir string) error {
// Create default Lizard configuration
if err := createLizardConfigFile(toolsConfigDir, []domain.PatternConfiguration{}); err != nil {
return fmt.Errorf("failed to create default Lizard configuration: %w", err)
}

// Add other default tool configurations here as needed
// For example:
// if err := createDefaultEslintConfigFile(toolsConfigDir); err != nil {
// return fmt.Errorf("failed to create default ESLint configuration: %w", err)
// }
for _, tool := range AvailableTools {
patternsConfig, err := codacyclient.GetDefaultToolPatternsConfig(initFlags, tool)
if err != nil {
return fmt.Errorf("failed to get default tool patterns config: %w", err)
}
switch tool {
Comment thread
franciscoovazevedo marked this conversation as resolved.
case ESLint:
eslintConfigurationString := tools.CreateEslintConfig(patternsConfig)

eslintConfigFile, err := os.Create(filepath.Join(toolsConfigDir, "eslint.config.mjs"))
if err != nil {
return fmt.Errorf("failed to create eslint config file: %v", err)
}
defer eslintConfigFile.Close()

_, err = eslintConfigFile.WriteString(eslintConfigurationString)
if err != nil {
return fmt.Errorf("failed to write eslint config: %v", err)
}
case Trivy:
if err := createTrivyConfigFile(patternsConfig, toolsConfigDir); err != nil {
return fmt.Errorf("failed to create default Trivy configuration: %w", err)
}
case PMD:
if err := createPMDConfigFile(patternsConfig, toolsConfigDir); err != nil {
return fmt.Errorf("failed to create default PMD configuration: %w", err)
}
case PyLint:
if err := createPylintConfigFile(patternsConfig, toolsConfigDir); err != nil {
return fmt.Errorf("failed to create default Pylint configuration: %w", err)
}
case DartAnalyzer:
if err := createDartAnalyzerConfigFile(patternsConfig, toolsConfigDir); err != nil {
return fmt.Errorf("failed to create default Dart Analyzer configuration: %w", err)
}
case Semgrep:
if err := createSemgrepConfigFile(patternsConfig, toolsConfigDir); err != nil {
return fmt.Errorf("failed to create default Semgrep configuration: %w", err)
}
case Lizard:
if err := createLizardConfigFile(toolsConfigDir, patternsConfig); err != nil {
return fmt.Errorf("failed to create default Lizard configuration: %w", err)
}
}
}

return nil
}
Expand All @@ -596,3 +626,14 @@ const (
Semgrep string = "6792c561-236d-41b7-ba5e-9d6bee0d548b"
Lizard string = "76348462-84b3-409a-90d3-955e90abfb87"
)

// AvailableTools lists all tool UUIDs supported by Codacy CLI.
var AvailableTools = []string{
ESLint,
Trivy,
PMD,
PyLint,
DartAnalyzer,
Semgrep,
Lizard,
}
67 changes: 66 additions & 1 deletion cmd/init_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package cmd

import (
"codacy/cli-v2/config"
"codacy/cli-v2/tools"
"codacy/cli-v2/utils"
"os"
"path/filepath"
"testing"
Expand Down Expand Up @@ -161,7 +163,7 @@ func TestCleanConfigDirectory(t *testing.T) {

for _, file := range testFiles {
filePath := filepath.Join(tempDir, file)
err := os.WriteFile(filePath, []byte("test content"), 0644)
err := os.WriteFile(filePath, []byte("test content"), utils.DefaultFilePerms)
assert.NoError(t, err, "Failed to create test file: %s", filePath)
}

Expand All @@ -179,3 +181,66 @@ func TestCleanConfigDirectory(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, 0, len(files), "Expected 0 files after cleaning, got %d", len(files))
}

func TestInitCommand_NoToken(t *testing.T) {
tempDir := t.TempDir()
originalWD, err := os.Getwd()
assert.NoError(t, err, "Failed to get current working directory")
defer os.Chdir(originalWD)

// Use the real plugins/tools/semgrep/rules.yaml file
rulesPath := filepath.Join("plugins", "tools", "semgrep", "rules.yaml")
if _, err := os.Stat(rulesPath); os.IsNotExist(err) {
t.Skip("plugins/tools/semgrep/rules.yaml not found; skipping test")
}

// Change to the temp directory to simulate a new project
err = os.Chdir(tempDir)
assert.NoError(t, err, "Failed to change working directory to tempDir")

// Simulate running init with no token
initFlags.ApiToken = ""
initFlags.Provider = ""
initFlags.Organization = ""
initFlags.Repository = ""

// Call the Run logic from initCmd
if err := config.Config.CreateLocalCodacyDir(); err != nil {
t.Fatalf("Failed to create local codacy directory: %v", err)
}

toolsConfigDir := config.Config.ToolsConfigDirectory()
if err := os.MkdirAll(toolsConfigDir, utils.DefaultFilePerms); err != nil {
t.Fatalf("Failed to create tools-configs directory: %v", err)
}

cliLocalMode := len(initFlags.ApiToken) == 0
if cliLocalMode {
noTools := []tools.Tool{}
err := createConfigurationFiles(noTools, cliLocalMode)
assert.NoError(t, err, "createConfigurationFiles should not return an error")
if err := buildDefaultConfigurationFiles(toolsConfigDir); err != nil {
t.Fatalf("Failed to build default configuration files: %v", err)
}
}

// Assert that the expected config files are created
codacyDir := config.Config.LocalCodacyDirectory()
expectedFiles := []string{
"tools-configs/eslint.config.mjs",
"tools-configs/trivy.yaml",
"tools-configs/ruleset.xml",
"tools-configs/pylint.rc",
"tools-configs/analysis_options.yaml",
"tools-configs/semgrep.yaml",
"tools-configs/lizard.yaml",
"codacy.yaml",
"cli-config.yaml",
}

for _, file := range expectedFiles {
filePath := filepath.Join(codacyDir, file)
_, err := os.Stat(filePath)
assert.NoError(t, err, "Expected config file %s to be created", file)
}
}
Loading
Loading