Jelajahi Sumber

duplicate windows os/exec.LookPath() and do not resolve files in current working dir if CWD is not explicitly in PATH.

Signed-off-by: guillaume.tardif <[email protected]>
guillaume.tardif 5 tahun lalu
induk
melakukan
4489f39e5d
3 mengubah file dengan 150 tambahan dan 1 penghapusan
  1. 11 1
      cli/mobycli/exec.go
  2. 28 0
      cli/mobycli/lp_unix.go
  3. 111 0
      cli/mobycli/lp_windows.go

+ 11 - 1
cli/mobycli/exec.go

@@ -22,6 +22,7 @@ import (
 	"os"
 	"os/exec"
 	"os/signal"
+	"runtime"
 	"strings"
 
 	"github.com/spf13/cobra"
@@ -60,7 +61,16 @@ func mustDelegateToMoby(ctxType string) bool {
 
 // Exec delegates to com.docker.cli if on moby context
 func Exec(root *cobra.Command) {
-	cmd := exec.Command(ComDockerCli, os.Args[1:]...)
+	execBinary := ComDockerCli
+	if runtime.GOOS == "windows" { // workaround for windows issue https://github.com/golang/go/issues/38736
+		var err error
+		execBinary, err = LookPath(ComDockerCli)
+		if err != nil {
+			fmt.Fprintln(os.Stderr, err)
+			os.Exit(1)
+		}
+	}
+	cmd := exec.Command(execBinary, os.Args[1:]...)
 	cmd.Stdin = os.Stdin
 	cmd.Stdout = os.Stdout
 	cmd.Stderr = os.Stderr

+ 28 - 0
cli/mobycli/lp_unix.go

@@ -0,0 +1,28 @@
+// +build !windows
+
+/*
+   Copyright 2020 Docker Compose CLI authors
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package mobycli
+
+import (
+	"os/exec"
+)
+
+// LookPath simply delegate to os/exec.LookPath if not on windows
+func LookPath(file string) (string, error) {
+	return exec.LookPath(file)
+}

+ 111 - 0
cli/mobycli/lp_windows.go

@@ -0,0 +1,111 @@
+/*
+   Copyright 2020 Docker Compose CLI authors
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package mobycli
+
+import (
+	"errors"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"strings"
+)
+
+// ErrNotFound is the error resulting if a path search failed to find an executable file.
+var ErrNotFound = errors.New("executable file not found in %PATH%")
+
+func chkStat(file string) error {
+	d, err := os.Stat(file)
+	if err != nil {
+		return err
+	}
+	if d.IsDir() {
+		return os.ErrPermission
+	}
+	return nil
+}
+
+func hasExt(file string) bool {
+	i := strings.LastIndex(file, ".")
+	if i < 0 {
+		return false
+	}
+	return strings.LastIndexAny(file, `:\/`) < i
+}
+
+func findExecutable(file string, exts []string) (string, error) {
+	if len(exts) == 0 {
+		return file, chkStat(file)
+	}
+	if hasExt(file) {
+		if chkStat(file) == nil {
+			return file, nil
+		}
+	}
+	for _, e := range exts {
+		if f := file + e; chkStat(f) == nil {
+			return f, nil
+		}
+	}
+	return "", os.ErrNotExist
+}
+
+// LookPath searches for an executable named file in the
+// directories named by the PATH environment variable.
+// If file contains a slash, it is tried directly and the PATH is not consulted.
+// LookPath also uses PATHEXT environment variable to match
+// a suitable candidate.
+// The result may be an absolute path or a path relative to the current directory.
+func LookPath(file string) (string, error) {
+	var exts []string
+	x := os.Getenv(`PATHEXT`)
+	if x != "" {
+		for _, e := range strings.Split(strings.ToLower(x), `;`) {
+			if e == "" {
+				continue
+			}
+			if e[0] != '.' {
+				e = "." + e
+			}
+			exts = append(exts, e)
+		}
+	} else {
+		exts = []string{".com", ".exe", ".bat", ".cmd"}
+	}
+
+	if strings.ContainsAny(file, `:\/`) {
+		if f, err := findExecutable(file, exts); err == nil {
+			return f, nil
+		} else {
+			return "", &exec.Error{file, err}
+		}
+	}
+	// DO NOT lookup current folder
+	//if f, err := findExecutable(filepath.Join(".", file), exts); err == nil {
+	//	return f, nil
+	//}
+	path := os.Getenv("path")
+	for _, dir := range filepath.SplitList(path) {
+		// empty dir means dupicate semicolon in PATH, should not resolve files in current working dir...
+		if strings.TrimSpace(dir) == "" {
+			continue
+		}
+		if f, err := findExecutable(filepath.Join(dir, file), exts); err == nil {
+			return f, nil
+		}
+	}
+	return "", &exec.Error{file, ErrNotFound}
+}