package compass import ( "fmt" "os" "strings" "unicode" "github.com/knadh/koanf/providers/env/v2" "github.com/knadh/koanf/v2" ) const envPrefix = "SUMMER_" func loadEnvVars(k *koanf.Koanf, environ []string) error { if k == nil { return nil } return k.Load(env.Provider(".", env.Opt{ Prefix: envPrefix, EnvironFunc: func() []string { if environ == nil { return []string{} } return environ }, TransformFunc: transformSummerKey, }), nil) } func transformSummerKey(key, value string) (string, any) { if key == "SUMMER_ENV" { return "", nil } rest, ok := strings.CutPrefix(key, envPrefix) if !ok || rest == "" { return "", nil } parts := strings.Split(rest, "__") for i, part := range parts { parts[i] = strings.ToLower(part) } return strings.Join(parts, "."), value } func readDotEnv(path string) ([]string, error) { raw, err := os.ReadFile(path) if err != nil { if os.IsNotExist(err) { return nil, nil } return nil, fmt.Errorf("compass: read .env: %w", err) } var out []string for _, line := range strings.Split(string(raw), "\n") { line = strings.TrimSpace(line) if line == "" || strings.HasPrefix(line, "#") { continue } if after, ok := strings.CutPrefix(line, "export "); ok { line = strings.TrimSpace(after) } key, val, ok := strings.Cut(line, "=") if !ok { continue } key = strings.TrimSpace(key) if key == "" { continue } val = strings.TrimSpace(val) if len(val) >= 2 { if (val[0] == '"' && val[len(val)-1] == '"') || (val[0] == '\'' && val[len(val)-1] == '\'') { val = val[1 : len(val)-1] } } out = append(out, key+"="+val) } return out, nil } func mergeEnviron(real, dotenv []string) []string { present := make(map[string]struct{}, len(real)) out := make([]string, 0, len(real)+len(dotenv)) for _, kv := range real { key, _, _ := strings.Cut(kv, "=") present[key] = struct{}{} out = append(out, kv) } for _, kv := range dotenv { key, _, _ := strings.Cut(kv, "=") if _, ok := present[key]; ok { continue } out = append(out, kv) } return out } func lookupEnv(environ []string, key string) string { for i := len(environ) - 1; i >= 0; i-- { k, v, ok := strings.Cut(environ[i], "=") if ok && k == key { return v } } return "" } func sanitizeEnv(env string) (string, error) { if env == "" { return "", fmt.Errorf("compass: environment name is empty") } if env == "." || env == ".." || strings.Contains(env, "..") || strings.ContainsAny(env, `/\`) { return "", fmt.Errorf("compass: invalid environment name") } for _, r := range env { if unicode.IsLetter(r) || unicode.IsDigit(r) || r == '-' || r == '_' { continue } return "", fmt.Errorf("compass: invalid environment name") } return env, nil }