From 6912771c399dd9f4781205f2a47c6484e8e1baea Mon Sep 17 00:00:00 2001 From: Chris Lane Date: Thu, 30 Jan 2020 18:19:43 -0500 Subject: [PATCH 1/6] chore: refactors `config.Paths` Refactors the reading of multiple envvars out of `config.Paths` in order to facilitate cleaner unit-testing. --- cmd/cheat/main.go | 10 +++++++++- internal/config/paths.go | 19 +++++++++---------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/cmd/cheat/main.go b/cmd/cheat/main.go index 76b9a39..864bbc3 100755 --- a/cmd/cheat/main.go +++ b/cmd/cheat/main.go @@ -6,6 +6,7 @@ import ( "fmt" "os" "runtime" + "strings" "github.com/docopt/docopt-go" @@ -31,8 +32,15 @@ func main() { os.Exit(0) } + // read the envvars into a map of strings + envvars := map[string]string{} + for _, e := range os.Environ() { + pair := strings.SplitN(e, "=", 2) + envvars[pair[0]] = pair[1] + } + // load the os-specifc paths at which the config file may be located - confpaths, err := config.Paths(runtime.GOOS) + confpaths, err := config.Paths(runtime.GOOS, envvars) if err != nil { fmt.Fprintf(os.Stderr, "failed to load config: %v\n", err) os.Exit(1) diff --git a/internal/config/paths.go b/internal/config/paths.go index ab08916..ab246f8 100644 --- a/internal/config/paths.go +++ b/internal/config/paths.go @@ -2,7 +2,6 @@ package config import ( "fmt" - "os" "path" "github.com/mitchellh/go-homedir" @@ -10,13 +9,13 @@ import ( // Paths returns config file paths that are appropriate for the operating // system -func Paths(sys string) ([]string, error) { +func Paths(sys string, envvars map[string]string) ([]string, error) { - // if CHEAT_CONFIG_PATH is set, return it - if os.Getenv("CHEAT_CONFIG_PATH") != "" { + // if CHEAT_CONFIG_PATH is set, expand ~ and return it + if confpath, ok := envvars["CHEAT_CONFIG_PATH"]; ok { // expand ~ - expanded, err := homedir.Expand(os.Getenv("CHEAT_CONFIG_PATH")) + expanded, err := homedir.Expand(confpath) if err != nil { return []string{}, fmt.Errorf("failed to expand ~: %v", err) } @@ -27,14 +26,14 @@ func Paths(sys string) ([]string, error) { switch sys { case "darwin", "linux", "freebsd": return []string{ - path.Join(os.Getenv("XDG_CONFIG_HOME"), "/cheat/conf.yml"), - path.Join(os.Getenv("HOME"), ".config/cheat/conf.yml"), - path.Join(os.Getenv("HOME"), ".cheat/conf.yml"), + path.Join(envvars["XDG_CONFIG_HOME"], "/cheat/conf.yml"), + path.Join(envvars["HOME"], ".config/cheat/conf.yml"), + path.Join(envvars["HOME"], ".cheat/conf.yml"), }, nil case "windows": return []string{ - fmt.Sprintf("%s/cheat/conf.yml", os.Getenv("APPDATA")), - fmt.Sprintf("%s/cheat/conf.yml", os.Getenv("PROGRAMDATA")), + fmt.Sprintf("%s/cheat/conf.yml", envvars["APPDATA"]), + fmt.Sprintf("%s/cheat/conf.yml", envvars["PROGRAMDATA"]), }, nil default: return []string{}, fmt.Errorf("unsupported os: %s", sys) From 8a313b92ca8f9f71371e88903df511c333a51e45 Mon Sep 17 00:00:00 2001 From: Chris Lane Date: Thu, 30 Jan 2020 19:25:53 -0500 Subject: [PATCH 2/6] chore: implements unit-tests for `config.Paths` --- internal/config/paths_test.go | 124 ++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 internal/config/paths_test.go diff --git a/internal/config/paths_test.go b/internal/config/paths_test.go new file mode 100644 index 0000000..fd955b0 --- /dev/null +++ b/internal/config/paths_test.go @@ -0,0 +1,124 @@ +package config + +import ( + "reflect" + "testing" + + "github.com/davecgh/go-spew/spew" +) + +// TestValidatePathsNix asserts that the proper config paths are returned on +// *nix platforms +func TestValidatePathsNix(t *testing.T) { + + // mock some envvars + envvars := map[string]string{ + "HOME": "/home/foo", + "XDG_CONFIG_HOME": "/home/bar", + } + + // specify the platforms to test + oses := []string{ + "darwin", + "freebsd", + "linux", + } + + // test each *nix os + for _, os := range oses { + // get the paths for the platform + paths, err := Paths(os, envvars) + if err != nil { + t.Errorf("paths returned an error: %v", err) + } + + // specify the expected output + want := []string{ + "/home/bar/cheat/conf.yml", + "/home/foo/.config/cheat/conf.yml", + "/home/foo/.cheat/conf.yml", + } + + // assert that output matches expectations + if !reflect.DeepEqual(paths, want) { + t.Errorf( + "failed to return expected paths: want:\n%s, got:\n%s", + spew.Sdump(want), + spew.Sdump(paths), + ) + } + } +} + +// TestValidatePathsWindows asserts that the proper config paths are returned +// on Windows platforms +func TestValidatePathsWindows(t *testing.T) { + + // mock some envvars + envvars := map[string]string{ + "APPDATA": "/apps", + "PROGRAMDATA": "/programs", + } + + // get the paths for the platform + paths, err := Paths("windows", envvars) + if err != nil { + t.Errorf("paths returned an error: %v", err) + } + + // specify the expected output + want := []string{ + "/apps/cheat/conf.yml", + "/programs/cheat/conf.yml", + } + + // assert that output matches expectations + if !reflect.DeepEqual(paths, want) { + t.Errorf( + "failed to return expected paths: want:\n%s, got:\n%s", + spew.Sdump(want), + spew.Sdump(paths), + ) + } +} + +// TestValidatePathsUnsupported asserts that an error is returned on +// unsupported platforms +func TestValidatePathsUnsupported(t *testing.T) { + _, err := Paths("unsupported", map[string]string{}) + if err == nil { + t.Errorf("failed to return error on unsupported platform") + } +} + +// TestValidatePathsCheatConfigPath asserts that the proper config path is +// returned when `CHEAT_CONFIG_PATH` is explicitly specified. +func TestValidatePathsCheatConfigPath(t *testing.T) { + + // mock some envvars + envvars := map[string]string{ + "HOME": "/home/foo", + "XDG_CONFIG_HOME": "/home/bar", + "CHEAT_CONFIG_PATH": "/home/baz/conf.yml", + } + + // get the paths for the platform + paths, err := Paths("linux", envvars) + if err != nil { + t.Errorf("paths returned an error: %v", err) + } + + // specify the expected output + want := []string{ + "/home/baz/conf.yml", + } + + // assert that output matches expectations + if !reflect.DeepEqual(paths, want) { + t.Errorf( + "failed to return expected paths: want:\n%s, got:\n%s", + spew.Sdump(want), + spew.Sdump(paths), + ) + } +} From 408e944eea49449005395382d8a14856e2ff4342 Mon Sep 17 00:00:00 2001 From: Chris Lane Date: Thu, 30 Jan 2020 19:45:02 -0500 Subject: [PATCH 3/6] chore: refactors `config.path` (small) Performs a minor refactoring on `config.Paths` to consistently use `path.Join` when computing config directory paths. Previously, both `path.Join` and `fmt.Sprintf` were being used, strictly due to an oversight. --- internal/config/paths.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/config/paths.go b/internal/config/paths.go index ab246f8..9a6fc2e 100644 --- a/internal/config/paths.go +++ b/internal/config/paths.go @@ -32,8 +32,8 @@ func Paths(sys string, envvars map[string]string) ([]string, error) { }, nil case "windows": return []string{ - fmt.Sprintf("%s/cheat/conf.yml", envvars["APPDATA"]), - fmt.Sprintf("%s/cheat/conf.yml", envvars["PROGRAMDATA"]), + path.Join(envvars["APPDATA"], "/cheat/conf.yml"), + path.Join(envvars["PROGRAMDATA"], "/cheat/conf.yml"), }, nil default: return []string{}, fmt.Errorf("unsupported os: %s", sys) From 506fb8be15d3dc35459e8acf02956d8f57cc80fe Mon Sep 17 00:00:00 2001 From: Chris Lane Date: Thu, 30 Jan 2020 19:59:35 -0500 Subject: [PATCH 4/6] fix: XDG_CONFIG_HOME mishandling Attempts to resolve an issue regarding automatic config file generation, as referenced in #501. This issue is believed occur when `XDG_CONFIG_HOME` is unset. --- internal/config/paths.go | 17 +++++++++++---- internal/config/paths_test.go | 41 +++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/internal/config/paths.go b/internal/config/paths.go index 9a6fc2e..52a2d86 100644 --- a/internal/config/paths.go +++ b/internal/config/paths.go @@ -11,7 +11,7 @@ import ( // system func Paths(sys string, envvars map[string]string) ([]string, error) { - // if CHEAT_CONFIG_PATH is set, expand ~ and return it + // if `CHEAT_CONFIG_PATH` is set, expand ~ and return it if confpath, ok := envvars["CHEAT_CONFIG_PATH"]; ok { // expand ~ @@ -25,11 +25,20 @@ func Paths(sys string, envvars map[string]string) ([]string, error) { switch sys { case "darwin", "linux", "freebsd": - return []string{ - path.Join(envvars["XDG_CONFIG_HOME"], "/cheat/conf.yml"), + paths := []string{} + + // don't include the `XDG_CONFIG_HOME` path if that envvar is not set + if xdgpath, ok := envvars["XDG_CONFIG_HOME"]; ok { + paths = append(paths, path.Join(xdgpath, "/cheat/conf.yml")) + } + + // `HOME` will always be set on a POSIX-compliant system, though + paths = append(paths, []string{ path.Join(envvars["HOME"], ".config/cheat/conf.yml"), path.Join(envvars["HOME"], ".cheat/conf.yml"), - }, nil + }...) + + return paths, nil case "windows": return []string{ path.Join(envvars["APPDATA"], "/cheat/conf.yml"), diff --git a/internal/config/paths_test.go b/internal/config/paths_test.go index fd955b0..0838c3e 100644 --- a/internal/config/paths_test.go +++ b/internal/config/paths_test.go @@ -50,6 +50,47 @@ func TestValidatePathsNix(t *testing.T) { } } +// TestValidatePathsNixNoXDG asserts that the proper config paths are returned +// on *nix platforms when `XDG_CONFIG_HOME is not set +func TestValidatePathsNixNoXDG(t *testing.T) { + + // mock some envvars + envvars := map[string]string{ + "HOME": "/home/foo", + } + + // specify the platforms to test + oses := []string{ + "darwin", + "freebsd", + "linux", + } + + // test each *nix os + for _, os := range oses { + // get the paths for the platform + paths, err := Paths(os, envvars) + if err != nil { + t.Errorf("paths returned an error: %v", err) + } + + // specify the expected output + want := []string{ + "/home/foo/.config/cheat/conf.yml", + "/home/foo/.cheat/conf.yml", + } + + // assert that output matches expectations + if !reflect.DeepEqual(paths, want) { + t.Errorf( + "failed to return expected paths: want:\n%s, got:\n%s", + spew.Sdump(want), + spew.Sdump(paths), + ) + } + } +} + // TestValidatePathsWindows asserts that the proper config paths are returned // on Windows platforms func TestValidatePathsWindows(t *testing.T) { From a3fe4f40bb660cea36d411244e3c4074b9644a0e Mon Sep 17 00:00:00 2001 From: Chris Lane Date: Thu, 30 Jan 2020 20:06:06 -0500 Subject: [PATCH 5/6] chore: bumps version to 3.4.1 --- cmd/cheat/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/cheat/main.go b/cmd/cheat/main.go index 864bbc3..2cec23e 100755 --- a/cmd/cheat/main.go +++ b/cmd/cheat/main.go @@ -14,7 +14,7 @@ import ( "github.com/cheat/cheat/internal/config" ) -const version = "3.4.0" +const version = "3.4.1" func main() { From 2c7ce48859731021d155924331826aaf0b0245a1 Mon Sep 17 00:00:00 2001 From: Chris Lane Date: Thu, 30 Jan 2020 20:08:11 -0500 Subject: [PATCH 6/6] chore: multi-os builds on travis Modifies `.travis.yml` to specify that builds be performed on both Linux and MacOSX. Experimental. --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index 9aeb500..20c9b51 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,10 @@ language: go go: - 1.13.x +os: + - linux + - osx + env: - GO111MODULE=on