From ea98b5c74f8bbc4e61c9df06cc8220aed876ff4b Mon Sep 17 00:00:00 2001 From: mappu Date: Mon, 2 Feb 2026 18:49:26 +1300 Subject: [PATCH] miqt-docker: perform cygpath conversion (Fixes: #308) --- cmd/miqt-docker/filepath.go | 28 +++++++++++++++ cmd/miqt-docker/filepath_test.go | 58 ++++++++++++++++++++++++++++++++ cmd/miqt-docker/main.go | 4 ++- 3 files changed, 89 insertions(+), 1 deletion(-) diff --git a/cmd/miqt-docker/filepath.go b/cmd/miqt-docker/filepath.go index 662aff575..47e7453dc 100644 --- a/cmd/miqt-docker/filepath.go +++ b/cmd/miqt-docker/filepath.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "path/filepath" + "regexp" "runtime" "strings" ) @@ -62,3 +63,30 @@ func highestCommonParent(paths []string) (string, error) { return strings.Join(parts, string(filepath.Separator)), nil } + +// maybeTransformCygpath converts a Cygwin or MSYS2 path to a Windows path +// that the path/filepath package will understand. +// This is to handle the case where `git` in $PATH is a Cygwin binary instead +// of a native one. +func maybeTransformCygpath(s string) string { + if runtime.GOOS == "windows" { + + // Cygwin + // Paths of the form /cygdrive/c/Users/username/Desktop/ + if strings.HasPrefix(s, `/cygdrive/`) { + parts := strings.Split(s[1:], `/`) // skip first slash + return strings.ToUpper(parts[1]) + `:\` + strings.Join(parts[2:], `\`) + } + + // MSYS2 + // Paths of the form /c/Users/username/Desktop/ + if regexp.MustCompile(`^/[a-z]/`).MatchString(s) { + parts := strings.Split(s[1:], `/`) // skip first slash + return strings.ToUpper(parts[0]) + `:\` + strings.Join(parts[1:], `\`) + } + + } + + // Not an affected path, or, non-Windows OS + return s +} diff --git a/cmd/miqt-docker/filepath_test.go b/cmd/miqt-docker/filepath_test.go index 2888e7689..ce9c2ce34 100644 --- a/cmd/miqt-docker/filepath_test.go +++ b/cmd/miqt-docker/filepath_test.go @@ -76,3 +76,61 @@ func TestHighestCommonParent(t *testing.T) { } } } + +func TestTransformCygpath(t *testing.T) { + if runtime.GOOS != "windows" { + t.Skip("Cygpath transforms are GOOS=windows only") + } + + type testCase struct { + input string + expect string + } + + cases := []testCase{ + + // No transform, no trailing slash + testCase{ + input: `C:\normal\windows\path`, + expect: `C:\normal\windows\path`, + }, + + // No transform, has trailing slash + testCase{ + input: `C:\normal\windows\path\`, + expect: `C:\normal\windows\path\`, + }, + + // Cygwin, no trailing slash + testCase{ + input: `/cygdrive/c/normal/windows/path`, + expect: `C:\normal\windows\path`, + }, + + // Cygwin, has trailing slash + testCase{ + input: `/cygdrive/c/normal/windows/path/`, + expect: `C:\normal\windows\path\`, + }, + + // MSYS2, no trailing slash + testCase{ + input: `/f/normal/windows/path`, + expect: `F:\normal\windows\path`, + }, + + // MSYS2, has trailing slash + testCase{ + input: `/f/normal/windows/path/`, + expect: `F:\normal\windows\path\`, + }, + } + + for idx, tc := range cases { + got := maybeTransformCygpath(tc.input) + if got != tc.expect { + t.Errorf("test %d: input(%v) got %q, want %q", idx, tc.input, got, tc.expect) + } + } + +} diff --git a/cmd/miqt-docker/main.go b/cmd/miqt-docker/main.go index b57c4fa3a..755973c0c 100644 --- a/cmd/miqt-docker/main.go +++ b/cmd/miqt-docker/main.go @@ -216,7 +216,9 @@ func getDockerRunArgsForGlob(dockerfiles []fs.DirEntry, containerNameGlob string // Don't panic } else { if gitroot_sz := strings.TrimSpace(string(gitroot)); len(gitroot_sz) > 0 { - parentPaths = append(parentPaths, gitroot_sz) + // On Windows, sometimes Cygwin/MSYS git is in $PATH, which returns + // a special kind of filepath that requires conversion to use + parentPaths = append(parentPaths, maybeTransformCygpath(gitroot_sz)) } }