// Package godotenv is a go port of the ruby dotenv library (https://github.com/bkeepers/dotenv) // // Examples/readme can be found on the GitHub page at https://github.com/joho/godotenv // // The TL;DR is that you make a .env file that looks something like // // SOME_ENV_VAR=somevalue // // and then in your go code you can call // // godotenv.Load() // // and all the env vars declared in .env will be available through os.Getenv("SOME_ENV_VAR")
package godotenv import ( ) const doubleQuoteSpecialChars = "\\\n\r\"!$`" // Parse reads an env file from io.Reader, returning a map of keys and values. func ( io.Reader) (map[string]string, error) { var bytes.Buffer , := io.Copy(&, ) if != nil { return nil, } return UnmarshalBytes(.Bytes()) } // Load will read your env file(s) and load them into ENV for this process. // // Call this function as close as possible to the start of your program (ideally in main). // // If you call Load without any args it will default to loading .env in the current path. // // You can otherwise tell it which files to load (there can be more than one) like: // // godotenv.Load("fileone", "filetwo") // // It's important to note that it WILL NOT OVERRIDE an env variable that already exists - consider the .env file to set dev vars or sensible defaults. func ( ...string) ( error) { = filenamesOrDefault() for , := range { = loadFile(, false) if != nil { return // return early on a spazout } } return } // Overload will read your env file(s) and load them into ENV for this process. // // Call this function as close as possible to the start of your program (ideally in main). // // If you call Overload without any args it will default to loading .env in the current path. // // You can otherwise tell it which files to load (there can be more than one) like: // // godotenv.Overload("fileone", "filetwo") // // It's important to note this WILL OVERRIDE an env variable that already exists - consider the .env file to forcefully set all vars. func ( ...string) ( error) { = filenamesOrDefault() for , := range { = loadFile(, true) if != nil { return // return early on a spazout } } return } // Read all env (with same file loading semantics as Load) but return values as // a map rather than automatically writing values into env func ( ...string) ( map[string]string, error) { = filenamesOrDefault() = make(map[string]string) for , := range { , := readFile() if != nil { = return // return early on a spazout } for , := range { [] = } } return } // Unmarshal reads an env file from a string, returning a map of keys and values. func ( string) ( map[string]string, error) { return UnmarshalBytes([]byte()) } // UnmarshalBytes parses env file from byte slice of chars, returning a map of keys and values. func ( []byte) (map[string]string, error) { := make(map[string]string) := parseBytes(, ) return , } // Exec loads env vars from the specified filenames (empty map falls back to default) // then executes the cmd specified. // // Simply hooks up os.Stdin/err/out to the command and calls Run(). // // If you want more fine grained control over your command it's recommended // that you use `Load()`, `Overload()` or `Read()` and the `os/exec` package yourself. func ( []string, string, []string, bool) error { := Load if { = Overload } if := (...); != nil { return } := exec.Command(, ...) .Stdin = os.Stdin .Stdout = os.Stdout .Stderr = os.Stderr return .Run() } // Write serializes the given environment and writes it to a file. func ( map[string]string, string) error { , := Marshal() if != nil { return } , := os.Create() if != nil { return } defer .Close() _, = .WriteString( + "\n") if != nil { return } return .Sync() } // Marshal outputs the given environment as a dotenv-formatted environment file. // Each line is in the format: KEY="VALUE" where VALUE is backslash-escaped. func ( map[string]string) (string, error) { := make([]string, 0, len()) for , := range { if , := strconv.Atoi(); == nil { = append(, fmt.Sprintf(`%s=%d`, , )) } else { = append(, fmt.Sprintf(`%s="%s"`, , doubleQuoteEscape())) } } sort.Strings() return strings.Join(, "\n"), nil } func filenamesOrDefault( []string) []string { if len() == 0 { return []string{".env"} } return } func loadFile( string, bool) error { , := readFile() if != nil { return } := map[string]bool{} := os.Environ() for , := range { := strings.Split(, "=")[0] [] = true } for , := range { if ![] || { _ = os.Setenv(, ) } } return nil } func readFile( string) ( map[string]string, error) { , := os.Open() if != nil { return } defer .Close() return Parse() } func doubleQuoteEscape( string) string { for , := range doubleQuoteSpecialChars { := "\\" + string() if == '\n' { = `\n` } if == '\r' { = `\r` } = strings.Replace(, string(), , -1) } return }