Skip to content

feat(cli): add coder open vscode #11191

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Jan 2, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
win
  • Loading branch information
mafredri committed Jan 2, 2024
commit 257c45fdb0adf72d6249a40aa21324670c08968c
52 changes: 50 additions & 2 deletions cli/open.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"net/url"
"path"
"path/filepath"
"runtime"
"strings"

"github.com/skratchdot/open-golang/open"
Expand Down Expand Up @@ -119,8 +120,14 @@ func (r *RootCmd) openVSCode() *clibase.Cmd {
return xerrors.Errorf("path %q requires expansion and is not supported, use an absolute path instead", d)

case workspaceAgent.OperatingSystem == "windows":
// TODO(mafredri): For now we keep this simple instead of discerning out relative paths on Windows.
directory = d
switch {
case directory != "" && !isWindowsAbsPath(d):
directory = windowsJoinPath(directory, d)
case isWindowsAbsPath(d):
directory = d
default:
return xerrors.Errorf("path %q not supported, use an absolute path instead", d)
}

// Note that we use `path` instead of `filepath` since we want Unix behavior.
case directory != "" && !path.IsAbs(d):
Expand Down Expand Up @@ -218,3 +225,44 @@ func (r *RootCmd) openVSCode() *clibase.Cmd {

return cmd
}

// isWindowsAbsPath checks if the path is an absolute path on Windows. On Unix
// systems the check is very simplistic and does not cover edge cases.
//
//nolint:revive // Shadow path variable for readability.
func isWindowsAbsPath(path string) bool {
if runtime.GOOS == "windows" {
return filepath.IsAbs(path)
}

switch {
case len(path) >= 2 && path[1] == ':':
// Path starts with a drive letter.
return len(path) == 2 || (len(path) >= 4 && path[2] == '\\' && path[3] == '\\')
default:
return false
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried /path/to/something, \path\to\something and C:/path/to/something which also both work in VSCode. Defaults to the drive Windows is installed on if there isn't a drive letter.

IDK how paths work in Windows containers either, unsure if they have drive letters or not.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I'll need to play around with this some more. I was actually planning on testing with agents on Windows today, but it turns out our win template is broken atm. I know at least the "expanded directory" resolves to C:\... via test runners. I suspected / might work in VS Code but out of precaution I wanted to transform them.

Perhaps we can just make everything slash and only have the logic for detecting abs path on Windows.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added some handling for the cases you mentioned, and relaxed the abs requirement to include /, \. I know there are more exceptions like \\.UNC\ paths and the like, but I don't know if it's worth adding preemptive handling for that.

Copy link
Member Author

@mafredri mafredri Dec 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I can't seem to make CI happy because Go filepath.IsAbs on windows seems to think differently? https://github.com/coder/coder/actions/runs/7262677715/job/19786347550?pr=11191#step:5:496

(I kept the fallback to filepath vs our impl. on Windows so that we could catch these types of inconsistencies, but I can just remove that if we don't care.)

}
}

// windowsJoinPath joins the elements into a path, using Windows path separator
// and converting forward slashes to backslashes. On Unix systems a very
// simplistic join operator is used.
func windowsJoinPath(elem ...string) string {
if runtime.GOOS == "windows" {
return filepath.Join(elem...)
}

var s string
for _, e := range elem {
e = strings.ReplaceAll(e, "/", "\\")
if e == "" {
continue
}
if s == "" {
s = e
continue
}
s += "\\" + strings.TrimSuffix(s, "\\")
}
return s
}
12 changes: 10 additions & 2 deletions cli/open_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"net/url"
"os"
"path/filepath"
"runtime"
"testing"

"github.com/stretchr/testify/assert"
Expand All @@ -27,6 +28,7 @@ func TestOpenVSCode(t *testing.T) {
client, workspace, agentToken := setupWorkspaceForAgent(t, func(agents []*proto.Agent) []*proto.Agent {
agents[0].Directory = agentDir
agents[0].Name = agentName
agents[0].OperatingSystem = runtime.GOOS
return agents
})

Expand Down Expand Up @@ -161,6 +163,7 @@ func TestOpenVSCode_NoAgentDirectory(t *testing.T) {
agentName := "agent1"
client, workspace, agentToken := setupWorkspaceForAgent(t, func(agents []*proto.Agent) []*proto.Agent {
agents[0].Name = agentName
agents[0].OperatingSystem = runtime.GOOS
return agents
})

Expand All @@ -176,6 +179,11 @@ func TestOpenVSCode_NoAgentDirectory(t *testing.T) {
wd, err := os.Getwd()
require.NoError(t, err)

absPath := "/home/coder"
if runtime.GOOS == "windows" {
absPath = "C:\\home\\coder"
}

tests := []struct {
name string
args []string
Expand All @@ -196,8 +204,8 @@ func TestOpenVSCode_NoAgentDirectory(t *testing.T) {
},
{
name: "ok with absolute path",
args: []string{"--test.open-error", workspace.Name, "/home/coder"},
wantDir: "/home/coder",
args: []string{"--test.open-error", workspace.Name, absPath},
wantDir: absPath,
},
{
name: "ok with token",
Expand Down