Skip to content
Merged
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
5 changes: 5 additions & 0 deletions .golangci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ linters:
- gosec
text: "G101"
path: "pkg/telemetry/analytics/client.go"
# Per-IDE async commands and setup functions are intentionally parallel
# (one package per browser IDE), so dupl across them is by design.
- linters:
- dupl
path: "cmd/internal/agentcontainer/(codeserver|vscodeweb|openvscode)_async.go"
- path: "hack/"
linters:
- forbidigo
Expand Down
2 changes: 1 addition & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ See the [install instructions for Devsy](https://devsy.sh/docs/getting-started/i


## Reporting a Vulnerability
Please report vulnerabilities to: [security@devsy.sh](mailto:security@devsy.sh)
Please report vulnerabilities to: [dev@devsy.sh](mailto:dev@devsy.sh)
1 change: 1 addition & 0 deletions cmd/internal/agentcontainer/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ func NewContainerCmd(flags *flags.GlobalFlags) *cobra.Command {
containerCmd.AddCommand(NewVSCodeAsyncCmd())
containerCmd.AddCommand(NewOpenVSCodeAsyncCmd())
containerCmd.AddCommand(NewCodeServerAsyncCmd())
containerCmd.AddCommand(NewVSCodeWebAsyncCmd())
containerCmd.AddCommand(NewCredentialsServerCmd(flags))
containerCmd.AddCommand(NewSetupDevsyPlatformAccessCmd(flags))
containerCmd.AddCommand(NewSSHServerCmd(flags))
Expand Down
66 changes: 66 additions & 0 deletions cmd/internal/agentcontainer/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import (
"github.com/devsy-org/devsy/pkg/ide/openvscode"
"github.com/devsy-org/devsy/pkg/ide/rstudio"
"github.com/devsy-org/devsy/pkg/ide/vscode"
"github.com/devsy-org/devsy/pkg/ide/vscodeweb"
"github.com/devsy-org/devsy/pkg/log"
provider2 "github.com/devsy-org/devsy/pkg/provider"
"github.com/devsy-org/devsy/pkg/ts"
Expand Down Expand Up @@ -595,6 +596,8 @@ func (cmd *SetupContainerCmd) installIDE(
return cmd.setupOpenVSCode(setupInfo, ide.Options)
case string(config2.IDECodeServer):
return cmd.setupCodeServer(setupInfo, ide.Options)
case string(config2.IDEVSCodeWeb):
return cmd.setupVSCodeWeb(setupInfo, ide.Options)
case string(config2.IDEGoland):
return jetbrains.NewGolandServer(config.GetRemoteUser(setupInfo), ide.Options).
Install(setupInfo)
Expand Down Expand Up @@ -773,6 +776,7 @@ func (cmd *SetupContainerCmd) setupOpenVSCode(
return openVSCode.Start()
}

//nolint:dupl // mirrored by setupVSCodeWeb by design (one package per browser IDE).
func (cmd *SetupContainerCmd) setupCodeServer(
setupInfo *config.Result,
ideOptions map[string]config2.OptionValue,
Expand Down Expand Up @@ -831,6 +835,68 @@ func (cmd *SetupContainerCmd) setupCodeServer(
return cs.Start()
}

// setupVSCodeWeb intentionally mirrors setupCodeServer; the per-IDE browser
// setup functions are parallel by design (one package per browser IDE).
//
//nolint:dupl // mirrors setupCodeServer by design.
func (cmd *SetupContainerCmd) setupVSCodeWeb(
setupInfo *config.Result,
ideOptions map[string]config2.OptionValue,
) error {
log.Debugf("setup vscode-web")
vsCodeConfiguration := config.GetVSCodeConfiguration(setupInfo.MergedConfig)
settings := ""
if len(vsCodeConfiguration.Settings) > 0 {
out, err := json.Marshal(vsCodeConfiguration.Settings)
if err != nil {
return err
}
settings = string(out)
}

user := config.GetRemoteUser(setupInfo)
vw := vscodeweb.NewVSCodeWeb(vscodeweb.ServerOptions{
Extensions: vsCodeConfiguration.Extensions,
Settings: settings,
UserName: user,
Host: "0.0.0.0",
Port: strconv.Itoa(vscodeweb.DefaultVSCodeWebPort),
Values: ideOptions,
})
Comment on lines +858 to +865

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Honor BIND_ADDRESS instead of hardcoding the default listener.

pkg/ide/vscodeweb/vscodeweb.go exposes BIND_ADDRESS, but this path always passes 0.0.0.0:10802, and NewVSCodeWeb only uses Values for download selection. As written, any user-supplied bind address is ignored.

Suggested fix
+	host := "0.0.0.0"
+	port := strconv.Itoa(vscodeweb.DefaultVSCodeWebPort)
+	if bindAddr := vscodeweb.Options.GetValue(ideOptions, vscodeweb.BindAddressOption); bindAddr != "" {
+		parsedHost, parsedPort, err := net.SplitHostPort(bindAddr)
+		if err != nil {
+			return fmt.Errorf("parse %s: %w", vscodeweb.BindAddressOption, err)
+		}
+		host, port = parsedHost, parsedPort
+	}
+
 	vw := vscodeweb.NewVSCodeWeb(vscodeweb.ServerOptions{
 		Extensions: vsCodeConfiguration.Extensions,
 		Settings:   settings,
 		UserName:   user,
-		Host:       "0.0.0.0",
-		Port:       strconv.Itoa(vscodeweb.DefaultVSCodeWebPort),
+		Host:       host,
+		Port:       port,
 		Values:     ideOptions,
 	})
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
vw := vscodeweb.NewVSCodeWeb(vscodeweb.ServerOptions{
Extensions: vsCodeConfiguration.Extensions,
Settings: settings,
UserName: user,
Host: "0.0.0.0",
Port: strconv.Itoa(vscodeweb.DefaultVSCodeWebPort),
Values: ideOptions,
})
host := "0.0.0.0"
port := strconv.Itoa(vscodeweb.DefaultVSCodeWebPort)
if bindAddr := vscodeweb.Options.GetValue(ideOptions, vscodeweb.BindAddressOption); bindAddr != "" {
parsedHost, parsedPort, err := net.SplitHostPort(bindAddr)
if err != nil {
return fmt.Errorf("parse %s: %w", vscodeweb.BindAddressOption, err)
}
host, port = parsedHost, parsedPort
}
vw := vscodeweb.NewVSCodeWeb(vscodeweb.ServerOptions{
Extensions: vsCodeConfiguration.Extensions,
Settings: settings,
UserName: user,
Host: host,
Port: port,
Values: ideOptions,
})
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/internal/agentcontainer/setup.go` around lines 853 - 860, The VSCode Web
setup is ignoring the configured bind address and always hardcoding the listener
in the NewVSCodeWeb ServerOptions. Update the setup in setup.go to read and pass
through the user-supplied BIND_ADDRESS/ideOptions value instead of fixed 0.0.0.0
and the default port, and ensure NewVSCodeWeb receives the actual bind target so
it can honor the configured address. Use the ServerOptions construction around
NewVSCodeWeb and the BIND_ADDRESS handling in vscodeweb.go as the key places to
align.


if err := vw.Install(); err != nil {
return err
}

if len(vsCodeConfiguration.Extensions) > 0 {
err := command.StartBackgroundOnce("vscode-web-async", func() (*exec.Cmd, error) {
log.Infof(
"installing extensions in the background: %s",
strings.Join(vsCodeConfiguration.Extensions, ","),
)
binaryPath, err := os.Executable()
if err != nil {
return nil, err
}
//nolint:gosec // binaryPath is from os.Executable(), not user input
return exec.Command(
binaryPath,
cmdInternal,
cmdAgent,
cmdContainer,
"vscode-web-async",
"--setup-info",
cmd.SetupInfo,
), nil
})
if err != nil {
return fmt.Errorf("install extensions: %w", err)
}
}

return vw.Start()
}

func configureSystemGitCredentials(
ctx context.Context,
client tunnel.TunnelClient,
Expand Down
59 changes: 59 additions & 0 deletions cmd/internal/agentcontainer/vscodeweb_async.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package agentcontainer

import (
"encoding/json"

"github.com/devsy-org/devsy/cmd/flags"
"github.com/devsy-org/devsy/pkg/compress"
"github.com/devsy-org/devsy/pkg/devcontainer/config"
"github.com/devsy-org/devsy/pkg/ide/vscodeweb"
"github.com/devsy-org/devsy/pkg/log"
"github.com/spf13/cobra"
)

// VSCodeWebAsyncCmd holds the cmd flags.
type VSCodeWebAsyncCmd struct {
*flags.GlobalFlags

SetupInfo string
}

// NewVSCodeWebAsyncCmd creates a new command.
func NewVSCodeWebAsyncCmd() *cobra.Command {
cmd := &VSCodeWebAsyncCmd{}
vsCodeWebAsyncCmd := &cobra.Command{
Use: "vscode-web-async",
Short: "Starts VS Code Web",
Args: cobra.NoArgs,
RunE: cmd.Run,
}
vsCodeWebAsyncCmd.Flags().
StringVar(&cmd.SetupInfo, "setup-info", "", "The container setup info")
_ = vsCodeWebAsyncCmd.MarkFlagRequired("setup-info")
return vsCodeWebAsyncCmd
}

// Run runs the command logic.
func (cmd *VSCodeWebAsyncCmd) Run(_ *cobra.Command, _ []string) error {
log.Debugf("Start setting up container")
decompressed, err := compress.Decompress(cmd.SetupInfo)
if err != nil {
return err
}

setupInfo := &config.Result{}
if err := json.Unmarshal([]byte(decompressed), setupInfo); err != nil {
return err
}

return setupVSCodeWebExtensions(setupInfo)
}

func setupVSCodeWebExtensions(setupInfo *config.Result) error {
vsCodeConfiguration := config.GetVSCodeConfiguration(setupInfo.MergedConfig)
user := config.GetRemoteUser(setupInfo)
return vscodeweb.NewVSCodeWeb(vscodeweb.ServerOptions{
Extensions: vsCodeConfiguration.Extensions,
UserName: user,
}).InstallExtensions()
}
2 changes: 1 addition & 1 deletion cmd/pro/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ func (cmd *StartCmd) retryUpgradeAfterPurge(
"- via Online Chat: %s\n- via Email: %s\n",
greenBold("https://slack.devsy.sh/"),
greenBold("https://devsy.sh/"),
greenBold("support@devsy.sh"),
greenBold("dev@devsy.sh"),
),
)
}
Expand Down
2 changes: 1 addition & 1 deletion desktop/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"homepage": "https://devsy.sh",
"author": {
"name": "Devsy",
"email": "support@devsy.sh"
"email": "dev@devsy.sh"
},
"type": "module",
"main": "dist/main/index.js",
Expand Down
37 changes: 37 additions & 0 deletions desktop/src/renderer/public/icons/ides/vscode-web.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ const IDE_GROUPS = [
{ value: "none", label: "None", iconName: "none" },
{ value: "vscode", label: "VS Code", iconName: "vscode" },
{ value: "openvscode", label: "VS Code Browser", iconName: "vscodebrowser" },
{ value: "vscode-web", label: "VS Code Web", iconName: "vscode-web" },
{ value: "code-server", label: "code-server", iconName: "code-server" },
{ value: "cursor", label: "Cursor", iconName: "cursor" },
{ value: "zed", label: "Zed", iconName: "zed" },
Expand Down
1 change: 1 addition & 0 deletions desktop/src/renderer/src/pages/SettingsPage.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ const IDE_OPTIONS = [
{ value: "none", label: "None" },
{ value: "vscode", label: "VS Code" },
{ value: "openvscode", label: "VS Code Browser" },
{ value: "vscode-web", label: "VS Code Web" },
{ value: "cursor", label: "Cursor" },
{ value: "zed", label: "Zed" },
{ value: "codium", label: "VSCodium" },
Expand Down
1 change: 1 addition & 0 deletions desktop/src/renderer/src/pages/WorkspaceDetailPage.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ const IDE_OPTIONS = [
{ value: "none", label: "None" },
{ value: "vscode", label: "VS Code" },
{ value: "openvscode", label: "VS Code Browser" },
{ value: "vscode-web", label: "VS Code Web" },
{ value: "code-server", label: "code-server" },
{ value: "cursor", label: "Cursor" },
{ value: "zed", label: "Zed" },
Expand Down
3 changes: 3 additions & 0 deletions e2e/tests/ide/ide.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ var _ = ginkgo.Describe("devsy ide test suite", ginkgo.Label("ide"), ginkgo.Orde
err = f.DevsyUpWithIDE(ctx, tempDir, "--ide-launch=skip", "--ide=jupyternotebook")
framework.ExpectNoError(err)

err = f.DevsyUpWithIDE(ctx, tempDir, "--ide-launch=skip", "--ide=vscode-web")
framework.ExpectNoError(err)

// TODO: Fix broken IDE
// err = f.DevsyUpWithIDE(ctx, tempDir, "--ide-launch=skip", "--ide=fleet")
// framework.ExpectNoError(err)
Expand Down
1 change: 1 addition & 0 deletions pkg/config/ide.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const (
IDEVSCodeInsiders IDE = "vscode-insiders"
IDEOpenVSCode IDE = "openvscode"
IDECodeServer IDE = "code-server"
IDEVSCodeWeb IDE = "vscode-web"
IDEIntellij IDE = "intellij"
IDEGoland IDE = "goland"
IDERustRover IDE = "rustrover"
Expand Down
2 changes: 1 addition & 1 deletion pkg/driver/kubernetes/pullsecrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ func (k *KubernetesDriver) createPullSecret(
dockerCredentials *dockercredentials.Credentials,
) error {
authToken := dockerCredentials.AuthToken()
email := "noreply@devsy.sh"
email := "dev@devsy.sh"

encodedSecretData, err := PreparePullSecretData(dockerCredentials.ServerURL, authToken, email)
if err != nil {
Expand Down
8 changes: 8 additions & 0 deletions pkg/ide/ideparse/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/devsy-org/devsy/pkg/ide/openvscode"
"github.com/devsy-org/devsy/pkg/ide/rstudio"
"github.com/devsy-org/devsy/pkg/ide/vscode"
"github.com/devsy-org/devsy/pkg/ide/vscodeweb"
"github.com/devsy-org/devsy/pkg/provider"
)

Expand Down Expand Up @@ -67,6 +68,13 @@ var AllowedIDEs = []AllowedIDE{
Icon: "https://raw.githubusercontent.com/coder/code-server/main/src/browser/media/favicon.svg",
Group: config.IDEGroupPrimary,
},
{
Name: config.IDEVSCodeWeb,
DisplayName: "VS Code Web",
Options: vscodeweb.Options,
Icon: config.WebsiteAssetsURL + "/vscode-web.svg",
Group: config.IDEGroupPrimary,
},
{
Name: config.IDECursor,
DisplayName: "Cursor",
Expand Down
4 changes: 4 additions & 0 deletions pkg/ide/opener/browser_tunnel_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ const LabelVSCodeBrowser = "vscode"
// (coder.com) browser-IDE tunnel.
const LabelCodeServer = "code-server"

// LabelVSCodeWeb is the TunnelState.Label value used for the official VS Code
// (code serve-web) browser-IDE tunnel.
const LabelVSCodeWeb = "vscode-web"

// TunnelState describes a running browser tunnel for a workspace.
//
// CreateTime is the helper process's creation timestamp in milliseconds
Expand Down
27 changes: 25 additions & 2 deletions pkg/ide/opener/opener.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"context"
"fmt"
"net"
"net/url"
"strconv"
"strings"

Expand All @@ -22,6 +23,7 @@ import (
"github.com/devsy-org/devsy/pkg/ide/openvscode"
"github.com/devsy-org/devsy/pkg/ide/rstudio"
"github.com/devsy-org/devsy/pkg/ide/vscode"
"github.com/devsy-org/devsy/pkg/ide/vscodeweb"
"github.com/devsy-org/devsy/pkg/ide/zed"
pkglog "github.com/devsy-org/devsy/pkg/log"
open2 "github.com/devsy-org/devsy/pkg/open"
Expand Down Expand Up @@ -97,6 +99,8 @@ func browserIDEOpener(
return openVSCodeBrowser, true
case string(config.IDECodeServer):
return openCodeServerBrowser, true
case string(config.IDEVSCodeWeb):
return openVSCodeWebBrowser, true
case string(config.IDEJupyterNotebook):
return openJupyterBrowser, true
case string(config.IDEMarimo):
Expand Down Expand Up @@ -417,7 +421,7 @@ func openVSCodeBrowser(
Label: LabelVSCodeBrowser,
LogName: "vscode",
TargetURLFn: func(port int, folder string) string {
return fmt.Sprintf("http://localhost:%d/?folder=%s", port, folder)
return fmt.Sprintf("http://localhost:%d/?folder=%s", port, url.QueryEscape(folder))
},
})
}
Expand All @@ -436,7 +440,26 @@ func openCodeServerBrowser(
Label: LabelCodeServer,
LogName: "code-server",
TargetURLFn: func(port int, folder string) string {
return fmt.Sprintf("http://localhost:%d/?folder=%s", port, folder)
return fmt.Sprintf("http://localhost:%d/?folder=%s", port, url.QueryEscape(folder))
},
})
}

func openVSCodeWebBrowser(
ctx context.Context,
ideOptions map[string]config.OptionValue,
params IDEParams,
) (string, error) {
return openBrowserIDE(ctx, params, browserIDESpec{
BindAddrOption: vscodeweb.Options.GetValue(ideOptions, vscodeweb.BindAddressOption),
DefaultPort: vscodeweb.DefaultVSCodeWebPort,
ForwardPorts: vscodeweb.Options.GetValue(
ideOptions, vscodeweb.ForwardPortsOption,
) == config.BoolTrue,
Comment on lines +453 to +458

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Use the parsed bind port for forwarded ports.

This path exposes BIND_ADDRESS, but the shared openBrowserIDE helper still populates ExtraPorts from spec.DefaultPort instead of the parsed port. A config like 0.0.0.0:12345 will therefore target 12345 in the browser URL while auto-forwarding still registers 10802. Please switch the shared helper to use the parsed port before routing more IDEs through it.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/ide/opener/opener.go` around lines 452 - 457, The shared browser IDE
helper is still using browserIDESpec.DefaultPort to populate ExtraPorts, so
forwarded ports can diverge from the parsed bind port exposed by BIND_ADDRESS.
Update openBrowserIDE to derive ExtraPorts from the parsed port value obtained
from the bind address, and ensure callers like the browser IDE path continue
passing through the parsed bind settings via browserIDESpec so auto-forwarding
matches the browser URL.

Label: LabelVSCodeWeb,
LogName: "vscode-web",
TargetURLFn: func(port int, folder string) string {
return fmt.Sprintf("http://localhost:%d/?folder=%s", port, url.QueryEscape(folder))
},
Comment thread
coderabbitai[bot] marked this conversation as resolved.
})
}
Expand Down
2 changes: 2 additions & 0 deletions pkg/ide/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ func ReusesAuthSock(ide string) bool {
return true
case string(config.IDECodeServer):
return true
case string(config.IDEVSCodeWeb):
return true
case string(config.IDEJupyterNotebook):
return true
case string(config.IDEMarimo):
Expand Down
Loading
Loading