Files
httprunner/internal/config/env.go
lilong.129 184081592c feat: add global .env file support from ~/.hrp/.env
- Add support for loading environment variables from ~/.hrp/.env
- Implement priority order: current working directory > ~/.hrp/.env > system environment variables
- Use godotenv.Overload() for both global and local .env files to ensure proper priority
- Maintain backward compatibility with existing functionality
- Add comprehensive error handling and logging
2025-06-02 11:52:36 +08:00

126 lines
3.0 KiB
Go

package config
import (
"encoding/json"
"os"
"path/filepath"
"strconv"
"sync"
"github.com/joho/godotenv"
"github.com/rs/zerolog/log"
)
var loadEnvOnce sync.Once
// LoadEnv loads environment variables from .env file
// it will search for .env file from current working directory upward recursively
// if not found, it will try to load from ~/.hrp/.env as fallback
// Priority: current working directory > ~/.hrp/.env > system environment variables
func LoadEnv() (err error) {
loadEnvOnce.Do(func() {
// first try to load from ~/.hrp/.env, override system env variables (medium priority)
var homeDir string
homeDir, err = os.UserHomeDir()
if err != nil {
log.Warn().Err(err).Msg("get user home directory failed")
} else {
globalEnvFile := filepath.Join(homeDir, ".hrp", ".env")
if _, e := os.Stat(globalEnvFile); e == nil {
// load global .env file and override system environment variables
err = godotenv.Overload(globalEnvFile)
if err != nil {
log.Error().Err(err).
Str("path", globalEnvFile).Msg("load global env file failed")
return
}
log.Info().Str("path", globalEnvFile).Msg("load global env success")
}
}
// get current working directory
var cwd string
cwd, err = os.Getwd()
if err != nil {
log.Error().Err(err).Msg("get current working directory failed")
return
}
// locate .env file from current working directory upward recursively
envPath := cwd
for {
envFile := filepath.Join(envPath, ".env")
if _, e := os.Stat(envFile); e == nil {
// found .env file
// override existing env variables (highest priority)
err = godotenv.Overload(envFile)
if err != nil {
log.Error().Err(err).
Str("path", envFile).Msg("overload env file failed")
return
}
log.Info().Str("path", envFile).Msg("overload env success")
return
}
// reached root directory
parent := filepath.Dir(envPath)
if parent == envPath {
log.Info().Msg("no .env file found from current directory to root")
return
}
envPath = parent
}
})
return err
}
func GetEnvConfig(key string) string {
return os.Getenv(key)
}
func GetEnvConfigInJSON(key string) (map[string]interface{}, error) {
value := GetEnvConfig(key)
if value == "" {
return nil, nil
}
var result map[string]interface{}
if err := json.Unmarshal([]byte(value), &result); err != nil {
return nil, err
}
return result, nil
}
func GetEnvConfigInBool(key string) bool {
value := GetEnvConfig(key)
if value == "" {
return false
}
boolValue, _ := strconv.ParseBool(value)
return boolValue
}
// GetEnvConfigOrDefault get env config or default value
func GetEnvConfigOrDefault(key, defaultValue string) string {
value := GetEnvConfig(key)
if value == "" {
return defaultValue
}
return value
}
func GetEnvConfigInInt(key string, defaultValue int) int {
value := GetEnvConfig(key)
if value == "" {
return defaultValue
}
intValue, err := strconv.Atoi(value)
if err != nil {
return defaultValue
}
return intValue
}