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.

108109

sshCmdStr := fmt.Sprintf("ssh %v %v '/usr/bin/env bash -l'", o.sshFlags, host)

109-110110

sshCmd := exec.Command("sh", "-l", "-c", sshCmdStr)

111111

sshCmd.Stdout = os.Stdout

112112

sshCmd.Stderr = os.Stderr

@@ -145,10 +145,9 @@ func sshCode(host, dir string, o options) error {

145145

flog.Info("Tunneling remote port %v to %v", o.remotePort, o.bindAddr)

146146147147

sshCmdStr :=

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.

153152

sshCmd := exec.Command("sh", "-l", "-c", sshCmdStr)

154153

sshCmd.Stdin = os.Stdin

@@ -266,9 +265,12 @@ func openBrowser(url string) {

266265

const (

267266

macPath = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"

268267

wslPath = "/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe"

268+

winPath = "C:/Program Files (x86)/Google/Chrome/Application/chrome.exe"

269269

)

270270271271

switch {

272+

case commandExists("chrome"):

273+

openCmd = exec.Command("chrome", chromeOptions(url)...)

272274

case commandExists("google-chrome"):

273275

openCmd = exec.Command("google-chrome", chromeOptions(url)...)

274276

case commandExists("google-chrome-stable"):

@@ -281,6 +283,8 @@ func openBrowser(url string) {

281283

openCmd = exec.Command(macPath, chromeOptions(url)...)

282284

case pathExists(wslPath):

283285

openCmd = exec.Command(wslPath, chromeOptions(url)...)

286+

case pathExists(winPath):

287+

openCmd = exec.Command(winPath, chromeOptions(url)...)

284288

default:

285289

err := browser.OpenURL(url)

286290

if 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.

337341

func 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+338347

sshDirectoryMode, err := os.Lstat(expandPath(sshDirectory))

339348

if err != nil {

340349

if reuseConnection {

@@ -451,8 +460,10 @@ func syncUserSettings(sshFlags string, host string, back bool) error {

451460

return 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+

}

456467

var (

457468

src = localConfDir + "/"

458469

dest = host + ":" + remoteSettingsDir

@@ -477,7 +488,10 @@ func syncExtensions(sshFlags string, host string, back bool) error {

477488

return 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+

}

481495482496

var (

483497

src = 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",

508523

src, 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

526541

pkill -f %v || true

527-

mkdir -p ~/.local/share/code-server %v

542+

mkdir -p $HOME/.local/share/code-server %v

528543

cd %v

529544

curlflags="-o latest-linux"

530545

if [ -f latest-linux ]; then

@@ -535,8 +550,8 @@ curl $curlflags https://codesrv-ci.cdr.sh/latest-linux

535550

ln latest-linux %v

536551

chmod +x %v`,

537552

codeServerPath,

538-

filepath.Dir(codeServerPath),

539-

filepath.Dir(codeServerPath),

553+

filepath.ToSlash(filepath.Dir(codeServerPath)),

554+

filepath.ToSlash(filepath.Dir(codeServerPath)),

540555

codeServerPath,

541556

codeServerPath,

542557

codeServerPath,

@@ -548,6 +563,11 @@ chmod +x %v`,

548563

func ensureDir(path string) error {

549564

_, err := os.Stat(path)

550565

if 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+

}

551571

err = os.MkdirAll(path, 0750)

552572

}

553573

@@ -608,3 +628,26 @@ func parseGCPSSHCmd(instance string) (ip, sshFlags string, err error) {

608628609629

return 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+

}