From 28b52615cf7a190dcf98f5618320cfd091835a7c Mon Sep 17 00:00:00 2001 From: "John Barton (joho)" Date: Sat, 20 Sep 2014 21:38:51 +1000 Subject: [PATCH 1/4] WIP on adding a bin command --- cmd/cmd.go | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 cmd/cmd.go diff --git a/cmd/cmd.go b/cmd/cmd.go new file mode 100644 index 0000000..cb16b5a --- /dev/null +++ b/cmd/cmd.go @@ -0,0 +1,56 @@ +package main + +import ( + "flag" + "fmt" + "os" + "os/exec" + "strings" + + "github.com/joho/godotenv" +) + +func main() { + showHelp := *flag.Bool("h", false, "show help") + rawEnvFilenames := *flag.String("f", "", "comma separated paths to .env files") + + flag.Parse() + + usage := ` +Run a process with a env setup from a .env file + +godotenv [-f ENV_FILE_PATHS] COMMAND_ARGS + +ENV_FILE_PATHS: comma separated paths to .env files +COMMAND_ARGS: command and args you want to run + +example + godotenv -f /path/to/something/.env,/another/path/.env fortune +` + // if no args or -h flag + // print usage and return + if showHelp || len(os.Args) < 2 { + fmt.Println(usage) + return + } + + // load env + // TODO something in flag passing or whatever isn't quite right + envFilenames := strings.Split(rawEnvFilenames, ",") + fmt.Printf("env filenames %v\n", envFilenames) + godotenv.Load(envFilenames...) + fmt.Printf("FOO=%v\n", os.Getenv("FOO")) + + // take rest of args and "exec" them + args := flag.Args() + cmd := args[0] + cmdArgs := args[1:len(args)] + fmt.Printf("cmd %v with args: %v\n", cmd, cmdArgs) + + command := exec.Command(cmd, cmdArgs...) + // command.Env = os.Environ() + command.Stdin = os.Stdin + command.Stdout = os.Stdout + command.Stderr = os.Stderr + command.Start() +} From 8c38c298ed3de45b7892633c01cf68439fd3acd3 Mon Sep 17 00:00:00 2001 From: "John Barton (joho)" Date: Sun, 21 Sep 2014 10:42:05 +1000 Subject: [PATCH 2/4] Get the minimum viable of the command working. Stripped debug code out Tidied up some flag handling Get it working with and without the -f flag. --- cmd/cmd.go | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/cmd/cmd.go b/cmd/cmd.go index cb16b5a..bf87066 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -11,8 +11,10 @@ import ( ) func main() { - showHelp := *flag.Bool("h", false, "show help") - rawEnvFilenames := *flag.String("f", "", "comma separated paths to .env files") + var showHelp bool + flag.BoolVar(&showHelp, "h", false, "show help") + var rawEnvFilenames string + flag.StringVar(&rawEnvFilenames, "f", "", "comma separated paths to .env files") flag.Parse() @@ -29,26 +31,28 @@ example ` // if no args or -h flag // print usage and return - if showHelp || len(os.Args) < 2 { + args := flag.Args() + if showHelp || len(args) == 0 { fmt.Println(usage) return } // load env - // TODO something in flag passing or whatever isn't quite right - envFilenames := strings.Split(rawEnvFilenames, ",") - fmt.Printf("env filenames %v\n", envFilenames) - godotenv.Load(envFilenames...) - fmt.Printf("FOO=%v\n", os.Getenv("FOO")) + // TODO would be nicer for an empty rawEnvFilenames to give us an empty map + // and then only call Load() once + // other TODO error handling on Load() + if rawEnvFilenames == "" { + godotenv.Load() + } else { + envFilenames := strings.Split(rawEnvFilenames, ",") + godotenv.Load(envFilenames...) + } // take rest of args and "exec" them - args := flag.Args() cmd := args[0] cmdArgs := args[1:len(args)] - fmt.Printf("cmd %v with args: %v\n", cmd, cmdArgs) command := exec.Command(cmd, cmdArgs...) - // command.Env = os.Environ() command.Stdin = os.Stdin command.Stdout = os.Stdout command.Stderr = os.Stderr From b2cd7d822bc9db274d539d32860fff56a43a6072 Mon Sep 17 00:00:00 2001 From: "John Barton (joho)" Date: Sun, 12 Oct 2014 09:50:38 +1100 Subject: [PATCH 3/4] Move the env loadings & command running into main package. --- cmd/cmd.go | 24 +++++++++--------------- godotenv.go | 11 +++++++++++ 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/cmd/cmd.go b/cmd/cmd.go index bf87066..555dc1e 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -3,8 +3,8 @@ package main import ( "flag" "fmt" - "os" - "os/exec" + "log" + "strings" "github.com/joho/godotenv" @@ -38,23 +38,17 @@ example } // load env - // TODO would be nicer for an empty rawEnvFilenames to give us an empty map - // and then only call Load() once - // other TODO error handling on Load() - if rawEnvFilenames == "" { - godotenv.Load() - } else { - envFilenames := strings.Split(rawEnvFilenames, ",") - godotenv.Load(envFilenames...) + var envFilenames []string + if rawEnvFilenames != "" { + envFilenames = strings.Split(rawEnvFilenames, ",") } // take rest of args and "exec" them cmd := args[0] cmdArgs := args[1:len(args)] - command := exec.Command(cmd, cmdArgs...) - command.Stdin = os.Stdin - command.Stdout = os.Stdout - command.Stderr = os.Stderr - command.Start() + err := godotenv.Exec(envFilenames, cmd, cmdArgs) + if err != nil { + log.Fatal(err) + } } diff --git a/godotenv.go b/godotenv.go index 6b643f3..83c7a80 100644 --- a/godotenv.go +++ b/godotenv.go @@ -19,6 +19,7 @@ import ( "bufio" "errors" "os" + "os/exec" "strings" ) @@ -65,6 +66,16 @@ func Read(filenames ...string) (envMap map[string]string, err error) { return } +func Exec(filenames []string, cmd string, cmdArgs []string) error { + Load(filenames...) + + command := exec.Command(cmd, cmdArgs...) + command.Stdin = os.Stdin + command.Stdout = os.Stdout + command.Stderr = os.Stderr + return command.Run() +} + func filenamesOrDefault(filenames []string) []string { if len(filenames) == 0 { return []string{".env"} From 1736a884e7dd3984ce70558d0df8b72c9c04aa83 Mon Sep 17 00:00:00 2001 From: "John Barton (joho)" Date: Sun, 12 Oct 2014 09:58:10 +1100 Subject: [PATCH 4/4] Improve godoc on Read and Exec --- godotenv.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/godotenv.go b/godotenv.go index 83c7a80..816e759 100644 --- a/godotenv.go +++ b/godotenv.go @@ -46,6 +46,10 @@ func Load(filenames ...string) (err error) { 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 Read(filenames ...string) (envMap map[string]string, err error) { filenames = filenamesOrDefault(filenames) envMap = make(map[string]string) @@ -66,6 +70,15 @@ func Read(filenames ...string) (envMap map[string]string, err error) { return } +/* + 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()` or `Read()` and the `os/exec` package yourself. +*/ func Exec(filenames []string, cmd string, cmdArgs []string) error { Load(filenames...)