Add git bash and mingw support (#132) · coder/sshcode@50e859c
@@ -10,6 +10,7 @@ import (
1010"os/exec"
1111"os/signal"
1212"path/filepath"
13+"runtime"
1314"strconv"
1415"strings"
1516"syscall"
@@ -106,7 +107,6 @@ func sshCode(host, dir string, o options) error {
106107107108// Downloads the latest code-server and allows it to be executed.
108109sshCmdStr := fmt.Sprintf("ssh %v %v '/usr/bin/env bash -l'", o.sshFlags, host)
109-110110sshCmd := exec.Command("sh", "-l", "-c", sshCmdStr)
111111sshCmd.Stdout = os.Stdout
112112sshCmd.Stderr = os.Stderr
@@ -145,10 +145,9 @@ func sshCode(host, dir string, o options) error {
145145flog.Info("Tunneling remote port %v to %v", o.remotePort, o.bindAddr)
146146147147sshCmdStr :=
148-fmt.Sprintf("ssh -tt -q -L %v:localhost:%v %v %v 'cd %v; %v --host 127.0.0.1 --auth none --port=%v'",
149-o.bindAddr, o.remotePort, o.sshFlags, host, dir, codeServerPath, o.remotePort,
148+fmt.Sprintf("ssh -tt -q -L %v:localhost:%v %v %v '%v %v --host 127.0.0.1 --auth none --port=%v'",
149+o.bindAddr, o.remotePort, o.sshFlags, host, codeServerPath, dir, o.remotePort,
150150 )
151-152151// Starts code-server and forwards the remote port.
153152sshCmd := exec.Command("sh", "-l", "-c", sshCmdStr)
154153sshCmd.Stdin = os.Stdin
@@ -266,9 +265,12 @@ func openBrowser(url string) {
266265const (
267266macPath = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
268267wslPath = "/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe"
268+winPath = "C:/Program Files (x86)/Google/Chrome/Application/chrome.exe"
269269 )
270270271271switch {
272+case commandExists("chrome"):
273+openCmd = exec.Command("chrome", chromeOptions(url)...)
272274case commandExists("google-chrome"):
273275openCmd = exec.Command("google-chrome", chromeOptions(url)...)
274276case commandExists("google-chrome-stable"):
@@ -281,6 +283,8 @@ func openBrowser(url string) {
281283openCmd = exec.Command(macPath, chromeOptions(url)...)
282284case pathExists(wslPath):
283285openCmd = exec.Command(wslPath, chromeOptions(url)...)
286+case pathExists(winPath):
287+openCmd = exec.Command(winPath, chromeOptions(url)...)
284288default:
285289err := browser.OpenURL(url)
286290if err != nil {
@@ -335,6 +339,11 @@ func randomPort() (string, error) {
335339// checkSSHDirectory performs sanity and safety checks on sshDirectory, and
336340// returns a new value for o.reuseConnection depending on the checks.
337341func checkSSHDirectory(sshDirectory string, reuseConnection bool) bool {
342+if runtime.GOOS == "windows" {
343+flog.Info("OS is windows, disabling connection reuse feature")
344+return false
345+ }
346+338347sshDirectoryMode, err := os.Lstat(expandPath(sshDirectory))
339348if err != nil {
340349if reuseConnection {
@@ -451,8 +460,10 @@ func syncUserSettings(sshFlags string, host string, back bool) error {
451460return err
452461 }
453462454-const remoteSettingsDir = "~/.local/share/code-server/User/"
455-463+var remoteSettingsDir = "~/.local/share/code-server/User/"
464+if runtime.GOOS == "windows" {
465+remoteSettingsDir = ".local/share/code-server/User/"
466+ }
456467var (
457468src = localConfDir + "/"
458469dest = host + ":" + remoteSettingsDir
@@ -477,7 +488,10 @@ func syncExtensions(sshFlags string, host string, back bool) error {
477488return err
478489 }
479490480-const remoteExtensionsDir = "~/.local/share/code-server/extensions/"
491+var remoteExtensionsDir = "~/.local/share/code-server/extensions/"
492+if runtime.GOOS == "windows" {
493+remoteExtensionsDir = ".local/share/code-server/extensions/"
494+ }
481495482496var (
483497src = localExtensionsDir + "/"
@@ -505,6 +519,7 @@ func rsync(src string, dest string, sshFlags string, excludePaths ...string) err
505519// locally in order to properly delete an extension.
506520"--delete",
507521"--copy-unsafe-links",
522+"-zz",
508523src, dest,
509524 )...,
510525 )
@@ -524,7 +539,7 @@ func downloadScript(codeServerPath string) string {
524539525540[ "$(uname -m)" != "x86_64" ] && echo "Unsupported server architecture $(uname -m). code-server only has releases for x86_64 systems." && exit 1
526541pkill -f %v || true
527-mkdir -p ~/.local/share/code-server %v
542+mkdir -p $HOME/.local/share/code-server %v
528543cd %v
529544curlflags="-o latest-linux"
530545if [ -f latest-linux ]; then
@@ -535,8 +550,8 @@ curl $curlflags https://codesrv-ci.cdr.sh/latest-linux
535550ln latest-linux %v
536551chmod +x %v`,
537552codeServerPath,
538-filepath.Dir(codeServerPath),
539-filepath.Dir(codeServerPath),
553+filepath.ToSlash(filepath.Dir(codeServerPath)),
554+filepath.ToSlash(filepath.Dir(codeServerPath)),
540555codeServerPath,
541556codeServerPath,
542557codeServerPath,
@@ -548,6 +563,11 @@ chmod +x %v`,
548563func ensureDir(path string) error {
549564_, err := os.Stat(path)
550565if os.IsNotExist(err) {
566+// This fixes a issue where Go reads `/c/` as `C:\c\` and creates
567+// empty directories on the client that don't need to exist.
568+if runtime.GOOS == "windows" && strings.HasPrefix(path, "/c/") {
569+path = "C:" + path[2:]
570+ }
551571err = os.MkdirAll(path, 0750)
552572 }
553573@@ -608,3 +628,26 @@ func parseGCPSSHCmd(instance string) (ip, sshFlags string, err error) {
608628609629return strings.TrimSpace(userIP), sshFlags, nil
610630}
631+632+// gitbashWindowsDir strips a the msys2 install directory from the beginning of
633+// the path. On msys2, if a user provides `/workspace` sshcode will receive
634+// `C:/msys64/workspace` which won't work on the remote host.
635+func gitbashWindowsDir(dir string) string {
636+637+// Don't bother figuring out path if it's relative to home dir.
638+if strings.HasPrefix(dir, "~/") {
639+if dir == "~" {
640+return "~/"
641+ }
642+return dir
643+ }
644+645+mingwPrefix, err := exec.Command("sh", "-c", "{ cd / && pwd -W; }").Output()
646+if err != nil {
647+// Default to a sane location.
648+mingwPrefix = []byte("C:/mingw64")
649+ }
650+651+prefix := strings.TrimSuffix(string(mingwPrefix), "/\n")
652+return strings.TrimPrefix(dir, prefix)
653+}