// Package env provides a minimal, dependency-free .env file loader. // // It reads KEY=VAL pairs from a file and sets them into the process // environment using os.Setenv, but only for variables that are NOT already // set. This gives the real environment (system env / shell) priority over // values in the .env file. package env import ( "bufio" "fmt" "os" "strings" ) const DefaultPath = ".env" func LoadEnvDefault() error { return LoadEnv(DefaultPath) } func LoadEnv(path string) error { f, err := os.Open(path) if err != nil { return err } defer f.Close() scanner := bufio.NewScanner(f) for scanner.Scan() { line := strings.TrimSpace(scanner.Text()) if line == "" || strings.HasPrefix(line, "#") { continue } key, value, ok := strings.Cut(line, "=") if !ok { continue } key = strings.TrimSpace(key) if key == "" { continue } value = strings.TrimSpace(value) value = unquote(value) if _, exists := os.LookupEnv(key); exists { continue } if err := os.Setenv(key, value); err != nil { return fmt.Errorf("failed to set env var %q: %w", key, err) } } if err := scanner.Err(); err != nil { return fmt.Errorf("failed to read %s: %w", path, err) } return nil } func unquote(value string) string { if len(value) >= 2 { first, last := value[0], value[len(value)-1] if (first == '"' && last == '"') || (first == '\'' && last == '\'') { return value[1 : len(value)-1] } } return value }