Skip to content

Commit

Permalink
Merge pull request #94 from DepsHubHQ/93-parse-the-config-before-doin…
Browse files Browse the repository at this point in the history
…g-all-the-processing

Parse the config before doing all the processing
  • Loading branch information
semanser authored Jan 16, 2025
2 parents 34db2d0 + 55ff4c2 commit 8e80b4b
Show file tree
Hide file tree
Showing 41 changed files with 714 additions and 668 deletions.
18 changes: 5 additions & 13 deletions cmd/depshub/lint.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (

"github.com/charmbracelet/lipgloss"
"github.com/depshubhq/depshub/internal/linter"
"github.com/depshubhq/depshub/internal/linter/rules"
"github.com/depshubhq/depshub/pkg/types"
"github.com/spf13/cobra"
)

Expand All @@ -22,12 +22,6 @@ var lintCmd = &cobra.Command{
Run: func(cmd *cobra.Command, args []string) {
configPath, err := cmd.Flags().GetString("config")

if err != nil {
fmt.Println(err)
return
}
config, err := linter.NewConfig(configPath)

if err != nil {
fmt.Println(err)
return
Expand All @@ -40,20 +34,18 @@ var lintCmd = &cobra.Command{
}

lint := linter.New()
mistakes, err := lint.Run(p)
mistakes, err := lint.Run(p, configPath)

if err != nil {
fmt.Printf("Error: %s", err)
return
}

mistakes = config.Apply(mistakes)

errorsCount := 0
warningsCount := 0

for _, mistake := range mistakes {
if mistake.Rule.GetLevel() == rules.LevelError {
if mistake.Rule.GetLevel() == types.LevelError {
errorsCount++
} else {
warningsCount++
Expand Down Expand Up @@ -87,13 +79,13 @@ var lintCmd = &cobra.Command{
}

for _, mistake := range mistakes {
if mistake.Rule.GetLevel() == rules.LevelDisabled {
if mistake.Rule.GetLevel() == types.LevelDisabled {
continue
}

name := fmt.Sprintf("[%s]", mistake.Rule.GetName())

if mistake.Rule.GetLevel() == rules.LevelError {
if mistake.Rule.GetLevel() == types.LevelError {
name = errors.Render(fmt.Sprintf("[%s]", mistake.Rule.GetName()))
} else {
name = warnings.Render(fmt.Sprintf("[%s]", mistake.Rule.GetName()))
Expand Down
8 changes: 8 additions & 0 deletions depshub.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
version: 1
ignore:
- "**/testdata/**"
manifest_files:
- filter: "**"
rules:
- name: "allowed-licenses"
value: ["", "MIT", "Apache-2.0", "BSD-3-Clause"]
140 changes: 140 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package config

import (
"errors"
"fmt"
"path/filepath"

"github.com/bmatcuk/doublestar/v4"
"github.com/depshubhq/depshub/pkg/types"
"github.com/spf13/viper"
)

type Config struct {
path string
config ConfigFile
}

type ConfigFile struct {
Version int `mapstructure:"version"`
Ignore []string `mapstructure:"ignore"`
ManifestFiles []ManifestFile `mapstructure:"manifest_files"`
}

type Rule struct {
Name string `mapstructure:"name"`
Disabled bool `mapstructure:"disabled"`
Value any `mapstructure:"value"`
Level types.Level `mapstructure:"level"`
}

type ManifestFile struct {
Filter string `mapstructure:"filter"`
Rules []Rule `mapstructure:"rules"`
Packages []string `mapstructure:"packages"`
}

func New(filePath string) (Config, error) {
folder := filepath.Dir(filePath)

viper.AddConfigPath(folder)
viper.SetConfigName("depshub")
viper.SetConfigType("yaml")
err := viper.ReadInConfig()

if err != nil {
if errors.As(err, &viper.ConfigFileNotFoundError{}) && filePath != "." {
return Config{}, err
}

if errors.As(err, &viper.ConfigParseError{}) {
return Config{}, err
}
}

c := Config{path: filePath}

err = viper.Unmarshal(&c.config)

if err != nil {
return Config{}, err
}

return c, nil
}

// Checks if a path is ignored by the config
func (c Config) Ignored(path string) (bool, error) {
ignored := false

for _, ignore := range c.config.Ignore {
matched, err := doublestar.Match(ignore, path)

if err != nil {
return false, err
}

if matched {
ignored = true
break
}
}

return ignored, nil
}

func (c Config) Apply(manifestPath string, packageName string, rule types.Rule) error {
// Reset the to the default state before applying any settings
rule.Reset()

// Iterate through manifest files in config
for _, mf := range c.config.ManifestFiles {
// Check if manifest path matches the filter
matched, err := doublestar.Match(mf.Filter, manifestPath)
if err != nil {
return fmt.Errorf("invalid filter pattern %q: %w", mf.Filter, err)
}
if !matched {
continue
}

// Check if package is in the packages list (if specified)
if len(mf.Packages) > 0 {
packageMatch := false
for _, pkg := range mf.Packages {
if pkg == packageName {
packageMatch = true
break
}
}
if !packageMatch {
continue
}
}

// Look for matching rule by name
for _, configRule := range mf.Rules {
if configRule.Name == rule.GetName() {
if configRule.Disabled {
rule.SetLevel(types.LevelDisabled)
}

// Apply level if specified
if configRule.Level != "" {
rule.SetLevel(configRule.Level)
}

// Apply value if specified
if configRule.Value != nil {
if err := rule.SetValue(configRule.Value); err != nil {
return fmt.Errorf("failed to set value for rule %q: %w", rule.GetName(), err)
}
}

break
}
}
}

return nil
}
80 changes: 80 additions & 0 deletions internal/config/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package config

import (
"os"
"path/filepath"
"testing"

"github.com/depshubhq/depshub/pkg/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

// TestNewConfig tests various scenarios for config loading
func TestNewConfig(t *testing.T) {
t.Run("valid config file", func(t *testing.T) {
// Create a temporary config file
dir := t.TempDir()
configPath := filepath.Join(dir, "depshub.yaml")
configContent := []byte(`
version: 1
ignore:
- "test-ignore"
manifest_files:
- filter: "*.lock"
rules:
- name: "test-rule"
level: "warning"
value: 42
packages:
- "test-package"
`)
err := os.WriteFile(configPath, configContent, 0644)
require.NoError(t, err)

// Test loading the config
config, err := New(configPath)
require.NoError(t, err)
assert.Equal(t, 1, config.config.Version)
assert.Len(t, config.config.ManifestFiles, 1)
assert.Equal(t, "*.lock", config.config.ManifestFiles[0].Filter)
assert.Equal(t, "test-rule", config.config.ManifestFiles[0].Rules[0].Name)
assert.Equal(t, "test-ignore", config.config.Ignore[0])
})

t.Run("config file not found", func(t *testing.T) {
_, err := New("nonexistent/path/depshub.yaml")
assert.Error(t, err)
})

t.Run("invalid config file", func(t *testing.T) {
dir := t.TempDir()
configPath := filepath.Join(dir, "depshub.yaml")
invalidContent := []byte(`invalid: yaml: content`)
err := os.WriteFile(configPath, invalidContent, 0644)
require.NoError(t, err)

_, err = New(configPath)
assert.Error(t, err)
})
}

// mockRule implements the Rule interface for testing
type mockRule struct {
name string
level types.Level
value int
disabled bool
}

func (m *mockRule) Check(manifests []types.Manifest, info types.PackagesInfo, config types.Config) ([]types.Mistake, error) {
return nil, nil
}
func (m *mockRule) GetLevel() types.Level { return m.level }
func (m *mockRule) GetMessage() string { return "mock message" }
func (m *mockRule) GetName() string { return m.name }
func (m *mockRule) IsSupported(mt types.ManagerType) bool { return true }
func (m *mockRule) SetLevel(l types.Level) { m.level = l }
func (m *mockRule) SetValue(v any) error { m.value = v.(int); return nil }

// TODO add tests for the Apply function
Loading

0 comments on commit 8e80b4b

Please sign in to comment.