Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 2 additions & 0 deletions cmd/agent/container/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,8 @@ func (cmd *SetupContainerCmd) installIDE(setupInfo *config.Result, ide *provider
return cmd.setupVSCode(setupInfo, ide.Options, vscode.FlavorPositron, log)
case string(config2.IDECodium):
return cmd.setupVSCode(setupInfo, ide.Options, vscode.FlavorCodium, log)
case string(config2.IDECodiumInsiders):
return cmd.setupVSCode(setupInfo, ide.Options, vscode.FlavorCodiumInsiders, log)
case string(config2.IDEOpenVSCode):
return cmd.setupOpenVSCode(setupInfo, ide.Options, log)
case string(config2.IDEGoland):
Expand Down
26 changes: 21 additions & 5 deletions cmd/up.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ func (cmd *UpCmd) Run(
}

// configure container ssh
var sshConfigPath string
if cmd.ConfigureSSH {
devPodHome := ""
envDevPodHome, ok := os.LookupEnv("DEVPOD_HOME")
Expand All @@ -205,7 +206,7 @@ func (cmd *UpCmd) Run(
}
setupGPGAgentForwarding := cmd.GPGAgentForwarding || devPodConfig.ContextOption(config.ContextOptionGPGAgentForwarding) == "true"

err = configureSSH(devPodConfig, client, cmd.SSHConfigPath, user, workdir, setupGPGAgentForwarding, devPodHome)
sshConfigPath, err = configureSSH(devPodConfig, client, cmd.SSHConfigPath, user, workdir, setupGPGAgentForwarding, devPodHome)
if err != nil {
return err
}
Expand Down Expand Up @@ -238,6 +239,7 @@ func (cmd *UpCmd) Run(
result.SubstitutionContext.ContainerWorkspaceFolder,
vscode.Options.GetValue(ideConfig.Options, vscode.OpenNewWindow) == "true",
vscode.FlavorStable,
sshConfigPath,
log,
)
case string(config.IDEVSCodeInsiders):
Expand All @@ -247,6 +249,7 @@ func (cmd *UpCmd) Run(
result.SubstitutionContext.ContainerWorkspaceFolder,
vscode.Options.GetValue(ideConfig.Options, vscode.OpenNewWindow) == "true",
vscode.FlavorInsiders,
sshConfigPath,
log,
)
case string(config.IDECursor):
Expand All @@ -256,6 +259,7 @@ func (cmd *UpCmd) Run(
result.SubstitutionContext.ContainerWorkspaceFolder,
vscode.Options.GetValue(ideConfig.Options, vscode.OpenNewWindow) == "true",
vscode.FlavorCursor,
sshConfigPath,
log,
)
case string(config.IDECodium):
Expand All @@ -265,6 +269,17 @@ func (cmd *UpCmd) Run(
result.SubstitutionContext.ContainerWorkspaceFolder,
vscode.Options.GetValue(ideConfig.Options, vscode.OpenNewWindow) == "true",
vscode.FlavorCodium,
sshConfigPath,
log,
)
case string(config.IDECodiumInsiders):
return vscode.Open(
ctx,
client.Workspace(),
result.SubstitutionContext.ContainerWorkspaceFolder,
vscode.Options.GetValue(ideConfig.Options, vscode.OpenNewWindow) == "true",
vscode.FlavorCodiumInsiders,
sshConfigPath,
log,
)
case string(config.IDEPositron):
Expand All @@ -274,6 +289,7 @@ func (cmd *UpCmd) Run(
result.SubstitutionContext.ContainerWorkspaceFolder,
vscode.Options.GetValue(ideConfig.Options, vscode.OpenNewWindow) == "true",
vscode.FlavorPositron,
sshConfigPath,
log,
)
case string(config.IDEOpenVSCode):
Expand Down Expand Up @@ -995,10 +1011,10 @@ func startBrowserTunnel(
return nil
}

func configureSSH(c *config.Config, client client2.BaseWorkspaceClient, sshConfigPath, user, workdir string, gpgagent bool, devPodHome string) error {
func configureSSH(c *config.Config, client client2.BaseWorkspaceClient, sshConfigPath, user, workdir string, gpgagent bool, devPodHome string) (string, error) {
path, err := devssh.ResolveSSHConfigPath(sshConfigPath)
if err != nil {
return errors.Wrap(err, "Invalid ssh config path")
return "", errors.Wrap(err, "Invalid ssh config path")
}
sshConfigPath = path

Expand All @@ -1013,10 +1029,10 @@ func configureSSH(c *config.Config, client client2.BaseWorkspaceClient, sshConfi
log.Default,
)
if err != nil {
return err
return "", err
}

return nil
return sshConfigPath, nil
}

func mergeDevPodUpOptions(baseOptions *provider2.CLIOptions) error {
Expand Down
1 change: 1 addition & 0 deletions pkg/config/ide.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,6 @@ const (
IDEPositron IDE = "positron"
IDEMarimo IDE = "marimo"
IDECodium IDE = "codium"
IDECodiumInsiders IDE = "codium-insiders"
Copy link
Member

Choose a reason for hiding this comment

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

We'll have to expose this in DevPod Desktop as well, will create a follow up PR

IDEZed IDE = "zed"
)
7 changes: 7 additions & 0 deletions pkg/ide/ideparse/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,13 @@ var AllowedIDEs = []AllowedIDE{
Icon: "https://devpod.sh/assets/codium.svg",
Experimental: true,
},
{
Name: config.IDECodiumInsiders,
DisplayName: "Codium Insiders",
Options: vscode.Options,
Icon: "https://devpod.sh/assets/codium_insiders.svg",
Experimental: true,
},
{
Name: config.IDEPositron,
DisplayName: "Positron",
Expand Down
143 changes: 88 additions & 55 deletions pkg/ide/vscode/open.go
Original file line number Diff line number Diff line change
@@ -1,21 +1,30 @@
package vscode

import (
"bytes"
"context"
"errors"
"fmt"
"os/exec"
"runtime"
"slices"
"strings"

"github.com/loft-sh/devpod/pkg/command"
"github.com/loft-sh/log"
"github.com/skratchdot/open-golang/open"
)

func Open(ctx context.Context, workspace, folder string, newWindow bool, flavor Flavor, log log.Logger) error {
const (
FlatpakStable string = "com.visualstudio.code"
FlatpakInsiders string = "com.visualstudio.code.insiders"
FlatpakCodium string = "com.vscodium.codium"
FlatpakCodiumInsiders string = "com.vscodium.codium-insiders"
)

func Open(ctx context.Context, workspace, folder string, newWindow bool, flavor Flavor, sshConfigPath string, log log.Logger) error {
log.Infof("Starting %s...", flavor.DisplayName())
cliErr := openViaCLI(ctx, workspace, folder, newWindow, flavor, log)
cliErr := openViaCLI(ctx, workspace, folder, newWindow, flavor, sshConfigPath, log)
if cliErr == nil {
return nil
}
Expand All @@ -41,6 +50,8 @@ func openViaBrowser(workspace, folder string, newWindow bool, flavor Flavor, log
protocol = `positron://`
case FlavorCodium:
protocol = `codium://`
case FlavorCodiumInsiders:
protocol = `codium-insiders://`
}

openURL := protocol + `vscode-remote/ssh-remote+` + workspace + `.devpod/` + folder
Expand All @@ -58,20 +69,44 @@ func openViaBrowser(workspace, folder string, newWindow bool, flavor Flavor, log
return nil
}

func openViaCLI(ctx context.Context, workspace, folder string, newWindow bool, flavor Flavor, log log.Logger) error {
func openViaCLI(ctx context.Context, workspace, folder string, newWindow bool, flavor Flavor, sshConfigPath string, log log.Logger) error {
// try to find code cli
codePath := findCLI(flavor)
if codePath == "" {
codePath := findCLI(flavor, log)
if codePath == nil {
return fmt.Errorf("couldn't find the %s binary", flavor)
}

if codePath[0] == "flatpak" {
log.Debugf("Running with Flatpak using the package %s.", codePath[2])
out, err := exec.Command(codePath[0], "ps", "--columns=application").Output()
if err != nil {
return command.WrapCommandError(out, err)
}
splitted := strings.Split(string(out), "\n")
foundRunning := false
// Ignore the header
for _, str := range splitted[1:] {
if strings.TrimSpace(str) == codePath[2] {
foundRunning = true
break
}
}

if foundRunning {
log.Warnf("The IDE is already running via Flatpak. If you are encountering SSH connectivity issues, make sure to give read access to your SSH config file (e.g flatpak override %s --filesystem=%s) and restart your IDE.", codePath[2], sshConfigPath)
}

codePath = slices.Insert(codePath, 2, fmt.Sprintf("--filesystem=%s:ro", sshConfigPath))
}

sshExtension := "ms-vscode-remote.remote-ssh"
if flavor == FlavorCodium {
if flavor == FlavorCodium || flavor == FlavorCodiumInsiders {
sshExtension = "jeanp413.open-remote-ssh"
}

// make sure ms-vscode-remote.remote-ssh is installed
out, err := exec.Command(codePath, "--list-extensions").Output()
listArgs := append(codePath, "--list-extensions")
out, err := exec.Command(listArgs[0], listArgs[1:]...).Output()
if err != nil {
return command.WrapCommandError(out, err)
}
Expand All @@ -88,9 +123,9 @@ func openViaCLI(ctx context.Context, workspace, folder string, newWindow bool, f

// install remote-ssh extension
if !found {
args := []string{"--install-extension", sshExtension}
log.Debugf("Run vscode command %s %s", codePath, strings.Join(args, " "))
out, err := exec.CommandContext(ctx, codePath, args...).Output()
args := append(codePath, "--install-extension", sshExtension)
log.Debugf("Run vscode command %s %s", args[0], strings.Join(args[1:], " "))
out, err := exec.CommandContext(ctx, args[0], args[1:]...).Output()
if err != nil {
return fmt.Errorf("install ssh extension: %w", command.WrapCommandError(out, err))
}
Expand All @@ -108,66 +143,64 @@ func openViaCLI(ctx context.Context, workspace, folder string, newWindow bool, f
}
// Needs to be separated by `=` because of windows
folderUriArg := fmt.Sprintf("--folder-uri=vscode-remote://ssh-remote+%s.devpod/%s", workspace, folder)
args = append(codePath, args...)
args = append(args, folderUriArg)
log.Debugf("Run %s command %s %s", flavor.DisplayName(), codePath, strings.Join(args, " "))
out, err = exec.CommandContext(ctx, codePath, args...).CombinedOutput()
cmd := exec.CommandContext(ctx, args[0], args[1:]...)
var b bytes.Buffer
if codePath[0] != "flatpak" {
cmd.Stdout = &b
cmd.Stderr = &b
} else {
log.Debug("Skipping output capture due to issue with `flatpak run` leading the command to hang")
}
log.Debugf("Run %s command %s %s", flavor.DisplayName(), args[0], strings.Join(args[1:], " "))
err = cmd.Run()
if err != nil {
return command.WrapCommandError(out, err)
return command.WrapCommandError(b.Bytes(), err)
}

return nil
}

func findCLI(flavor Flavor) string {
if flavor == FlavorStable {
if command.Exists("code") {
return "code"
} else if runtime.GOOS == "darwin" && command.Exists("/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code") {
return "/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code"
}

return ""
func existsInFlatpak(packageName string, log log.Logger) bool {
if err := exec.Command("flatpak", "info", packageName).Run(); err == nil {
return true
} else {
log.Debugf("Flatpak command for %s returned: %s", packageName, err)
}
return false
}

if flavor == FlavorInsiders {
if command.Exists("code-insiders") {
return "code-insiders"
} else if runtime.GOOS == "darwin" && command.Exists("/Applications/Visual Studio Code - Insiders.app/Contents/Resources/app/bin/code") {
return "/Applications/Visual Studio Code - Insiders.app/Contents/Resources/app/bin/code"
}

return ""
func getCommandArgs(execName, macOSPath, flatpakPackage string, log log.Logger) []string {
if command.Exists(execName) {
return []string{execName}
}

if flavor == FlavorCursor {
if command.Exists("cursor") {
return "cursor"
} else if runtime.GOOS == "darwin" && command.Exists("/Applications/Cursor.app/Contents/Resources/app/bin/cursor") {
return "/Applications/Cursor.app/Contents/Resources/app/bin/cursor"
}

return ""
if runtime.GOOS == "darwin" && command.Exists(macOSPath) {
return []string{macOSPath}
}

if flavor == FlavorPositron {
if command.Exists("positron") {
return "positron"
} else if runtime.GOOS == "darwin" && command.Exists("/Applications/Positron.app/Contents/Resources/app/bin/positron") {
return "/Applications/Positron.app/Contents/Resources/app/bin/positron"
}

return ""
if runtime.GOOS == "linux" && flatpakPackage != "" && command.Exists("flatpak") && existsInFlatpak(flatpakPackage, log) {
return []string{"flatpak", "run", flatpakPackage}
}

if flavor == FlavorCodium {
if command.Exists("codium") {
return "codium"
} else if runtime.GOOS == "darwin" && command.Exists("/Applications/Codium.app/Contents/Resources/app/bin/codium") {
return "/Applications/Codium.app/Contents/Resources/app/bin/codium"
}
return nil
}

return ""
func findCLI(flavor Flavor, log log.Logger) []string {
switch flavor {
case FlavorStable:
return getCommandArgs("code", "/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code", FlatpakStable, log)
case FlavorInsiders:
return getCommandArgs("code-insiders", "/Applications/Visual Studio Code - Insiders.app/Contents/Resources/app/bin/code", FlatpakInsiders, log)
case FlavorCursor:
return getCommandArgs("cursor", "/Applications/Cursor.app/Contents/Resources/app/bin/cursor", "", log)
case FlavorPositron:
return getCommandArgs("positron", "/Applications/Positron.app/Contents/Resources/app/bin/positron", "", log)
case FlavorCodium:
return getCommandArgs("codium", "/Applications/Codium.app/Contents/Resources/app/bin/codium", FlatpakCodium, log)
case FlavorCodiumInsiders:
return getCommandArgs("codium-insiders", "/Applications/CodiumInsiders.app/Contents/Resources/app/bin/codium-insiders", FlatpakCodiumInsiders, log)
}

return ""
return nil
}
Loading