94 lines
2.8 KiB
Go
94 lines
2.8 KiB
Go
package app
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
// ResolveConfigPath determines which controller configuration file to load.
|
|
// Explicit CLI and environment paths are authoritative. Without either, the
|
|
// current directory, executable directory, and container default are checked.
|
|
func ResolveConfigPath(cliPath, envPath string) (string, error) {
|
|
if path := strings.TrimSpace(cliPath); path != "" {
|
|
return requireConfigFile(path, "-config")
|
|
}
|
|
if path := strings.TrimSpace(envPath); path != "" {
|
|
return requireConfigFile(path, "ENV_CONTROLLER_CONFIG")
|
|
}
|
|
|
|
candidates := make([]string, 0, 3)
|
|
if cwd, err := os.Getwd(); err == nil {
|
|
candidates = append(candidates, filepath.Join(cwd, "controller.json"))
|
|
}
|
|
if executable, err := os.Executable(); err == nil {
|
|
candidates = append(candidates, filepath.Join(filepath.Dir(executable), "controller.json"))
|
|
}
|
|
candidates = append(candidates, filepath.FromSlash("/config/controller.json"))
|
|
candidates = uniqueCleanPaths(candidates)
|
|
|
|
for _, candidate := range candidates {
|
|
info, err := os.Stat(candidate)
|
|
if err == nil && !info.IsDir() {
|
|
absolute, absErr := filepath.Abs(candidate)
|
|
if absErr == nil {
|
|
return absolute, nil
|
|
}
|
|
return candidate, nil
|
|
}
|
|
}
|
|
|
|
exampleHints := make([]string, 0, 2)
|
|
for _, candidate := range candidates {
|
|
example := filepath.Join(filepath.Dir(candidate), "controller.example.json")
|
|
if info, err := os.Stat(example); err == nil && !info.IsDir() {
|
|
exampleHints = append(exampleHints, example)
|
|
}
|
|
}
|
|
|
|
message := fmt.Sprintf("controller configuration not found; searched: %s", strings.Join(candidates, ", "))
|
|
if len(exampleHints) > 0 {
|
|
message += fmt.Sprintf("; create controller.json from: %s", strings.Join(uniqueCleanPaths(exampleHints), ", "))
|
|
}
|
|
message += "; alternatively pass -config <path> or set ENV_CONTROLLER_CONFIG"
|
|
return "", fmt.Errorf("%s", message)
|
|
}
|
|
|
|
func requireConfigFile(path, source string) (string, error) {
|
|
cleaned := filepath.Clean(path)
|
|
info, err := os.Stat(cleaned)
|
|
if err != nil {
|
|
return "", fmt.Errorf("configuration path from %s %q: %w", source, cleaned, err)
|
|
}
|
|
if info.IsDir() {
|
|
return "", fmt.Errorf("configuration path from %s %q is a directory", source, cleaned)
|
|
}
|
|
absolute, err := filepath.Abs(cleaned)
|
|
if err == nil {
|
|
return absolute, nil
|
|
}
|
|
return cleaned, nil
|
|
}
|
|
|
|
func uniqueCleanPaths(paths []string) []string {
|
|
seen := make(map[string]struct{}, len(paths))
|
|
result := make([]string, 0, len(paths))
|
|
for _, path := range paths {
|
|
if strings.TrimSpace(path) == "" {
|
|
continue
|
|
}
|
|
cleaned := filepath.Clean(path)
|
|
key := cleaned
|
|
if filepath.Separator == '\\' {
|
|
key = strings.ToLower(cleaned)
|
|
}
|
|
if _, exists := seen[key]; exists {
|
|
continue
|
|
}
|
|
seen[key] = struct{}{}
|
|
result = append(result, cleaned)
|
|
}
|
|
return result
|
|
}
|