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
59 changes: 59 additions & 0 deletions cmd/agent/container/codeserver_async.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package container

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/codeserver"
"github.com/devsy-org/devsy/pkg/log"
"github.com/spf13/cobra"
)

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

SetupInfo string
}

// NewCodeServerAsyncCmd creates a new command.
func NewCodeServerAsyncCmd() *cobra.Command {
cmd := &CodeServerAsyncCmd{}
codeServerAsyncCmd := &cobra.Command{
Use: "code-server-async",
Short: "Starts code-server",
Args: cobra.NoArgs,
RunE: cmd.Run,
}
codeServerAsyncCmd.Flags().
StringVar(&cmd.SetupInfo, "setup-info", "", "The container setup info")
_ = codeServerAsyncCmd.MarkFlagRequired("setup-info")
return codeServerAsyncCmd
}

// Run runs the command logic.
func (cmd *CodeServerAsyncCmd) 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 setupCodeServerExtensions(setupInfo)
}

func setupCodeServerExtensions(setupInfo *config.Result) error {
vsCodeConfiguration := config.GetVSCodeConfiguration(setupInfo.MergedConfig)
user := config.GetRemoteUser(setupInfo)
return codeserver.NewCodeServer(codeserver.ServerOptions{
Extensions: vsCodeConfiguration.Extensions,
UserName: user,
}).InstallExtensions()
}
1 change: 1 addition & 0 deletions cmd/agent/container/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ func NewContainerCmd(flags *flags.GlobalFlags) *cobra.Command {
containerCmd.AddCommand(NewDaemonCmd())
containerCmd.AddCommand(NewVSCodeAsyncCmd())
containerCmd.AddCommand(NewOpenVSCodeAsyncCmd())
containerCmd.AddCommand(NewCodeServerAsyncCmd())
containerCmd.AddCommand(NewCredentialsServerCmd(flags))
containerCmd.AddCommand(NewSetupLoftPlatformAccessCmd(flags))
containerCmd.AddCommand(NewSSHServerCmd(flags))
Expand Down
60 changes: 60 additions & 0 deletions cmd/agent/container/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
"github.com/devsy-org/devsy/pkg/dockercredentials"
"github.com/devsy-org/devsy/pkg/extract"
"github.com/devsy-org/devsy/pkg/git"
"github.com/devsy-org/devsy/pkg/ide/codeserver"
"github.com/devsy-org/devsy/pkg/ide/fleet"
"github.com/devsy-org/devsy/pkg/ide/jetbrains"
"github.com/devsy-org/devsy/pkg/ide/jupyter"
Expand Down Expand Up @@ -576,6 +577,8 @@ func (cmd *SetupContainerCmd) installIDE(
return cmd.setupVSCode(setupInfo, ide.Options, vscode.FlavorBob)
case string(config2.IDEOpenVSCode):
return cmd.setupOpenVSCode(setupInfo, ide.Options)
case string(config2.IDECodeServer):
return cmd.setupCodeServer(setupInfo, ide.Options)
case string(config2.IDEGoland):
return jetbrains.NewGolandServer(config.GetRemoteUser(setupInfo), ide.Options).
Install(setupInfo)
Expand Down Expand Up @@ -748,6 +751,63 @@ func (cmd *SetupContainerCmd) setupOpenVSCode(
return openVSCode.Start()
}

func (cmd *SetupContainerCmd) setupCodeServer(
setupInfo *config.Result,
ideOptions map[string]config2.OptionValue,
) error {
log.Debugf("setup code-server")
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)
cs := codeserver.NewCodeServer(codeserver.ServerOptions{
Extensions: vsCodeConfiguration.Extensions,
Settings: settings,
UserName: user,
Host: "0.0.0.0",
Port: strconv.Itoa(codeserver.DefaultCodeServerPort),
Values: ideOptions,
})

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

if len(vsCodeConfiguration.Extensions) > 0 {
err := command.StartBackgroundOnce("code-server-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,
"agent",
"container",
"code-server-async",
"--setup-info",
cmd.SetupInfo,
), nil
})
if err != nil {
return fmt.Errorf("install extensions: %w", err)
}
}

return cs.Start()
}

