From 87ccb43fdc884dc88e4deab0f3b2723b3a22c60b Mon Sep 17 00:00:00 2001 From: Christopher Dignam Date: Tue, 4 Jan 2022 12:49:25 -0500 Subject: [PATCH 1/2] Search up parent directories until we find our files. Fixes #165 --- godotenv.go | 59 ++++++++++++++++++++++++++++++++++++++++++++---- godotenv_test.go | 33 +++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 5 deletions(-) diff --git a/godotenv.go b/godotenv.go index 466f2eb..87d2c67 100644 --- a/godotenv.go +++ b/godotenv.go @@ -18,8 +18,10 @@ import ( "errors" "fmt" "io" + "io/ioutil" "os" "os/exec" + "path/filepath" "regexp" "sort" "strconv" @@ -28,6 +30,14 @@ import ( const doubleQuoteSpecialChars = "\\\n\r\"!$`" +func stringSet(s []string) map[string]interface{} { + m := make(map[string]interface{}) + for _, str := range s { + m[str] = true + } + return m +} + // 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) @@ -42,7 +52,46 @@ const doubleQuoteSpecialChars = "\\\n\r\"!$`" func Load(filenames ...string) (err error) { filenames = filenamesOrDefault(filenames) - for _, filename := range filenames { + filenamesMap := stringSet(filenames) + + // We start in the current working directory and look up until we find all + // of our files or hit the root path. + currentDirectory, err := os.Getwd() + if err != nil { + return err + } + + filePaths := make([]string, 0) + for { + files, err := ioutil.ReadDir(currentDirectory) + if err != nil { + return err + } + + // Check if any of our desired files are in the current directory. + for _, directoryFile := range files { + for filename := range filenamesMap { + if filename == directoryFile.Name() { + filePaths = append(filePaths, currentDirectory+"/"+directoryFile.Name()) + delete(filenamesMap, directoryFile.Name()) + } + + } + } + // We've found all of our files. + if len(filenamesMap) == 0 { + break + } + parent := filepath.Dir(currentDirectory) + + // We've hit the file system root. + if parent == currentDirectory { + break + } + currentDirectory = parent + } + + for _, filename := range filePaths { err = loadFile(filename, false) if err != nil { return // return early on a spazout @@ -187,8 +236,8 @@ func filenamesOrDefault(filenames []string) []string { return filenames } -func loadFile(filename string, overload bool) error { - envMap, err := readFile(filename) +func loadFile(filePath string, overload bool) error { + envMap, err := readFile(filePath) if err != nil { return err } @@ -209,8 +258,8 @@ func loadFile(filename string, overload bool) error { return nil } -func readFile(filename string) (envMap map[string]string, err error) { - file, err := os.Open(filename) +func readFile(filePath string) (envMap map[string]string, err error) { + file, err := os.Open(filePath) if err != nil { return } diff --git a/godotenv_test.go b/godotenv_test.go index 7274c14..4a2514c 100644 --- a/godotenv_test.go +++ b/godotenv_test.go @@ -63,6 +63,39 @@ func TestLoadFileNotFound(t *testing.T) { } } +func TestLoadFileInParent(t *testing.T) { + directory, err := os.MkdirTemp("", "sample") + if err != nil { + t.Error("Couldn't create temp directory.") + } + f, err := os.Create(directory + "/someparentfile.env") + if err != nil { + t.Error("Failed to create file.") + } + defer f.Close() + + f.WriteString("GODOT_LOAD_FILE_IN_PARENT_TEST=1\n") + + nestedPath := directory + "/some/nested/path" + err = os.MkdirAll(nestedPath, 0755) + if err != nil { + t.Error("Failed to create directories.") + } + err = os.Chdir(nestedPath) + if err != nil { + t.Error("Failed to change directory.") + } + + err = Load("someparentfile.env") + if err != nil { + t.Error("Error loading file.") + } + + if os.Getenv("GODOT_LOAD_FILE_IN_PARENT_TEST") != "1" { + t.Error("Failed to set env.") + } +} + func TestOverloadFileNotFound(t *testing.T) { err := Overload("somefilethatwillneverexistever.env") if err == nil { From bf915a6a9da91587a2989b5525050b58dac4df3a Mon Sep 17 00:00:00 2001 From: Christopher Dignam Date: Tue, 4 Jan 2022 12:50:07 -0500 Subject: [PATCH 2/2] doc --- godotenv_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/godotenv_test.go b/godotenv_test.go index 4a2514c..f498be9 100644 --- a/godotenv_test.go +++ b/godotenv_test.go @@ -63,6 +63,7 @@ func TestLoadFileNotFound(t *testing.T) { } } +// We should search up parent directories until we find our .env files. func TestLoadFileInParent(t *testing.T) { directory, err := os.MkdirTemp("", "sample") if err != nil {