Skip to content

Commit

Permalink
Search up parent directories until we find our files.
Browse files Browse the repository at this point in the history
Fixes joho#165
  • Loading branch information
cdignam-segment committed Jan 4, 2022
1 parent c40e9c6 commit 87ccb43
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 5 deletions.
59 changes: 54 additions & 5 deletions godotenv.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ import (
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"regexp"
"sort"
"strconv"
Expand All @@ -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)
Expand All @@ -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
Expand Down Expand Up @@ -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
}
Expand All @@ -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
}
Expand Down
33 changes: 33 additions & 0 deletions godotenv_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

0 comments on commit 87ccb43

Please sign in to comment.