如何在godotenv.Load()中使用动态位置来加载.env文件?

huangapple go评论85阅读模式
英文:

How to use dynamic location for godotenv.Load() .env file?

问题

问题

我正在使用Go构建一个REST API。使用godotenv包来加载环境变量。运行go run main.go,项目按预期运行,环境变量被加载。

然而,当想要运行测试时,使用命令go test ./...,其中包括运行config/config_test.go,会抛出以下错误:Error loading .env file(如函数中所指定)。

给定以下项目结构:

> app
> auth
> config
  - config.go
  - config_test.go
> migrations
> static
> vendor
- .env
- .gitignore
- docker-compose.yml
- go.mod
- go.sum
- main.go
- README.md

config.go中,我使用以下函数来加载数据库配置。

func GetConfig() *Config {
    err := godotenv.Load(".env")

    if err != nil {
        log.Fatalf("Error loading .env file")
    }

    dbHost := os.Getenv("DB_HOST")
    dbPort := os.Getenv("DB_PORT")
    dbName := os.Getenv("DB_DATABASE")
    dbUsername := os.Getenv("DB_USERNAME")
    dbPassword := os.Getenv("DB_PASSWORD")

    return &Config{
        DB: &DBConfig{
            Connection: "mysql",
            Host:       dbHost,
            Port:       dbPort,
            Username:   dbUsername,
            Password:   dbPassword,
            Name:       dbName,
            Charset:    "utf8",
        },
    }
}

我理解当从根目录运行时它可以工作,因为.env位于根目录。当运行config/config_test.go时,它会尝试在/config/.env中查找.env文件。如果我将这行代码更改为err := godotenv.Load("../.env"),则config_test.go可以成功运行,但是从根目录运行的go run main.go将无法成功运行。

问题

如何在config.goGetConfig()函数中动态加载.env文件的位置,以便go test ./...go run main.go都可以加载.env文件?

编辑

我知道在我的应用程序中将path string参数传递给GetConfig()函数可以解决这个问题(我正在app包中初始化此配置)。然而,我想在不同目录中创建多个测试,并且不希望传递参数。有没有其他方法可以实现这一点?

英文:

Problem

I am building a REST API in Go. The godotenv package is used to load the environment variables. Running go run main.go, the project runs the API as expected, the environment variables are loaded.

However, when wanting to run the test using: go test ./... - which runs config/config_test.go among others - it throws the following error: Error loading .env file (as specified in function).

Given the following project structure:

> app
> auth
> config
  - config.go
  - config_test.go
> migrations
> static
> vendor
- .env
- .gitignore
- docker-compose.yml
- go.mod
- go.sum
- main.go
- README.md

In config.go, I use the following function to load the Database configuration.

func GetConfig() *Config {
	err := godotenv.Load(".env")

	if err != nil {
		log.Fatalf("Error loading .env file")
	}

	dbHost := os.Getenv("DB_HOST")
	dbPort := os.Getenv("DB_PORT")
	dbName := os.Getenv("DB_DATABASE")
	dbUsername := os.Getenv("DB_USERNAME")
	dbPassword := os.Getenv("DB_PASSWORD")

	return &Config{
		DB: &DBConfig{
			Connection: "mysql",
			Host:       dbHost,
			Port:       dbPort,
			Username:   dbUsername,
			Password:   dbPassword,
			Name:       dbName,
			Charset:    "utf8",
		},
	}
}

I understand that it works when running from root, because the .env resides in root. When running config/config_test.go, it tries to look for the .env file in the /config/.env. If I change the line: err := godotenv.Load(".env") to err := godotenv.Load("../.env"), the config_test.go runs successfully, but the go run main.go from root does not run successfully.

Question

How can I load the .env location dynamically from the GetConfig() function in config.go, so that both the go test ./... and go run main.go can load the .env?

Edit

I am aware that passing a path string parameter to the GetConfig() function would work in my application (I am initializing this config in the app package). However, I want to create multiple tests in different directories, and prefer not to pass a parameter. Is there another way to accomplish this?

答案1

得分: 10

根据@Inian的建议,我实现了以下解决方案,也在godotenv包的Issues标签页上列出。

config.go中,我添加了一个目录名称的常量(在我的情况下是rest-api)。我添加了一个loadEnv函数,该函数尝试动态获取项目的根路径,基于项目名称和当前工作目录。

const projectDirName = "rest-api" // 更改为相关项目名称

func loadEnv() {
    projectName := regexp.MustCompile(`^(.*` + projectDirName + `)`)
    currentWorkDirectory, _ := os.Getwd()
    rootPath := projectName.Find([]byte(currentWorkDirectory))

    err := godotenv.Load(string(rootPath) + `/.env`)

    if err != nil {
        log.Fatalf("Error loading .env file")
    }
}

func GetConfig() *Config {
    loadEnv()

    dbHost := os.Getenv("DB_HOST")
    dbPort := os.Getenv("DB_PORT")
    dbName := os.Getenv("DB_DATABASE")
    dbUsername := os.Getenv("DB_USERNAME")
    dbPassword := os.Getenv("DB_PASSWORD")

    return &Config{
        DB: &DBConfig{
            Connection: "mysql",
            Host:       dbHost,
            Port:       dbPort,
            Username:   dbUsername,
            Password:   dbPassword,
            Name:       dbName,
            Charset:    "utf8",
        },
    }
}

以上是翻译好的内容,请确认是否满意。

英文:

Following the suggestion of @Inian, I implemented the following solution, also listed on the Issues tab of the godotenv package.

In config.go I added a constant for the directory name (which is rest-api in my case). I added a loadEnv function that tries to get the root path of the project dynamically, based on the project name and the current working directory.

const projectDirName = "rest-api" // change to relevant project name

func loadEnv() {
	projectName := regexp.MustCompile(`^(.*` + projectDirName + `)`)
	currentWorkDirectory, _ := os.Getwd()
	rootPath := projectName.Find([]byte(currentWorkDirectory))

	err := godotenv.Load(string(rootPath) + `/.env`)

	if err != nil {
		log.Fatalf("Error loading .env file")
	}
}

func GetConfig() *Config {
	loadEnv()

	dbHost := os.Getenv("DB_HOST")
	dbPort := os.Getenv("DB_PORT")
	dbName := os.Getenv("DB_DATABASE")
	dbUsername := os.Getenv("DB_USERNAME")
	dbPassword := os.Getenv("DB_PASSWORD")

	return &Config{
		DB: &DBConfig{
			Connection: "mysql",
			Host:       dbHost,
			Port:       dbPort,
			Username:   dbUsername,
			Password:   dbPassword,
			Name:       dbName,
			Charset:    "utf8",
		},
	}
}

huangapple
  • 本文由 发表于 2021年7月12日 19:15:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/68346410.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定