diff --git a/.goreleaser.yml b/.goreleaser.yml index 295117b..3ecd64e 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -11,7 +11,7 @@ builds: env: - CGO_ENABLE=0 ldflags: - - -s -w -X github.com/jcouture/nv/internal/build.Version={{.Version}} -X github.com/jcouture/nv/internal/build.Date={{time "2006-01-02"}} + - -s -w -X github.com/jcouture/nv/internal/build.Version={{.Version}} id: macos goos: [darwin] goarch: [amd64, arm64] @@ -24,7 +24,7 @@ builds: - <<: *build_defaults id: windows goos: [windows] - goarch: [386, amd64, arm] + goarch: [386, amd64, arm, arm64] checksum: name_template: 'checksums.txt' @@ -45,4 +45,4 @@ brews: homepage: https://github.com/jcouture/nv description: Lightweight utility to load context specific environment variables test: | - system "#{bin}/nv" \ No newline at end of file + system "#{bin}/nv" diff --git a/LICENSE b/LICENSE index 3284b31..c74a6d2 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright 2015-2018 Jean-Philippe Couture +Copyright 2015-2022 Jean-Philippe Couture Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..de13974 --- /dev/null +++ b/Makefile @@ -0,0 +1,3 @@ +.PHONY: test run +test: + @go test -v -cover ./... diff --git a/cmd/nv/main.go b/cmd/nv/main.go index ca7c37b..8aa17db 100644 --- a/cmd/nv/main.go +++ b/cmd/nv/main.go @@ -1,4 +1,4 @@ -// Copyright 2015-2021 Jean-Philippe Couture +// Copyright 2015-2022 Jean-Philippe Couture // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -29,14 +29,25 @@ import ( "syscall" "github.com/jcouture/nv/internal/build" - "github.com/jcouture/nv/internal/parser" + "github.com/jcouture/nv/internal/env" "github.com/mitchellh/go-homedir" ) func main() { + if len(os.Args) == 2 { + cmd := os.Args[1] + switch cmd { + case "-v", "version", "-version", "--version": + printVersion() + default: + printHelp() + } + os.Exit(0) + } + if len(os.Args) < 3 { - printUsage() - os.Exit(-1) + printHelp() + os.Exit(0) } fn := os.Args[1] @@ -45,24 +56,23 @@ func main() { filenames := strings.Split(fn, ",") - vars := make(map[string]string) + base := make(map[string]string) for _, filename := range filenames { - // Parse file - parser := parser.NewParser(filename) - parsedVars, err := parser.Parse() + override, err := env.Load(filename) if err != nil { fmt.Printf("[Err] %s\n", err) os.Exit(-1) } // Merge with possibly existing variables - mergeVars(vars, parsedVars) + base = env.Join(base, override) } - loadAndMergeGlobalVars(vars) + globals := loadGlobals() + base = env.Join(base, globals) - clearEnv() - setEnvVars(vars) + env.Clear() + env.Set(base) binary, lookErr := exec.LookPath(cmd) if lookErr != nil { @@ -76,40 +86,20 @@ func main() { } } -func printUsage() { - usage := `nv %s(%s) — context specific environment variables -Usage: nv [arguments...] -` - fmt.Printf(usage, build.Version, build.Date) -} - -func setEnvVars(vars map[string]string) { - for k, v := range vars { - os.Setenv(k, v) - } +func printHelp() { + fmt.Printf("usage: nv [--version] [--help]\n") + fmt.Printf(" [arguments...]\n") } -func mergeVars(vars1 map[string]string, vars2 map[string]string) { - for k, v := range vars2 { - vars1[k] = v - } +func printVersion() { + fmt.Printf("nv version %s\n", build.Version) } -func loadAndMergeGlobalVars(vars map[string]string) { - dir, _ := homedir.Dir() - fn := filepath.Join(dir, ".nv") - parser := parser.NewParser(fn) - parsedVars, err := parser.Parse() - if err != nil { - // Return without breaking a sweat - return - } - mergeVars(vars, parsedVars) -} +func loadGlobals() map[string]string { + hdir, _ := homedir.Dir() + fn := filepath.Join(hdir, ".nv") + // Purposefuly ignoring any errors + globals, _ := env.Load(fn) -func clearEnv() { - // Clearing everything out the environment... but $PATH (we’re savages)! - path := os.Getenv("PATH") - os.Clearenv() - os.Setenv("PATH", path) + return globals } diff --git a/internal/build/build.go b/internal/build/build.go index 025c950..a9ec46c 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -1,13 +1,23 @@ -package build +// Copyright 2015-2022 Jean-Philippe Couture +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. -import "time" +package build var Version = "DEV" - -var Date = "" // YYYY-MM-DD - -func init() { - if Version == "DEV" { - Date = time.Now().Format("2006-01-02") - } -} diff --git a/internal/env/env.go b/internal/env/env.go new file mode 100644 index 0000000..cb36b56 --- /dev/null +++ b/internal/env/env.go @@ -0,0 +1,56 @@ +// Copyright 2015-2022 Jean-Philippe Couture +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +package env + +import ( + "os" + + "github.com/jcouture/nv/internal/parser" +) + +func Set(vars map[string]string) { + for k, v := range vars { + os.Setenv(k, v) + } +} + +func Join(base map[string]string, override map[string]string) map[string]string { + if len(base) == 0 { + return override + } + for k, v := range override { + base[k] = v + } + + return base +} + +func Load(fn string) (map[string]string, error) { + parser := parser.NewParser(fn) + return parser.Parse() +} + +func Clear() { + // Clearing everything out the environment... except $PATH (we’re savages)! + path := os.Getenv("PATH") + os.Clearenv() + os.Setenv("PATH", path) +} diff --git a/internal/env/env_test.go b/internal/env/env_test.go new file mode 100644 index 0000000..a056042 --- /dev/null +++ b/internal/env/env_test.go @@ -0,0 +1,58 @@ +// Copyright 2015-2022 Jean-Philippe Couture +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +package env + +import ( + "os" + "testing" +) + +func TestSet(t *testing.T) { + name := "FOO" + expected := "BAR" + + vars := make(map[string]string) + vars[name] = "BAR" + Set(vars) + + result := os.Getenv(name) + if result != expected { + t.Errorf("Expected: %s, got: %s\n", expected, result) + } +} + +func TestJoin(t *testing.T) { + base := make(map[string]string) + base["FOO"] = "BAR" + + override := make(map[string]string) + override["COLOR"] = "RED" + + result := Join(base, override) + + if len(result) != 2 { + t.Errorf("Expected length: 2, got: %d\n", len(result)) + } + + if result["FOO"] != "BAR" { + t.Errorf("Expected FOO == BAR, got FOO == %s\n", result["FOO"]) + } +} diff --git a/internal/parser/parser.go b/internal/parser/parser.go index 7593893..105c957 100644 --- a/internal/parser/parser.go +++ b/internal/parser/parser.go @@ -1,4 +1,4 @@ -// Copyright 2015-2021 Jean-Philippe Couture +// Copyright 2015-2022 Jean-Philippe Couture // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/internal/parser/parser_test.go b/internal/parser/parser_test.go index 175e7db..d50da1f 100644 --- a/internal/parser/parser_test.go +++ b/internal/parser/parser_test.go @@ -1,4 +1,4 @@ -// Copyright 2015-2021 Jean-Philippe Couture +// Copyright 2015-2022 Jean-Philippe Couture // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal