diff --git a/next/cmd/config.go b/next/cmd/config.go index 7a67f6eff51d..f803599cf13f 100644 --- a/next/cmd/config.go +++ b/next/cmd/config.go @@ -591,8 +591,8 @@ func (c *Config) init(rootCmd *cobra.Command) error { } func (c *Config) persistentPreRunRootE(cmd *cobra.Command, args []string) error { - c.SourceDir = makeCleanAbsSlashPath(c.workingDir, c.SourceDir) - c.DestDir = makeCleanAbsSlashPath(c.workingDir, c.DestDir) + c.SourceDir = chezmoi.PathJoin(c.workingDir, c.SourceDir) + c.DestDir = chezmoi.PathJoin(c.workingDir, c.DestDir) if !getBoolAnnotation(cmd, doesNotRequireValidConfig) { if c.err != nil { diff --git a/next/cmd/util_posix.go b/next/cmd/util_posix.go index ab191e74ea0f..d04f7e25a827 100644 --- a/next/cmd/util_posix.go +++ b/next/cmd/util_posix.go @@ -4,7 +4,6 @@ package cmd import ( "io" - "path" "syscall" ) @@ -21,15 +20,6 @@ func getUmask() int { return umask } -// makeCleanAbsSlashPath returns a clean, absolute path separated with forward -// slashes. If file is not an absolute path then it is joined on to dir. -func makeCleanAbsSlashPath(dir, file string) string { - if !path.IsAbs(file) { - file = path.Join(dir, file) - } - return path.Clean(file) -} - func trimExecutableSuffix(s string) string { return s } diff --git a/next/cmd/util_test.go b/next/cmd/util_test.go index 25a50e953d22..ed65bfd4f01f 100644 --- a/next/cmd/util_test.go +++ b/next/cmd/util_test.go @@ -1,57 +1,11 @@ package cmd import ( - "runtime" - "strings" "testing" "github.com/stretchr/testify/assert" ) -func TestMakeCleanAbsSlashPath(t *testing.T) { - type testCase struct { - dir string - file string - expected string - } - testCases := []testCase{ - { - dir: "/home/user", - file: "file", - expected: "/home/user/file", - }, - { - dir: "/home/user", - file: "/tmp/file", - expected: "/tmp/file", - }, - } - if runtime.GOOS == "windows" { - testCases = append(testCases, - testCase{ - dir: `C:\Users\user`, - file: "file", - expected: `C:/Users/user/file`, - }, - testCase{ - dir: `C:\Users\user`, - file: `dir/file`, - expected: `C:/Users/user/dir/file`, - }, - testCase{ - dir: `C:\Users\user`, - file: `D:\Users\user\file`, - expected: `D:/Users/user/file`, - }, - ) - } - for _, tc := range testCases { - t.Run(strings.Join([]string{tc.dir, tc.file}, "_"), func(t *testing.T) { - assert.Equal(t, tc.expected, makeCleanAbsSlashPath(tc.dir, tc.file)) - }) - } -} - func TestUpperSnakeCaseToCamelCaseMap(t *testing.T) { actual := upperSnakeCaseToCamelCaseMap(map[string]string{ "BUG_REPORT_URL": "", diff --git a/next/cmd/util_windows.go b/next/cmd/util_windows.go index fbf7159dac38..72e19d18520d 100644 --- a/next/cmd/util_windows.go +++ b/next/cmd/util_windows.go @@ -29,15 +29,6 @@ func getUmask() int { return 0 } -// makeCleanAbsSlashPath returns a clean, absolute path separated with forward -// slashes. If file is not an absolute path then it is joined on to dir. -func makeCleanAbsSlashPath(dir, file string) string { - if !filepath.IsAbs(file) { - file = filepath.Join(dir, file) - } - return filepath.ToSlash(filepath.Clean(file)) -} - func trimExecutableSuffix(s string) string { if strings.EqualFold(filepath.Ext(s), ".exe") { return s[:len(s)-4] diff --git a/next/internal/chezmoi/path_posix.go b/next/internal/chezmoi/path_posix.go new file mode 100644 index 000000000000..84bc4dab9533 --- /dev/null +++ b/next/internal/chezmoi/path_posix.go @@ -0,0 +1,21 @@ +// +build !windows + +package chezmoi + +import ( + "path" +) + +// PathJoin returns a clean, absolute path. If file is not an absolute path then +// it is joined on to dir. +func PathJoin(dir, file string) string { + if !path.IsAbs(file) { + file = path.Join(dir, file) + } + return path.Clean(file) +} + +// PathsToSlashes returns paths. +func PathsToSlashes(paths []string) []string { + return paths +} diff --git a/next/internal/chezmoi/path_test.go b/next/internal/chezmoi/path_test.go new file mode 100644 index 000000000000..16c10ae8027f --- /dev/null +++ b/next/internal/chezmoi/path_test.go @@ -0,0 +1,53 @@ +package chezmoi + +import ( + "runtime" + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestPathJoin(t *testing.T) { + type testCase struct { + dir string + file string + expected string + } + testCases := []testCase{ + { + dir: "/home/user", + file: "file", + expected: "/home/user/file", + }, + { + dir: "/home/user", + file: "/tmp/file", + expected: "/tmp/file", + }, + } + if runtime.GOOS == "windows" { + testCases = append(testCases, + testCase{ + dir: `C:\Users\user`, + file: "file", + expected: `C:/Users/user/file`, + }, + testCase{ + dir: `C:\Users\user`, + file: `dir/file`, + expected: `C:/Users/user/dir/file`, + }, + testCase{ + dir: `C:\Users\user`, + file: `D:\Users\user\file`, + expected: `D:/Users/user/file`, + }, + ) + } + for _, tc := range testCases { + t.Run(strings.Join([]string{tc.dir, tc.file}, "_"), func(t *testing.T) { + assert.Equal(t, tc.expected, PathJoin(tc.dir, tc.file)) + }) + } +} diff --git a/next/internal/chezmoi/path_windows.go b/next/internal/chezmoi/path_windows.go new file mode 100644 index 000000000000..4f245f6a905d --- /dev/null +++ b/next/internal/chezmoi/path_windows.go @@ -0,0 +1,23 @@ +package chezmoi + +import ( + "path/filepath" +) + +// PathJoin returns a clean, absolute path separated with forward +// slashes. If file is not an absolute path then it is joined on to dir. +func PathJoin(dir, file string) string { + if !filepath.IsAbs(file) { + file = filepath.Join(dir, file) + } + return filepath.ToSlash(filepath.Clean(file)) +} + +// PathsToSlashes returns a copy of paths with filepath.ToSlash to each element. +func PathsToSlashes(paths []string) []string { + result := make([]string, 0, len(paths)) + for _, path := range paths { + result = append(result, filepath.ToSlash(path)) + } + return result +} diff --git a/next/internal/chezmoi/realsystem_test.go b/next/internal/chezmoi/realsystem_test.go index df1160a774c3..33c50e997df0 100644 --- a/next/internal/chezmoi/realsystem_test.go +++ b/next/internal/chezmoi/realsystem_test.go @@ -58,7 +58,7 @@ func TestRealSystemGlob(t *testing.T) { actualMatches, err := s.Glob(tc.pattern) require.NoError(t, err) sort.Strings(actualMatches) - assert.Equal(t, tc.expectedMatches, actualMatches) + assert.Equal(t, tc.expectedMatches, PathsToSlashes(actualMatches)) }) } } diff --git a/next/main_test.go b/next/main_test.go index 512670ea2b42..b0defab8265f 100644 --- a/next/main_test.go +++ b/next/main_test.go @@ -16,6 +16,7 @@ import ( "github.com/twpayne/go-vfs/vfst" "github.com/twpayne/chezmoi/next/cmd" + "github.com/twpayne/chezmoi/next/internal/chezmoi" ) //nolint:interfacer @@ -76,8 +77,8 @@ func cmdChHome(ts *testscript.TestScript, neg bool, args []string) { } var ( homeDir = ts.MkAbs(args[0]) - chezmoiConfigDir = filepath.Join(homeDir, ".config", "chezmoi") - chezmoiSourceDir = filepath.Join(homeDir, ".local", "share", "chezmoi") + chezmoiConfigDir = chezmoi.PathJoin(homeDir, ".config/chezmoi") + chezmoiSourceDir = chezmoi.PathJoin(homeDir, ".local/share/chezmoi") ) ts.Check(os.MkdirAll(homeDir, 0o777)) ts.Setenv("HOME", homeDir) @@ -225,10 +226,10 @@ func cmdMkSourceDir(ts *testscript.TestScript, neg bool, args []string) { func setup(env *testscript.Env) error { var ( - binDir = filepath.Join(env.WorkDir, "bin") - homeDir = filepath.Join(env.WorkDir, "home", "user") - chezmoiConfigDir = filepath.Join(homeDir, ".config", "chezmoi") - chezmoiSourceDir = filepath.Join(homeDir, ".local", "share", "chezmoi") + binDir = chezmoi.PathJoin(env.WorkDir, "bin") + homeDir = chezmoi.PathJoin(env.WorkDir, "home/user") + chezmoiConfigDir = chezmoi.PathJoin(homeDir, ".config/chezmoi") + chezmoiSourceDir = chezmoi.PathJoin(homeDir, ".local/share/chezmoi") ) env.Setenv("HOME", homeDir)