Home > Blockchain >  Need to load .env file before any init function is run
Need to load .env file before any init function is run

Time:07-29

I'm trying to load the environment variable with calling godotenv.Load in my main method but the problem is that I have init functions in other packages and they run before the environment variables are loaded from .env file, therefore causing trouble.

Is there any workaround for this?

CodePudding user response:

Golang initializes imported packages before anything else. This causes the problem with setting the envvars in the main() function. Luckily Godotenv has an autoloader function that uses import declaration to autoload an .env file from project root folder. Imports are evaluated in declaration order, so you must declare the import before all packages that depend on the envvars:

import _ "github.com/joho/godotenv/autoload"

To import a package solely for its initialization, you should use the blank identifier as explicit package name.

CodePudding user response:

Here's the spec for Golang's package initialization: https://go.dev/ref/spec#Package_initialization

In a nutshell, for a given package,

  1. All packages imported by the current package are initialized in lexical order by source file name and then in the order in which they are imported in each source file.

  2. All package-level variables in the current package are initialized in dependency order.

  3. All init() functions defined within the current package are then executed in lexical order by source file name, and then in document order within each file.

  4. Once all packages have been initialized, the main() function is invoked, starting execution of the program.

Per the spec,

Package initialization—variable initialization and the invocation of init functions—happens in a single goroutine, sequentially, one package at a time.

All you need to do is ensure that your loading/setting of environment variable happens in a package that gets initialized first. Note however that go fmt will alphabetize your imports.

So, given a program with 4 packages, main, alpha, bravo, and charlie,

where the dependencies are:

  • main imports alpha and bravo, and
  • alpha imports charlie

and each package has multiple init() functions (source code below), executing go run main.go writes the following to the console:

charlie/main.go: init: 1
charlie/main.go: init: 2
charlie/main.go: init: 3
alpha/main.go: init: 1
alpha/main.go: init: 2
alpha/main.go: init: 3
bravo/main.go: init: 1
bravo/main.go: init: 2
bravo/main-.go: init: 3
main.go: init: 1
main.go: init: 2
main.go: init: 3
main(): executing

./main.go

package main

import (
    "fmt"

    "some-host/init-example/alpha"
    "some-host/init-example/bravo"
)

func init() {
    fmt.Println("main.go: init: 1")
}

func init() {
    fmt.Println("main.go: init: 2")
}

func main() {
    fmt.Println("main(): executing")
    alpha.DoSomething()
    bravo.DoSomething()
}

func init() {
    fmt.Println("main.go: init: 3")
}

./alpha/alpha.go

package alpha

import (
    "fmt"
    "some-host/init-example/charlie"
)

func init() {
    fmt.Println("alpha/main.go: init: 1")
}

func init() {
    fmt.Println("alpha/main.go: init: 2")
}

func DoSomething() {
    charlie.DoSomething()
}

func init() {
    fmt.Println("alpha/main.go: init: 3")
}

./bravo/bravo.go

package bravo

import "fmt"

func init() {
    fmt.Println("bravo/main.go: init: 1")
}

func init() {
    fmt.Println("bravo/main.go: init: 2")
}

func DoSomething() {
}

func init() {
    fmt.Println("bravo/main-.go: init: 3")
}

./charlie/charlie.go

package charlie

import "fmt"

func init() {
    fmt.Println("charlie/main.go: init: 1")
}

func init() {
    fmt.Println("charlie/main.go: init: 2")
}

func DoSomething() {
}

func init() {
    fmt.Println("charlie/main.go: init: 3")
}
  •  Tags:  
  • go
  • Related