Rationalized the default configuration file path and updated documentation
All checks were successful
ci/woodpecker/tag/release Pipeline was successful
All checks were successful
ci/woodpecker/tag/release Pipeline was successful
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
`config.yml` defines application-level defaults used by CLI commands.
|
`config.yml` defines application-level defaults used by CLI commands.
|
||||||
|
|
||||||
By default, Scriptorium looks for `/etc/scriptorium/config.yml`. You can also pass `--config PATH`.
|
By default, Scriptorium looks for `/usr/local/etc/scriptorium/config.yml` and, if not present, then for `/etc/scriptorium/config.yml`. You can also pass `--config PATH`.
|
||||||
|
|
||||||
## Complete Example
|
## Complete Example
|
||||||
|
|
||||||
|
|||||||
@@ -450,7 +450,16 @@ func (c *serveConfig) addrIfSet(fs *flag.FlagSet) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func registerConfigPathFlag(fs *flag.FlagSet, target *string) {
|
func registerConfigPathFlag(fs *flag.FlagSet, target *string) {
|
||||||
fs.StringVar(target, "config", "", fmt.Sprintf("application config path (default %s)", appconfig.DefaultConfigPath))
|
fs.StringVar(
|
||||||
|
target,
|
||||||
|
"config",
|
||||||
|
"",
|
||||||
|
fmt.Sprintf(
|
||||||
|
"application config path (default search: %s, then %s)",
|
||||||
|
appconfig.DefaultConfigPathLocal,
|
||||||
|
appconfig.DefaultConfigPath,
|
||||||
|
),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func resolveAppSettings(fs *flag.FlagSet, configPath string, overrides appconfig.CLIOverrides) (appconfig.AppSettings, error) {
|
func resolveAppSettings(fs *flag.FlagSet, configPath string, overrides appconfig.CLIOverrides) (appconfig.AppSettings, error) {
|
||||||
|
|||||||
@@ -14,7 +14,15 @@ import (
|
|||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
const DefaultConfigPath = "/etc/scriptorium/config.yml"
|
const (
|
||||||
|
DefaultConfigPath = "/etc/scriptorium/config.yml"
|
||||||
|
DefaultConfigPathLocal = "/usr/local/etc/scriptorium/config.yml"
|
||||||
|
)
|
||||||
|
|
||||||
|
var defaultConfigSearchPaths = []string{
|
||||||
|
DefaultConfigPathLocal,
|
||||||
|
DefaultConfigPath,
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrConfigNotFound = errors.New("config file not found")
|
ErrConfigNotFound = errors.New("config file not found")
|
||||||
@@ -74,7 +82,24 @@ func LoadConfig(path string, explicit bool) (AppSettings, error) {
|
|||||||
resolved := BuiltInDefaults()
|
resolved := BuiltInDefaults()
|
||||||
configPath := strings.TrimSpace(path)
|
configPath := strings.TrimSpace(path)
|
||||||
if configPath == "" {
|
if configPath == "" {
|
||||||
configPath = DefaultConfigPath
|
if explicit {
|
||||||
|
configPath = DefaultConfigPath
|
||||||
|
} else {
|
||||||
|
for _, candidate := range defaultConfigSearchPaths {
|
||||||
|
if _, err := os.Stat(candidate); err == nil {
|
||||||
|
configPath = candidate
|
||||||
|
break
|
||||||
|
} else if !errors.Is(err, os.ErrNotExist) {
|
||||||
|
return AppSettings{}, fmt.Errorf("failed to stat config file %q: %w", candidate, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if configPath == "" {
|
||||||
|
if explicit {
|
||||||
|
return AppSettings{}, fmt.Errorf("%w: %s", ErrConfigNotFound, DefaultConfigPath)
|
||||||
|
}
|
||||||
|
return resolved, nil
|
||||||
}
|
}
|
||||||
configPath = filepath.Clean(configPath)
|
configPath = filepath.Clean(configPath)
|
||||||
|
|
||||||
|
|||||||
@@ -132,6 +132,53 @@ func TestLoadConfigEmptyFileResolvesToBuiltInDefaults(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestLoadConfigImplicitSearchPrefersUsrLocalEtcOverEtc(t *testing.T) {
|
||||||
|
tmp := t.TempDir()
|
||||||
|
localPath := filepath.Join(tmp, "usr-local.yml")
|
||||||
|
etcPath := filepath.Join(tmp, "etc.yml")
|
||||||
|
|
||||||
|
if err := os.WriteFile(localPath, []byte("prompt_dir: ./from-usr-local\n"), 0o644); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(etcPath, []byte("prompt_dir: ./from-etc\n"), 0o644); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
orig := defaultConfigSearchPaths
|
||||||
|
defaultConfigSearchPaths = []string{localPath, etcPath}
|
||||||
|
t.Cleanup(func() { defaultConfigSearchPaths = orig })
|
||||||
|
|
||||||
|
got, err := LoadConfig("", false)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("expected no error, got %v", err)
|
||||||
|
}
|
||||||
|
if got.PromptDir != filepath.Clean("./from-usr-local") {
|
||||||
|
t.Fatalf("expected usr-local config to win, got prompt_dir=%q", got.PromptDir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadConfigImplicitSearchFallsBackToEtcWhenUsrLocalMissing(t *testing.T) {
|
||||||
|
tmp := t.TempDir()
|
||||||
|
missingLocal := filepath.Join(tmp, "missing-local.yml")
|
||||||
|
etcPath := filepath.Join(tmp, "etc.yml")
|
||||||
|
|
||||||
|
if err := os.WriteFile(etcPath, []byte("prompt_dir: ./from-etc\n"), 0o644); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
orig := defaultConfigSearchPaths
|
||||||
|
defaultConfigSearchPaths = []string{missingLocal, etcPath}
|
||||||
|
t.Cleanup(func() { defaultConfigSearchPaths = orig })
|
||||||
|
|
||||||
|
got, err := LoadConfig("", false)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("expected no error, got %v", err)
|
||||||
|
}
|
||||||
|
if got.PromptDir != filepath.Clean("./from-etc") {
|
||||||
|
t.Fatalf("expected etc fallback config, got prompt_dir=%q", got.PromptDir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestApplyCLIOverridesAppliesPrecedence(t *testing.T) {
|
func TestApplyCLIOverridesAppliesPrecedence(t *testing.T) {
|
||||||
base := AppSettings{
|
base := AppSettings{
|
||||||
PromptDir: "/from/config/prompts",
|
PromptDir: "/from/config/prompts",
|
||||||
|
|||||||
Reference in New Issue
Block a user