From 2b838a957aa808dfd345af0ad09bbfff9f2737fd Mon Sep 17 00:00:00 2001 From: Dean Sheather Date: Fri, 21 May 2021 19:28:32 +0000 Subject: [PATCH 1/5] Enforce coder-cli in PATH on Windows --- internal/cmd/configssh.go | 44 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/internal/cmd/configssh.go b/internal/cmd/configssh.go index f4598837..96001473 100644 --- a/internal/cmd/configssh.go +++ b/internal/cmd/configssh.go @@ -6,6 +6,7 @@ import ( "io/ioutil" "net/url" "os" + "os/exec" "os/user" "path/filepath" "runtime" @@ -114,8 +115,11 @@ func configSSH(configpath *string, remove *bool) func(cmd *cobra.Command, _ []st return xerrors.New("SSH is disabled or not available for any workspaces in your Coder deployment.") } - binPath, err := os.Executable() + binPath, err := binPath() if err != nil { + if runtime.GOOS == "windows" { + return xerrors.Errorf("Failed to ensure `coder` is in $PATH, please move the `coder` binary to a location in $PATH (such as System32): %w", err) + } return xerrors.Errorf("Failed to get executable path: %w", err) } @@ -147,6 +151,42 @@ func configSSH(configpath *string, remove *bool) func(cmd *cobra.Command, _ []st } } +// binPath returns the path to the coder binary suitable for use in ssh +// ProxyCommand. +func binPath() (string, error) { + exePath, err := os.Executable() + if err != nil { + return "", xerrors.Errorf("get executable path: %w", err) + } + + // On Windows, the coder-cli executable must be in $PATH for Msys2 and Git + // Bash to function correctly. To prevent weird behavior when people switch + // between the two, we require this for all users. + if runtime.GOOS == "windows" { + binName := filepath.Base(exePath) + pathPath, err := exec.LookPath(exePath) + if err != nil { + return "", xerrors.Errorf("locate %q in $PATH: %w", binName, err) + } + + // Warn the user if the current executable is not the same as the one in + // $PATH. + if filepath.Clean(pathPath) != filepath.Clean(exePath) { + clog.LogWarn( + "The current executable path does not match the executable path found in $PATH.", + "This may lead to problems connecting to your workspace via SSH.", + fmt.Sprintf("\t Current executable path: %q", exePath), + fmt.Sprintf("\tExecutable path in $PATH: %q", pathPath), + ) + } + + return binName, nil + } + + // On platforms other than Windows we can use the full path to the binary. + return exePath, nil +} + // removeOldConfig removes the old ssh configuration from the user's sshconfig. // Returns true if the config was modified. func removeOldConfig(config string) (string, bool) { @@ -212,7 +252,7 @@ func makeSSHConfig(binPath, host, userName, workspaceName, privateKeyFilepath st host := fmt.Sprintf( `Host coder.%s HostName coder.%s - ProxyCommand %s tunnel %s 12213 stdio + ProxyCommand "%s" tunnel %s 12213 stdio StrictHostKeyChecking no ConnectTimeout=0 IdentitiesOnly yes From 74b7fa48e9965f8129f8920bf2990ebe98e1d2d3 Mon Sep 17 00:00:00 2001 From: Dean Sheather Date: Fri, 21 May 2021 19:36:10 +0000 Subject: [PATCH 2/5] Warn if coder-cli not in PATH on Windows --- internal/cmd/configssh.go | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/internal/cmd/configssh.go b/internal/cmd/configssh.go index 96001473..839d9bf0 100644 --- a/internal/cmd/configssh.go +++ b/internal/cmd/configssh.go @@ -117,9 +117,6 @@ func configSSH(configpath *string, remove *bool) func(cmd *cobra.Command, _ []st binPath, err := binPath() if err != nil { - if runtime.GOOS == "windows" { - return xerrors.Errorf("Failed to ensure `coder` is in $PATH, please move the `coder` binary to a location in $PATH (such as System32): %w", err) - } return xerrors.Errorf("Failed to get executable path: %w", err) } @@ -159,14 +156,21 @@ func binPath() (string, error) { return "", xerrors.Errorf("get executable path: %w", err) } - // On Windows, the coder-cli executable must be in $PATH for Msys2 and Git - // Bash to function correctly. To prevent weird behavior when people switch - // between the two, we require this for all users. + // On Windows, the coder-cli executable must be in $PATH for both Msys2/Git + // Bash and OpenSSH for Windows (used by Powershell and VS Code) to function + // correctly. Check if the current executable is in $PATH, and warn the user + // if it isn't. if runtime.GOOS == "windows" { binName := filepath.Base(exePath) pathPath, err := exec.LookPath(exePath) if err != nil { - return "", xerrors.Errorf("locate %q in $PATH: %w", binName, err) + clog.LogWarn( + "The current executable is not in $PATH.", + "This may lead to problems connecting to your workspace via SSH.", + fmt.Sprintf("Please move %q to a location in your $PATH (such as System32) and run `%s config-ssh` again.", binName, binName), + ) + // Return the exePath so SSH at least works outside of Msys2. + return exePath, nil } // Warn the user if the current executable is not the same as the one in From 4a7aca7ddd4d7a310ceba9027729036635780e5e Mon Sep 17 00:00:00 2001 From: Dean Sheather Date: Fri, 21 May 2021 20:54:33 +0000 Subject: [PATCH 3/5] Use safeexec to get windows path --- go.mod | 1 + go.sum | 2 ++ internal/cmd/configssh.go | 10 ++++++---- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 583311bf..608b5872 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( cdr.dev/slog v1.4.1 cdr.dev/wsep v0.0.0-20200728013649-82316a09813f github.com/briandowns/spinner v1.16.0 + github.com/cli/safeexec v1.0.0 github.com/fatih/color v1.12.0 github.com/google/go-cmp v0.5.6 github.com/gorilla/websocket v1.4.2 diff --git a/go.sum b/go.sum index e6bda4ba..85e7c0bb 100644 --- a/go.sum +++ b/go.sum @@ -78,6 +78,8 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5O github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/cli/safeexec v1.0.0 h1:0VngyaIyqACHdcMNWfo6+KdUYnqEr2Sg+bSP1pdF+dI= +github.com/cli/safeexec v1.0.0/go.mod h1:Z/D4tTN8Vs5gXYHDCbaM1S/anmEDnJb1iW0+EJ5zx3Q= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= diff --git a/internal/cmd/configssh.go b/internal/cmd/configssh.go index 839d9bf0..df95441c 100644 --- a/internal/cmd/configssh.go +++ b/internal/cmd/configssh.go @@ -6,20 +6,19 @@ import ( "io/ioutil" "net/url" "os" - "os/exec" "os/user" "path/filepath" "runtime" "sort" "strings" - "cdr.dev/coder-cli/pkg/clog" - + "github.com/cli/safeexec" "github.com/spf13/cobra" "golang.org/x/xerrors" "cdr.dev/coder-cli/coder-sdk" "cdr.dev/coder-cli/internal/coderutil" + "cdr.dev/coder-cli/pkg/clog" ) const sshStartToken = "# ------------START-CODER-ENTERPRISE-----------" @@ -162,7 +161,10 @@ func binPath() (string, error) { // if it isn't. if runtime.GOOS == "windows" { binName := filepath.Base(exePath) - pathPath, err := exec.LookPath(exePath) + // We use safeexec instead of os/exec because os/exec returns paths in + // the current working directory, which we will run into very often when + // looking for our own path. + pathPath, err := safeexec.LookPath(binName) if err != nil { clog.LogWarn( "The current executable is not in $PATH.", From 3746bf5269c71fa78b880179b2ecfee754ad11d3 Mon Sep 17 00:00:00 2001 From: Dean Sheather Date: Sat, 24 Jul 2021 07:21:00 +0000 Subject: [PATCH 4/5] Delete GOARCH=386 from windows build --- .github/workflows/release.yaml | 14 +++++++------- Makefile | 4 ++-- ci/scripts/build.sh | 14 ++++++++++---- internal/cmd/configssh.go | 1 + 4 files changed, 20 insertions(+), 13 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 19739a82..4a0606fb 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -22,8 +22,8 @@ jobs: - name: Upload windows uses: actions/upload-artifact@v2 with: - name: coder-cli-windows-386 - path: ./ci/bin/coder-cli-windows-386.zip + name: coder-cli-windows + path: ./ci/bin/coder-cli-windows.zip build_darwin: name: Build darwin binary runs-on: macos-latest @@ -74,7 +74,7 @@ jobs: draft: true prerelease: false - name: Upload Linux Release - id: upload-linux-release-asset + id: upload-linux-release-asset uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -84,7 +84,7 @@ jobs: asset_name: coder-cli-linux-amd64.tar.gz asset_content_type: application/tar+gzip - name: Upload MacOS Release - id: upload-macos-release-asset + id: upload-macos-release-asset uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -94,12 +94,12 @@ jobs: asset_name: coder-cli-darwin-amd64.zip asset_content_type: application/zip - name: Upload Windows Release - id: upload-windows-release-asset + id: upload-windows-release-asset uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: coder-cli-windows-386/coder-cli-windows-386.zip - asset_name: coder-cli-windows-386.zip + asset_path: coder-cli-windows/coder-cli-windows.zip + asset_name: coder-cli-windows.zip asset_content_type: application/zip diff --git a/Makefile b/Makefile index edd1a303..24c3d13e 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -# Makefile for Coder CLI +# Makefile for Coder CLI .PHONY: clean build build/macos build/windows build/linux fmt lint gendocs test/go dev @@ -14,7 +14,7 @@ build/macos: # requires darwin CGO_ENABLED=1 GOOS=darwin GOARCH=amd64 ./ci/scripts/build.sh build/windows: - CGO_ENABLED=0 GOOS=windows GOARCH=386 ./ci/scripts/build.sh + CGO_ENABLED=0 GOOS=windows ./ci/scripts/build.sh build/linux: CGO_ENABLED=0 GOOS=linux GOARCH=amd64 ./ci/scripts/build.sh diff --git a/ci/scripts/build.sh b/ci/scripts/build.sh index d19f1bf6..34d05be5 100755 --- a/ci/scripts/build.sh +++ b/ci/scripts/build.sh @@ -8,11 +8,17 @@ set -euo pipefail cd "$(git rev-parse --show-toplevel)/ci/scripts" -tag=$(git describe --tags) +tag="$(git describe --tags)" -echo "--- building coder-cli for $GOOS-$GOARCH" +flavor="$GOOS" +if [[ "$GOOS" == "windows" ]]; then + unset GOARCH +else + flavor+="-$GOARCH" +fi +echo "--- building coder-cli for $flavor" -tmpdir=$(mktemp -d) +tmpdir="$(mktemp -d)" go build -ldflags "-X cdr.dev/coder-cli/internal/version.Version=${tag}" -o "$tmpdir/coder" ../../cmd/coder cp ../gon.json $tmpdir/gon.json @@ -20,7 +26,7 @@ cp ../gon.json $tmpdir/gon.json pushd "$tmpdir" case "$GOOS" in "windows") - artifact="coder-cli-$GOOS-$GOARCH.zip" + artifact="coder-cli-$GOOS.zip" mv coder coder.exe zip "$artifact" coder.exe ;; diff --git a/internal/cmd/configssh.go b/internal/cmd/configssh.go index df95441c..4d233c36 100644 --- a/internal/cmd/configssh.go +++ b/internal/cmd/configssh.go @@ -161,6 +161,7 @@ func binPath() (string, error) { // if it isn't. if runtime.GOOS == "windows" { binName := filepath.Base(exePath) + // We use safeexec instead of os/exec because os/exec returns paths in // the current working directory, which we will run into very often when // looking for our own path. From 5da2fc0677db5349fa597ae7f3d9916b2aae336f Mon Sep 17 00:00:00 2001 From: Dean Sheather Date: Mon, 26 Jul 2021 18:58:44 +0000 Subject: [PATCH 5/5] Add comment to unset GOARCH --- ci/scripts/build.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/ci/scripts/build.sh b/ci/scripts/build.sh index 34d05be5..821c062e 100755 --- a/ci/scripts/build.sh +++ b/ci/scripts/build.sh @@ -12,6 +12,7 @@ tag="$(git describe --tags)" flavor="$GOOS" if [[ "$GOOS" == "windows" ]]; then + # GOARCH causes bugs with the safeexec package on Windows. unset GOARCH else flavor+="-$GOARCH"