Centralize effective config loading and path resolution
This commit is contained in:
@@ -375,30 +375,28 @@ func runProcess(args []string, stdout, stderr io.Writer) int {
|
||||
fmt.Fprintf(stderr, "audita process: invalid CLI configuration: %v\n", err)
|
||||
return 2
|
||||
}
|
||||
configPath, configSource, err := resolveConfigPath(configPathOverride, configPathOverrideSet, os.LookupEnv)
|
||||
effectiveConfig, err := config.LoadEffectiveConfig(configPathOverride, configPathOverrideSet)
|
||||
if err != nil {
|
||||
fmt.Fprintf(stderr, "audita process: %v\n", err)
|
||||
var effectiveConfigErr *config.EffectiveConfigError
|
||||
if errors.As(err, &effectiveConfigErr) {
|
||||
switch effectiveConfigErr.Kind {
|
||||
case config.EffectiveConfigErrorLoadFile, config.EffectiveConfigErrorApplyFile:
|
||||
fmt.Fprintf(stderr, "audita process: invalid config file: %v\n", effectiveConfigErr)
|
||||
case config.EffectiveConfigErrorApplyEnv:
|
||||
fmt.Fprintf(stderr, "audita process: invalid environment configuration: %v\n", effectiveConfigErr)
|
||||
default:
|
||||
fmt.Fprintf(stderr, "audita process: %v\n", effectiveConfigErr)
|
||||
}
|
||||
} else {
|
||||
fmt.Fprintf(stderr, "audita process: %v\n", err)
|
||||
}
|
||||
return 2
|
||||
}
|
||||
|
||||
cfg := config.Default()
|
||||
var configVersion *int
|
||||
if configPath != "" {
|
||||
fileCfg, fileErr := config.LoadFileConfig(configPath)
|
||||
if fileErr != nil {
|
||||
fmt.Fprintf(stderr, "audita process: invalid config file: %v\n", fileErr)
|
||||
return 2
|
||||
}
|
||||
if applyErr := cfg.ApplyFileConfig(fileCfg); applyErr != nil {
|
||||
fmt.Fprintf(stderr, "audita process: invalid config file: %v\n", applyErr)
|
||||
return 2
|
||||
}
|
||||
configVersion = &fileCfg.Version
|
||||
}
|
||||
if err := cfg.ApplyEnvOverrides(); err != nil {
|
||||
fmt.Fprintf(stderr, "audita process: invalid environment configuration: %v\n", err)
|
||||
return 2
|
||||
}
|
||||
cfg := effectiveConfig.Config
|
||||
configPath := effectiveConfig.ConfigPath
|
||||
configSource := effectiveConfig.ConfigSource
|
||||
configVersion := effectiveConfig.ConfigVersion
|
||||
|
||||
fs, pFlags := newProcessFlagSet(cfg, stderr)
|
||||
|
||||
@@ -679,28 +677,13 @@ func runConfigPrintEffective(args []string, stdout, stderr io.Writer) int {
|
||||
|
||||
configPathValue := strings.TrimSpace(*configPath)
|
||||
configPathSet := configPathValue != ""
|
||||
path, _, err := resolveConfigPath(configPathValue, configPathSet, os.LookupEnv)
|
||||
effectiveConfig, err := config.LoadEffectiveConfig(configPathValue, configPathSet)
|
||||
if err != nil {
|
||||
fmt.Fprintf(stderr, "audita config print-effective: %v\n", err)
|
||||
return 2
|
||||
}
|
||||
|
||||
cfg := config.Default()
|
||||
if path != "" {
|
||||
fileCfg, fileErr := config.LoadFileConfig(path)
|
||||
if fileErr != nil {
|
||||
fmt.Fprintf(stderr, "audita config print-effective: %v\n", fileErr)
|
||||
return 2
|
||||
}
|
||||
if applyErr := cfg.ApplyFileConfig(fileCfg); applyErr != nil {
|
||||
fmt.Fprintf(stderr, "audita config print-effective: %v\n", applyErr)
|
||||
return 2
|
||||
}
|
||||
}
|
||||
if err := cfg.ApplyEnvOverrides(); err != nil {
|
||||
fmt.Fprintf(stderr, "audita config print-effective: %v\n", err)
|
||||
return 2
|
||||
}
|
||||
cfg := effectiveConfig.Config
|
||||
|
||||
redacted := cfg.Redacted()
|
||||
out, err := json.MarshalIndent(redacted, "", " ")
|
||||
@@ -975,47 +958,6 @@ func findConfigPathOverride(args []string) (path string, set bool, err error) {
|
||||
return "", false, nil
|
||||
}
|
||||
|
||||
var statConfigPath = os.Stat
|
||||
|
||||
func resolveConfigPath(cliConfigPath string, cliConfigPathSet bool, lookup func(string) (string, bool)) (path string, source string, err error) {
|
||||
if cliConfigPathSet {
|
||||
path = strings.TrimSpace(cliConfigPath)
|
||||
if path == "" {
|
||||
return "", "", fmt.Errorf("--config requires a non-empty path")
|
||||
}
|
||||
if _, statErr := statConfigPath(path); statErr != nil {
|
||||
if os.IsNotExist(statErr) {
|
||||
return "", "", fmt.Errorf("config file not found: %s", path)
|
||||
}
|
||||
return "", "", fmt.Errorf("cannot access config file %s: %w", path, statErr)
|
||||
}
|
||||
return path, "flag", nil
|
||||
}
|
||||
|
||||
if raw, ok := lookup("AUDITA_CONFIG"); ok {
|
||||
path = strings.TrimSpace(raw)
|
||||
if path == "" {
|
||||
return "", "", fmt.Errorf("AUDITA_CONFIG must not be empty")
|
||||
}
|
||||
if _, statErr := statConfigPath(path); statErr != nil {
|
||||
if os.IsNotExist(statErr) {
|
||||
return "", "", fmt.Errorf("config file not found: %s", path)
|
||||
}
|
||||
return "", "", fmt.Errorf("cannot access config file %s: %w", path, statErr)
|
||||
}
|
||||
return path, "env", nil
|
||||
}
|
||||
|
||||
for _, defaultPath := range config.DefaultConfigSearchPaths {
|
||||
if _, statErr := statConfigPath(defaultPath); statErr == nil {
|
||||
return defaultPath, "default", nil
|
||||
} else if !os.IsNotExist(statErr) {
|
||||
return "", "", fmt.Errorf("cannot access config file %s: %w", defaultPath, statErr)
|
||||
}
|
||||
}
|
||||
return "", "", nil
|
||||
}
|
||||
|
||||
func isHelpCommand(args []string) bool {
|
||||
if len(args) == 0 {
|
||||
return false
|
||||
|
||||
@@ -98,66 +98,6 @@ func TestRunProcessHelpListsExpectedFlags(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveConfigPathDefaultIgnoredWhenMissing(t *testing.T) {
|
||||
lookup := func(string) (string, bool) { return "", false }
|
||||
path, source, err := resolveConfigPath("", false, lookup)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if path != "" || source != "" {
|
||||
t.Fatalf("expected no config path/source, got path=%q source=%q", path, source)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveConfigPathDefaultPrefersUsrLocalOverEtc(t *testing.T) {
|
||||
oldStat := statConfigPath
|
||||
statConfigPath = func(path string) (os.FileInfo, error) {
|
||||
if path == config.DefaultConfigPathUsrLocal || path == config.DefaultConfigPath {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, os.ErrNotExist
|
||||
}
|
||||
t.Cleanup(func() { statConfigPath = oldStat })
|
||||
|
||||
lookup := func(string) (string, bool) { return "", false }
|
||||
path, source, err := resolveConfigPath("", false, lookup)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if source != "default" {
|
||||
t.Fatalf("expected default source, got %q", source)
|
||||
}
|
||||
if path != config.DefaultConfigPathUsrLocal {
|
||||
t.Fatalf("expected %q, got %q", config.DefaultConfigPathUsrLocal, path)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveConfigPathDefaultFallsBackToEtc(t *testing.T) {
|
||||
oldStat := statConfigPath
|
||||
statConfigPath = func(path string) (os.FileInfo, error) {
|
||||
if path == config.DefaultConfigPathUsrLocal {
|
||||
return nil, os.ErrNotExist
|
||||
}
|
||||
if path == config.DefaultConfigPath {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, os.ErrNotExist
|
||||
}
|
||||
t.Cleanup(func() { statConfigPath = oldStat })
|
||||
|
||||
lookup := func(string) (string, bool) { return "", false }
|
||||
path, source, err := resolveConfigPath("", false, lookup)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if source != "default" {
|
||||
t.Fatalf("expected default source, got %q", source)
|
||||
}
|
||||
if path != config.DefaultConfigPath {
|
||||
t.Fatalf("expected %q, got %q", config.DefaultConfigPath, path)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunConfigValidateSuccess(t *testing.T) {
|
||||
var stdout bytes.Buffer
|
||||
var stderr bytes.Buffer
|
||||
@@ -258,6 +198,45 @@ func TestRunConfigPrintEffectiveOutputsRedactedJSON(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunConfigPrintEffectiveAppliesFileThenEnvironment(t *testing.T) {
|
||||
var stdout bytes.Buffer
|
||||
var stderr bytes.Buffer
|
||||
cfgPath := writeFile(t, "config.yml", "version: 1\nllm:\n proposal:\n model: file-model\n")
|
||||
t.Setenv("AUDITA_MODEL", "env-model")
|
||||
|
||||
exitCode := Run([]string{"config", "print-effective", "--config", cfgPath}, &stdout, &stderr)
|
||||
if exitCode != 0 {
|
||||
t.Fatalf("expected success, got %d stderr=%q", exitCode, stderr.String())
|
||||
}
|
||||
|
||||
var out struct {
|
||||
PrimaryLLM struct {
|
||||
Model string `json:"Model"`
|
||||
} `json:"PrimaryLLM"`
|
||||
}
|
||||
if err := json.Unmarshal(stdout.Bytes(), &out); err != nil {
|
||||
t.Fatalf("expected valid JSON output, got error: %v output=%q", err, stdout.String())
|
||||
}
|
||||
if out.PrimaryLLM.Model != "env-model" {
|
||||
t.Fatalf("expected env model override in print-effective output, got %q", out.PrimaryLLM.Model)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunConfigValidateIgnoresEnvironmentOverrides(t *testing.T) {
|
||||
var stdout bytes.Buffer
|
||||
var stderr bytes.Buffer
|
||||
cfgPath := writeFile(t, "config.yml", "version: 1\n")
|
||||
t.Setenv("AUDITA_MODULES", "made_up")
|
||||
|
||||
exitCode := Run([]string{"config", "validate", "--config", cfgPath}, &stdout, &stderr)
|
||||
if exitCode != 0 {
|
||||
t.Fatalf("expected success because config validate is file-only, got %d stderr=%q", exitCode, stderr.String())
|
||||
}
|
||||
if !strings.Contains(stdout.String(), "config is valid") {
|
||||
t.Fatalf("expected success message, got %q", stdout.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunConfigCommandDoesNotRequireTranscriptOrGlossary(t *testing.T) {
|
||||
var stdout bytes.Buffer
|
||||
var stderr bytes.Buffer
|
||||
|
||||
Reference in New Issue
Block a user