diff --git a/pkg/ide/vscode/vscode.go b/pkg/ide/vscode/vscode.go index 9646015e1..1a17ebcaf 100644 --- a/pkg/ide/vscode/vscode.go +++ b/pkg/ide/vscode/vscode.go @@ -3,6 +3,7 @@ package vscode import ( "context" "fmt" + "io/fs" "os" "os/exec" "path/filepath" @@ -248,7 +249,22 @@ func (o *VsCodeServer) findServerBinaryPath(location string) string { out, err := exec.CommandContext(ctx, binPath, "--help").CombinedOutput() cancel() if err != nil { - o.log.Infof("Execute %s: %v", binPath, command.WrapCommandError(out, err)) + legacyErr := command.WrapCommandError(out, err) + nestedBinPath, nestedErr := findServerBinaryInDir(binDir, "cursor-server") + if nestedErr == nil && nestedBinPath != binPath { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*4) + out, err = exec.CommandContext(ctx, nestedBinPath, "--help").CombinedOutput() + cancel() + if err == nil { + binPath = nestedBinPath + break + } + + o.log.Infof("Execute %s: %v", nestedBinPath, command.WrapCommandError(out, err)) + } else { + o.log.Infof("Execute %s: %v", binPath, legacyErr) + } + o.log.Info("Wait until cursor-server is installed...") time.Sleep(time.Second * 3) continue @@ -398,6 +414,36 @@ func (o *VsCodeServer) findServerBinaryPath(location string) string { return binPath } +func findServerBinaryInDir(root, binaryName string) (string, error) { + if _, err := os.Stat(root); err != nil { + return "", err + } + + found := "" + err := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + if d.IsDir() { + return nil + } + if filepath.Base(path) != binaryName { + return nil + } + + found = path + return fs.SkipAll + }) + if err != nil && err != fs.SkipAll { + return "", err + } + if found == "" { + return "", fmt.Errorf("read dir %s: install dir is missing", root) + } + + return found, nil +} + func (o *VsCodeServer) findCodeServerBinary(location string) (string, error) { serversDir := filepath.Join(location, "cli", "servers") entries, err := os.ReadDir(serversDir) diff --git a/pkg/ide/vscode/vscode_test.go b/pkg/ide/vscode/vscode_test.go new file mode 100644 index 000000000..28e58e4a5 --- /dev/null +++ b/pkg/ide/vscode/vscode_test.go @@ -0,0 +1,57 @@ +package vscode + +import ( + "os" + "path/filepath" + "testing" + + "github.com/loft-sh/log" +) + +func TestFindServerBinaryPathCursorLegacyLayout(t *testing.T) { + t.Parallel() + + location := t.TempDir() + expectedPath := filepath.Join(location, "bin", "123456", "bin", "cursor-server") + writeExecutable(t, expectedPath) + + server := &VsCodeServer{ + flavor: FlavorCursor, + log: log.Discard, + } + + if got := server.findServerBinaryPath(location); got != expectedPath { + t.Fatalf("expected %q, got %q", expectedPath, got) + } +} + +func TestFindServerBinaryPathCursorPlatformLayout(t *testing.T) { + t.Parallel() + + location := t.TempDir() + expectedPath := filepath.Join(location, "bin", "linux-arm64", "abcdef", "bin", "cursor-server") + writeExecutable(t, expectedPath) + if err := os.MkdirAll(filepath.Join(location, "bin", "multiplex-server"), 0755); err != nil { + t.Fatalf("create multiplex-server dir: %v", err) + } + + server := &VsCodeServer{ + flavor: FlavorCursor, + log: log.Discard, + } + + if got := server.findServerBinaryPath(location); got != expectedPath { + t.Fatalf("expected %q, got %q", expectedPath, got) + } +} + +func writeExecutable(t *testing.T, path string) { + t.Helper() + + if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil { + t.Fatalf("create parent dir: %v", err) + } + if err := os.WriteFile(path, []byte("#!/bin/sh\nexit 0\n"), 0755); err != nil { + t.Fatalf("write executable: %v", err) + } +}