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
44 changes: 33 additions & 11 deletions cli/cmd/lint.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,24 @@ func (r *runners) runLint(cmd *cobra.Command, args []string) error {

// Load .replicated config using tools parser (supports monorepos)
parser := tools.NewConfigParser()
config, err := parser.FindAndParseConfig(".")
configResult, err := parser.FindAndParseConfigWithPaths(".")

if err != nil {
return errors.Wrap(err, "failed to load .replicated config")
}

config := configResult.Config

// Display discovered config files in verbose mode
if r.args.lintVerbose && len(configResult.ConfigPaths) > 0 {
fmt.Fprintln(r.w, "Discovered config files:")
for _, configPath := range configResult.ConfigPaths {
fmt.Fprintf(r.w, " - Path: %s\n", configPath)
}
fmt.Fprintln(r.w)
r.w.Flush()
}

// Initialize JSON output structure
output := &JSONLintOutput{}

Expand Down Expand Up @@ -97,7 +109,11 @@ func (r *runners) runLint(cmd *cobra.Command, args []string) error {
autoDiscoveryMode := len(config.Charts) == 0 && len(config.Preflights) == 0 && len(config.Manifests) == 0

if autoDiscoveryMode {
fmt.Fprintf(r.w, "No .replicated config found. Auto-discovering lintable resources in current directory...\n\n")
if len(configResult.ConfigPaths) > 0 {
fmt.Fprintf(r.w, "No resources configured in .replicated. Auto-discovering lintable resources in current directory...\n\n")
} else {
fmt.Fprintf(r.w, "No .replicated config found. Auto-discovering lintable resources in current directory...\n\n")
}
r.w.Flush()

// Auto-discover Helm charts
Expand Down Expand Up @@ -686,7 +702,7 @@ func (r *runners) displayHelmResults(results *HelmLintResults) error {
fmt.Fprintln(r.w)

for _, chart := range results.Charts {
fmt.Fprintf(r.w, "==> Linting chart: %s\n\n", chart.Path)
fmt.Fprintf(r.w, "==> Linting chart\nPath: %s\n\n", chart.Path)

if len(chart.Messages) == 0 {
fmt.Fprintf(r.w, "No issues found\n")
Expand All @@ -700,8 +716,10 @@ func (r *runners) displayHelmResults(results *HelmLintResults) error {
}
}

fmt.Fprintf(r.w, "\nSummary for %s: %d error(s), %d warning(s), %d info\n",
chart.Path, chart.Summary.ErrorCount, chart.Summary.WarningCount, chart.Summary.InfoCount)
fmt.Fprintf(r.w, "\nSummary:\n")
fmt.Fprintf(r.w, " Path: %s\n", chart.Path)
fmt.Fprintf(r.w, " Errors: %d, Warnings: %d, Info: %d\n",
chart.Summary.ErrorCount, chart.Summary.WarningCount, chart.Summary.InfoCount)

if chart.Success {
fmt.Fprintf(r.w, "Status: Passed\n\n")
Expand Down Expand Up @@ -757,7 +775,7 @@ func (r *runners) displayPreflightResults(results *PreflightLintResults) error {
fmt.Fprintln(r.w)

for _, spec := range results.Specs {
fmt.Fprintf(r.w, "==> Linting preflight spec: %s\n\n", spec.Path)
fmt.Fprintf(r.w, "==> Linting preflight spec\nPath: %s\n\n", spec.Path)

if len(spec.Messages) == 0 {
fmt.Fprintf(r.w, "No issues found\n")
Expand All @@ -771,8 +789,10 @@ func (r *runners) displayPreflightResults(results *PreflightLintResults) error {
}
}

fmt.Fprintf(r.w, "\nSummary for %s: %d error(s), %d warning(s), %d info\n",
spec.Path, spec.Summary.ErrorCount, spec.Summary.WarningCount, spec.Summary.InfoCount)
fmt.Fprintf(r.w, "\nSummary:\n")
fmt.Fprintf(r.w, " Path: %s\n", spec.Path)
fmt.Fprintf(r.w, " Errors: %d, Warnings: %d, Info: %d\n",
spec.Summary.ErrorCount, spec.Summary.WarningCount, spec.Summary.InfoCount)

if spec.Success {
fmt.Fprintf(r.w, "Status: Passed\n\n")
Expand Down Expand Up @@ -828,7 +848,7 @@ func (r *runners) displaySupportBundleResults(results *SupportBundleLintResults)
fmt.Fprintln(r.w)

for _, spec := range results.Specs {
fmt.Fprintf(r.w, "==> Linting support bundle spec: %s\n\n", spec.Path)
fmt.Fprintf(r.w, "==> Linting support bundle spec\nPath: %s\n\n", spec.Path)

if len(spec.Messages) == 0 {
fmt.Fprintf(r.w, "No issues found\n")
Expand All @@ -842,8 +862,10 @@ func (r *runners) displaySupportBundleResults(results *SupportBundleLintResults)
}
}

fmt.Fprintf(r.w, "\nSummary for %s: %d error(s), %d warning(s), %d info\n",
spec.Path, spec.Summary.ErrorCount, spec.Summary.WarningCount, spec.Summary.InfoCount)
fmt.Fprintf(r.w, "\nSummary:\n")
fmt.Fprintf(r.w, " Path: %s\n", spec.Path)
fmt.Fprintf(r.w, " Errors: %d, Warnings: %d, Info: %d\n",
spec.Summary.ErrorCount, spec.Summary.WarningCount, spec.Summary.InfoCount)

if spec.Success {
fmt.Fprintf(r.w, "Status: Passed\n\n")
Expand Down
37 changes: 33 additions & 4 deletions pkg/tools/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,27 @@ func NewConfigParser() *ConfigParser {
return &ConfigParser{}
}

// ConfigResult holds both the parsed config and the paths of discovered config files
type ConfigResult struct {
Config *Config
ConfigPaths []string // Paths to all discovered config files (ordered from child to parent)
}

// FindAndParseConfig searches for a .replicated config file starting from the given path
// and walking up the directory tree. If path is empty, starts from current directory.
// Returns the parsed config or a default config if not found.
func (p *ConfigParser) FindAndParseConfig(startPath string) (*Config, error) {
result, err := p.FindAndParseConfigWithPaths(startPath)
if err != nil {
return nil, err
}
return result.Config, nil
}

// FindAndParseConfigWithPaths is like FindAndParseConfig but also returns the paths
// of all discovered config files. This is useful for verbose output to show users
// which config files are being used.
func (p *ConfigParser) FindAndParseConfigWithPaths(startPath string) (*ConfigResult, error) {
if startPath == "" {
var err error
startPath, err = os.Getwd()
Expand All @@ -46,7 +63,10 @@ func (p *ConfigParser) FindAndParseConfig(startPath string) (*Config, error) {
}
// Apply defaults for single-file case
p.ApplyDefaults(config)
return config, nil
return &ConfigResult{
Config: config,
ConfigPaths: []string{absPath},
}, nil
}

// Collect all config files from current dir to root
Expand Down Expand Up @@ -82,7 +102,10 @@ func (p *ConfigParser) FindAndParseConfig(startPath string) (*Config, error) {
// No config files found - return default config for auto-discovery mode
if len(configPaths) == 0 {
defaultConfig := p.DefaultConfig()
return defaultConfig, nil
return &ConfigResult{
Config: defaultConfig,
ConfigPaths: []string{},
}, nil
}

// If only one config, parse it and apply defaults
Expand All @@ -93,7 +116,10 @@ func (p *ConfigParser) FindAndParseConfig(startPath string) (*Config, error) {
}
// Apply defaults to single config
p.ApplyDefaults(config)
return config, nil
return &ConfigResult{
Config: config,
ConfigPaths: configPaths,
}, nil
}

// Multiple configs found - parse and merge them
Expand All @@ -116,7 +142,10 @@ func (p *ConfigParser) FindAndParseConfig(startPath string) (*Config, error) {
// Deduplicate resources (charts, preflights, manifests)
p.deduplicateResources(merged)

return merged, nil
return &ConfigResult{
Config: merged,
ConfigPaths: configPaths,
}, nil
}

// mergeConfigs merges multiple configs with later configs taking precedence
Expand Down