diff --git a/cmd/bagel/scan.go b/cmd/bagel/scan.go index 343ee53..a9eb41c 100644 --- a/cmd/bagel/scan.go +++ b/cmd/bagel/scan.go @@ -23,6 +23,7 @@ var ( strict bool noCache bool noProgress bool + baseDirs []string ) // scanCmd represents the scan command @@ -42,6 +43,9 @@ func init() { scanCmd.Flags().BoolVar(&strict, "strict", false, "exit with code 2 if any findings are detected") scanCmd.Flags().BoolVar(&noCache, "no-cache", false, "bypass file index cache and force rebuild") scanCmd.Flags().BoolVar(&noProgress, "no-progress", false, "disable progress bars") + scanCmd.Flags().StringSliceVar(&baseDirs, "base-dirs", nil, + "comma-separated directories to scan; overrides file_index.base_dirs. "+ + "e.g. --base-dirs /home,/Users,/root") } func runScan(cmd *cobra.Command, args []string) error { @@ -55,6 +59,12 @@ func runScan(cmd *cobra.Command, args []string) error { return fmt.Errorf("failed to load config: %w", err) } + // --base-dir takes precedence over config/defaults + if len(baseDirs) > 0 { + cfg.FileIndex.BaseDirs = baseDirs + log.Debug().Strs("base_dirs", baseDirs).Msg("Overriding base dirs from --base-dirs") + } + log.Debug().Msg("Starting scan") // Initialize probes diff --git a/pkg/config/config.go b/pkg/config/config.go index 2240199..c82e459 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -37,13 +37,19 @@ func Load(configPath string) (*models.Config, error) { v.SetEnvPrefix("BAGEL") v.AutomaticEnv() - // Read config file if it exists + // Read config file if it exists. + // A missing or unreadable config must never block a scan if err := v.ReadInConfig(); err != nil { var configFileNotFoundError viper.ConfigFileNotFoundError - if !errors.As(err, &configFileNotFoundError) { - return nil, fmt.Errorf("failed to read config file: %w", err) + switch { + case errors.As(err, &configFileNotFoundError): + log.Debug().Msg("config: no config file found; using built-in defaults") + case configPath != "": + return nil, fmt.Errorf("failed to read config file %q: %w", configPath, err) + default: + log.Warn().Err(err).Msg( + "config: could not read a discovered config file; using built-in defaults") } - // Config file not found is OK, we'll use defaults } // Unmarshal config diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 44416ff..ef1a1a3 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -67,3 +67,44 @@ func TestLoad_DefaultsEnableBothAIProbes(t *testing.T) { assert.True(t, cfg.Probes.AICredentials.Enabled) assert.True(t, cfg.Probes.AIChats.Enabled) } + +// TestLoad_NoConfigFileUsesDefaults verifies that when auto-discovery finds no +// config file, Load succeeds with built-in defaults — every probe enabled and +// the file-index patterns populated. +func TestLoad_NoConfigFileUsesDefaults(t *testing.T) { + t.Setenv("HOME", t.TempDir()) // empty home, no bagel.yaml + t.Chdir(t.TempDir()) // empty cwd, no bagel.yaml on "." path + + cfg, err := Load("") + require.NoError(t, err) + require.NotNil(t, cfg) + + assert.True(t, cfg.Probes.Git.Enabled) + assert.True(t, cfg.Probes.Cloud.Enabled) + assert.NotEmpty(t, cfg.FileIndex.Patterns) +} + +// TestLoad_DiscoveredConfigUnusableFallsBack verifies that when auto-discovery +// cannot turn up a usable config file, Load does not abort the scan: it falls +// back to defaults instead of erroring. +func TestLoad_DiscoveredConfigUnusableFallsBack(t *testing.T) { + dir := t.TempDir() + // A directory named bagel.yaml means there's no readable config file to + // load, standing in for an absent/inaccessible config under auto-discovery. + require.NoError(t, os.Mkdir(filepath.Join(dir, "bagel.yaml"), 0o755)) + t.Chdir(dir) + + cfg, err := Load("") + require.NoError(t, err) + require.NotNil(t, cfg) + assert.True(t, cfg.Probes.Git.Enabled) +} + +// TestLoad_ExplicitConfigFailsLoudly verifies the opposite contract: when the +// user explicitly names a --config file that cannot be read, Load returns an +// error rather than silently using defaults. +func TestLoad_ExplicitConfigFailsLoudly(t *testing.T) { + missing := filepath.Join(t.TempDir(), "does-not-exist.yaml") + _, err := Load(missing) + require.Error(t, err) +}