func configureSystemGitCredentials(
ctx context.Context,
client tunnel.TunnelClient,
Expand Down
3 changes: 3 additions & 0 deletions desktop/src/renderer/public/icons/ides/code-server.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions desktop/src/renderer/public/icons/ides/none.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions desktop/src/renderer/public/icons/ides/none_dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
32 changes: 32 additions & 0 deletions desktop/src/renderer/public/icons/ides/vscodebrowser.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 @@ -40,44 +40,47 @@ const STEPS: { id: Step; label: string }[] = [
{ id: "launch", label: "Launch" },
]

const ideIcon = (name: string) => `/icons/ides/${name}.svg`

const IDE_GROUPS = [
{
label: "Primary",
options: [
{ value: "none", label: "None" },
{ value: "vscode", label: "VS Code" },
{ value: "openvscode", label: "VS Code Browser" },
{ value: "cursor", label: "Cursor" },
{ value: "zed", label: "Zed" },
{ value: "codium", label: "VSCodium" },
{ value: "windsurf", label: "Windsurf Editor" },
{ value: "antigravity", label: "Google Antigravity" },
{ value: "bob", label: "IBM Bob" },
{ value: "none", label: "None", icon: ideIcon("none") },
{ value: "vscode", label: "VS Code", icon: ideIcon("vscode") },
{ value: "openvscode", label: "VS Code Browser", icon: ideIcon("vscodebrowser") },
{ value: "code-server", label: "code-server", icon: ideIcon("code-server") },
{ value: "cursor", label: "Cursor", icon: ideIcon("cursor") },
{ value: "zed", label: "Zed", icon: ideIcon("zed") },
{ value: "codium", label: "VSCodium", icon: ideIcon("codium") },
{ value: "windsurf", label: "Windsurf Editor", icon: ideIcon("windsurf") },
{ value: "antigravity", label: "Google Antigravity", icon: ideIcon("antigravity") },
{ value: "bob", label: "IBM Bob", icon: ideIcon("bob") },
],
},
{
label: "JetBrains",
options: [
{ value: "intellij", label: "IntelliJ IDEA" },
{ value: "pycharm", label: "PyCharm" },
{ value: "phpstorm", label: "PhpStorm" },
{ value: "rider", label: "Rider" },
{ value: "fleet", label: "Fleet" },
{ value: "goland", label: "GoLand" },
{ value: "webstorm", label: "WebStorm" },
{ value: "rustrover", label: "RustRover" },
{ value: "rubymine", label: "RubyMine" },
{ value: "clion", label: "CLion" },
{ value: "dataspell", label: "DataSpell" },
{ value: "intellij", label: "IntelliJ IDEA", icon: ideIcon("intellij") },
{ value: "pycharm", label: "PyCharm", icon: ideIcon("pycharm") },
{ value: "phpstorm", label: "PhpStorm", icon: ideIcon("phpstorm") },
{ value: "rider", label: "Rider", icon: ideIcon("rider") },
{ value: "fleet", label: "Fleet", icon: ideIcon("fleet") },
{ value: "goland", label: "GoLand", icon: ideIcon("goland") },
{ value: "webstorm", label: "WebStorm", icon: ideIcon("webstorm") },
{ value: "rustrover", label: "RustRover", icon: ideIcon("rustrover") },
{ value: "rubymine", label: "RubyMine", icon: ideIcon("rubymine") },
{ value: "clion", label: "CLion", icon: ideIcon("clion") },
{ value: "dataspell", label: "DataSpell", icon: ideIcon("dataspell") },
],
},
{
label: "Other",
options: [
{ value: "jupyternotebook", label: "Jupyter Notebook" },
{ value: "vscode-insiders", label: "VS Code Insiders" },
{ value: "positron", label: "Positron" },
{ value: "rstudio", label: "RStudio Server" },
{ value: "jupyternotebook", label: "Jupyter Notebook", icon: ideIcon("jupyter") },
{ value: "vscode-insiders", label: "VS Code Insiders", icon: ideIcon("vscode_insiders") },
{ value: "positron", label: "Positron", icon: ideIcon("positron") },
{ value: "rstudio", label: "RStudio Server", icon: ideIcon("rstudio") },
],
},
]
Expand Down Expand Up @@ -136,9 +139,9 @@ let initializedProviders = $derived(
$providers.filter((p) => p.state?.initialized === true),
)

const ideLabel = $derived(
ALL_IDES.find((i) => i.value === selectedIde)?.label ?? "Select an IDE...",
)
const selectedIdeEntry = $derived(ALL_IDES.find((i) => i.value === selectedIde))
const ideLabel = $derived(selectedIdeEntry?.label ?? "Select an IDE...")
const ideIconSrc = $derived(selectedIdeEntry?.icon)

let filteredIdes = $derived(
ideSearch
Expand Down Expand Up @@ -540,7 +543,12 @@ function selectTemplate(t: { name: string; source: string }) {
<Popover.Trigger class="w-full">
{#snippet child({ props })}
<Button variant="outline" class="h-9 w-full justify-between" {...props}>
<span class="flex-1 truncate text-left">{ideLabel}</span>
<span class="flex items-center gap-2 flex-1 truncate text-left">
{#if ideIconSrc}
<img src={ideIconSrc} alt="" class="h-4 w-4 shrink-0" />
{/if}
<span class="truncate">{ideLabel}</span>
</span>
<ChevronsUpDown class="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
{/snippet}
Expand All @@ -558,6 +566,7 @@ function selectTemplate(t: { name: string; source: string }) {
onSelect={() => { selectedIde = ide.value; ideComboOpen = false; ideSearch = "" }}
>
<Check class="mr-2 h-4 w-4 {selectedIde === ide.value ? 'opacity-100' : 'opacity-0'}" />
<img src={ide.icon} alt="" class="mr-2 h-4 w-4 shrink-0" />
{ide.label}
</Command.Item>
{/each}
Expand Down Expand Up @@ -628,7 +637,12 @@ function selectTemplate(t: { name: string; source: string }) {
{/if}
<div class="flex justify-between gap-3">
<span class="text-muted-foreground">IDE</span>
<span class="font-medium truncate">{ideLabel}</span>
<span class="flex items-center gap-2 font-medium truncate">
{#if ideIconSrc}
<img src={ideIconSrc} alt="" class="h-4 w-4 shrink-0" />
{/if}
<span class="truncate">{ideLabel}</span>
</span>
</div>
<div class="flex justify-between gap-3">
<span class="text-muted-foreground">Workspace ID</span>
Expand Down
1 change: 1 addition & 0 deletions pkg/config/ide.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const (
IDEVSCode IDE = "vscode"
IDEVSCodeInsiders IDE = "vscode-insiders"
IDEOpenVSCode IDE = "openvscode"
IDECodeServer IDE = "code-server"
IDEIntellij IDE = "intellij"
IDEGoland IDE = "goland"
IDERustRover IDE = "rustrover"
Expand Down
Loading
Loading