diff --git a/build/git.go b/build/git.go index 112ec363b..8ed0d00e8 100644 --- a/build/git.go +++ b/build/git.go @@ -46,9 +46,9 @@ func getGitAttributes(ctx context.Context, contextPath string, dockerfilePath st if filepath.IsAbs(contextPath) { wd = contextPath } else { - cwd, _ := os.Getwd() - wd, _ = filepath.Abs(filepath.Join(cwd, contextPath)) + wd, _ = filepath.Abs(filepath.Join(getWd(), contextPath)) } + wd = gitutil.SanitizePath(wd) gitc, err := gitutil.New(gitutil.WithContext(ctx), gitutil.WithWorkingDir(wd)) if err != nil { @@ -104,8 +104,7 @@ func getGitAttributes(ctx context.Context, contextPath string, dockerfilePath st dockerfilePath = filepath.Join(wd, "Dockerfile") } if !filepath.IsAbs(dockerfilePath) { - cwd, _ := os.Getwd() - dockerfilePath = filepath.Join(cwd, dockerfilePath) + dockerfilePath = filepath.Join(getWd(), dockerfilePath) } if r, err := filepath.Rel(root, dockerfilePath); err == nil && !strings.HasPrefix(r, "..") { res["label:"+DockerfileLabel] = r @@ -125,9 +124,21 @@ func getGitAttributes(ctx context.Context, contextPath string, dockerfilePath st if err != nil { continue } + if lp, err := getLongPathName(dir); err == nil { + dir = lp + } + dir = gitutil.SanitizePath(dir) if r, err := filepath.Rel(root, dir); err == nil && !strings.HasPrefix(r, "..") { so.FrontendAttrs["vcs:localdir:"+k] = r } } }, nil } + +func getWd() string { + wd, _ := os.Getwd() + if lp, err := getLongPathName(wd); err == nil { + return lp + } + return wd +} diff --git a/build/git_unix.go b/build/git_unix.go new file mode 100644 index 000000000..5bd8e4d9f --- /dev/null +++ b/build/git_unix.go @@ -0,0 +1,9 @@ +//go:build !windows +// +build !windows + +package build + +// getLongPathName is a no-op on non-Windows platforms. +func getLongPathName(path string) (string, error) { + return path, nil +} diff --git a/build/git_windows.go b/build/git_windows.go new file mode 100644 index 000000000..b3ec71540 --- /dev/null +++ b/build/git_windows.go @@ -0,0 +1,26 @@ +package build + +import "golang.org/x/sys/windows" + +// getLongPathName converts Windows short pathnames to full pathnames. +// For example C:\Users\ADMIN~1 --> C:\Users\Administrator. +func getLongPathName(path string) (string, error) { + // See https://groups.google.com/forum/#!topic/golang-dev/1tufzkruoTg + p, err := windows.UTF16FromString(path) + if err != nil { + return "", err + } + b := p // GetLongPathName says we can reuse buffer + n, err := windows.GetLongPathName(&p[0], &b[0], uint32(len(b))) + if err != nil { + return "", err + } + if n > uint32(len(b)) { + b = make([]uint16, n) + _, err = windows.GetLongPathName(&p[0], &b[0], uint32(len(b))) + if err != nil { + return "", err + } + } + return windows.UTF16ToString(b), nil +} diff --git a/go.mod b/go.mod index 65f2f1ec7..0cb9c0985 100644 --- a/go.mod +++ b/go.mod @@ -46,6 +46,7 @@ require ( go.opentelemetry.io/otel/trace v1.19.0 golang.org/x/mod v0.11.0 golang.org/x/sync v0.3.0 + golang.org/x/sys v0.15.0 golang.org/x/term v0.15.0 google.golang.org/grpc v1.58.3 gopkg.in/yaml.v3 v3.0.1 @@ -152,7 +153,6 @@ require ( golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 // indirect golang.org/x/net v0.17.0 // indirect golang.org/x/oauth2 v0.10.0 // indirect - golang.org/x/sys v0.15.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.10.0 // indirect diff --git a/util/gitutil/gitutil.go b/util/gitutil/gitutil.go index 3347415e2..a4b1b12bb 100644 --- a/util/gitutil/gitutil.go +++ b/util/gitutil/gitutil.go @@ -70,7 +70,7 @@ func (c *Git) RootDir() (string, error) { if err != nil { return "", err } - return sanitizePath(root), nil + return SanitizePath(root), nil } func (c *Git) GitDir() (string, error) { diff --git a/util/gitutil/path_unix.go b/util/gitutil/path.go similarity index 97% rename from util/gitutil/path_unix.go rename to util/gitutil/path.go index 41b4bd61d..9c6c693ef 100644 --- a/util/gitutil/path_unix.go +++ b/util/gitutil/path.go @@ -45,7 +45,7 @@ func gitPath(wd string) (string, error) { var windowsPathRegex = regexp.MustCompile(`^[A-Za-z]:[\\/].*$`) -func sanitizePath(path string) string { +func SanitizePath(path string) string { // If we're running in WSL, we need to convert Windows paths to Unix paths. // This is because the git binary can be invoked through `git.exe` and // therefore returns Windows paths. diff --git a/util/gitutil/path_unix_test.go b/util/gitutil/path_unix_test.go index 591d5fcd9..ef95e6c55 100644 --- a/util/gitutil/path_unix_test.go +++ b/util/gitutil/path_unix_test.go @@ -10,10 +10,10 @@ import ( ) func TestSanitizePathUnix(t *testing.T) { - assert.Equal(t, "/home/foobar", sanitizePath("/home/foobar")) + assert.Equal(t, "/home/foobar", SanitizePath("/home/foobar")) } func TestSanitizePathWSL(t *testing.T) { t.Setenv("WSL_DISTRO_NAME", "Ubuntu") - assert.Equal(t, "/mnt/c/Users/foobar", sanitizePath("C:\\Users\\foobar")) + assert.Equal(t, "/mnt/c/Users/foobar", SanitizePath("C:\\Users\\foobar")) } diff --git a/util/gitutil/path_windows.go b/util/gitutil/path_windows.go index 050895f45..6ec5ea128 100644 --- a/util/gitutil/path_windows.go +++ b/util/gitutil/path_windows.go @@ -9,6 +9,6 @@ func gitPath(wd string) (string, error) { return exec.LookPath("git.exe") } -func sanitizePath(path string) string { +func SanitizePath(path string) string { return filepath.ToSlash(filepath.Clean(path)) } diff --git a/util/gitutil/path_windows_test.go b/util/gitutil/path_windows_test.go index 5326e305e..ee2e452de 100644 --- a/util/gitutil/path_windows_test.go +++ b/util/gitutil/path_windows_test.go @@ -7,5 +7,5 @@ import ( ) func TestSanitizePathWindows(t *testing.T) { - assert.Equal(t, "C:\\Users\\foobar", sanitizePath("C:/Users/foobar")) + assert.Equal(t, "C:\\Users\\foobar", SanitizePath("C:/Users/foobar")) }