From 2f5154bed0af979216f8d73ebb2837682ae370ce Mon Sep 17 00:00:00 2001 From: Samuel K Date: Mon, 3 Aug 2026 18:16:58 +0000 Subject: [PATCH 1/2] fix(lint): reorder exported/unexported methods and constructors (funcorder) funcorder's constructor and struct-method checks are on by default once the linter is enabled -- no separate settings toggle exists. Fixes all 26 pre-existing violations: unexported methods placed before an exported method on the same struct, and constructors placed after their struct declaration. Pure reordering, no logic changes. --- cmd/pro/provider/watch/workspaces.go | 8 +- pkg/agent/tunnelserver/logger.go | 28 +-- pkg/apple/helper.go | 18 +- .../clientimplementation/proxy_client.go | 114 ++++----- pkg/daemon/platform/workspace_watcher.go | 8 +- pkg/dockerfile/parse.go | 224 +++++++++--------- pkg/driver/kubernetes/driver.go | 58 ++--- pkg/ide/fleet/fleet.go | 10 +- pkg/ide/jetbrains/generic.go | 8 +- pkg/ide/openvscode/openvscode.go | 96 ++++---- pkg/ide/rstudio/rstudio.go | 12 +- pkg/platform/client/client.go | 60 ++--- pkg/platform/remotecommand/stream.go | 14 +- pkg/platform/remotecommand/websocket.go | 64 ++--- pkg/stdio/listener.go | 10 +- pkg/types/time.go | 10 +- 16 files changed, 371 insertions(+), 371 deletions(-) diff --git a/cmd/pro/provider/watch/workspaces.go b/cmd/pro/provider/watch/workspaces.go index f97185f73..28fa3d864 100644 --- a/cmd/pro/provider/watch/workspaces.go +++ b/cmd/pro/provider/watch/workspaces.go @@ -193,10 +193,6 @@ func newStore( } } -func (s *instanceStore) key(meta metav1.ObjectMeta) string { - return fmt.Sprintf("%s/%s", meta.Namespace, meta.Name) -} - func (s *instanceStore) Add(instance *managementv1.DevsyWorkspaceInstance) { if s.filterByOwner && !platform.IsOwner(s.self, instance.Spec.Owner) { return @@ -253,6 +249,10 @@ func (s *instanceStore) List() []*ProWorkspaceInstance { return instanceList } +func (s *instanceStore) key(meta metav1.ObjectMeta) string { + return fmt.Sprintf("%s/%s", meta.Namespace, meta.Name) +} + func (s *instanceStore) buildProInstance( instance *managementv1.DevsyWorkspaceInstance, ) *ProWorkspaceInstance { diff --git a/pkg/agent/tunnelserver/logger.go b/pkg/agent/tunnelserver/logger.go index 8b9bd0933..8fd087982 100644 --- a/pkg/agent/tunnelserver/logger.go +++ b/pkg/agent/tunnelserver/logger.go @@ -51,20 +51,6 @@ type tunnelLogger struct { logChan chan *tunnel.LogMessage } -func (s *tunnelLogger) worker() { - for { - select { - case msg := <-s.logChan: - ctx, cancel := context.WithTimeout(s.ctx, 5*time.Second) - _, _ = s.client.Log(ctx, msg) - // ignore error since we can't use the logger itself - cancel() - case <-s.ctx.Done(): - return - } - } -} - func (s *tunnelLogger) Debugf(format string, args ...any) { if s.level < levelDebug { return @@ -108,3 +94,17 @@ func (s *tunnelLogger) Warnf(format string, args ...any) { Message: fmt.Sprintf(format, args...) + "\n", } } + +func (s *tunnelLogger) worker() { + for { + select { + case msg := <-s.logChan: + ctx, cancel := context.WithTimeout(s.ctx, 5*time.Second) + _, _ = s.client.Log(ctx, msg) + // ignore error since we can't use the logger itself + cancel() + case <-s.ctx.Done(): + return + } + } +} diff --git a/pkg/apple/helper.go b/pkg/apple/helper.go index 2ede04ab7..55cebd0f6 100644 --- a/pkg/apple/helper.go +++ b/pkg/apple/helper.go @@ -349,15 +349,6 @@ func (h *AppleHelper) EnsureBuilderRunning(ctx context.Context) error { return fmt.Errorf("start container builder: %s: %w", strings.TrimSpace(string(out)), err) } -func (h *AppleHelper) buildCmd(ctx context.Context, args ...string) *exec.Cmd { - //nolint:gosec // G204: operator-configured binary, internally-built args (as in pkg/docker) - cmd := exec.CommandContext(ctx, h.Command, args...) - if h.Environment != nil { - cmd.Env = append(os.Environ(), h.Environment...) - } - return cmd -} - // SystemRunning reports whether the container system service is running. func (h *AppleHelper) SystemRunning(ctx context.Context) bool { cctx, cancel := context.WithTimeout(ctx, 5*time.Second) @@ -369,6 +360,15 @@ func (h *AppleHelper) SystemRunning(ctx context.Context) bool { return strings.Contains(strings.ToLower(string(out)), stateRunning) } +func (h *AppleHelper) buildCmd(ctx context.Context, args ...string) *exec.Cmd { + //nolint:gosec // G204: operator-configured binary, internally-built args (as in pkg/docker) + cmd := exec.CommandContext(ctx, h.Command, args...) + if h.Environment != nil { + cmd.Env = append(os.Environ(), h.Environment...) + } + return cmd +} + func (h *AppleHelper) listContainers(ctx context.Context) ([]containerInspect, error) { out, err := h.buildCmd(ctx, "list", "--all", "--format", "json").Output() if err != nil { diff --git a/pkg/client/clientimplementation/proxy_client.go b/pkg/client/clientimplementation/proxy_client.go index 49c316730..1c054d048 100644 --- a/pkg/client/clientimplementation/proxy_client.go +++ b/pkg/client/clientimplementation/proxy_client.go @@ -159,28 +159,6 @@ func tryLock(ctx context.Context, lock *flock.Flock, name string) error { ) } -func (s *proxyClient) initLock() { - s.workspaceLockOnce.Do(func() { - s.m.Lock() - defer s.m.Unlock() - - // get locks dir - workspaceLocksDir, err := provider.GetLocksDir(s.workspace.Context) - if err != nil { - panic(fmt.Errorf("get workspaces dir: %w", err)) - } - // #nosec G301 -- TODO Consider using a more secure permission setting and ownership if needed. - if err = os.MkdirAll(workspaceLocksDir, 0o755); err != nil { - panic(fmt.Errorf("create workspace locks dir: %w", err)) - } - - // create workspace lock - s.workspaceLock = flock.New( - filepath.Join(workspaceLocksDir, s.workspace.ID+".workspace.lock"), - ) - }) -} - func (s *proxyClient) Provider() string { return s.config.Name } @@ -295,41 +273,6 @@ func (s *proxyClient) Up(ctx context.Context, opt client.UpOptions) error { }) } -// checkPlatformVersion validates the platform provider version compatibility. -func (s *proxyClient) checkPlatformVersion( - ctx context.Context, - providerOptions map[string]config.OptionValue, -) error { - devsyConfigPath := providerOptions["DEVSY_CONFIG"].Value - if devsyConfigPath == "" { - return nil - } - - baseClient, err := platformclient.InitClientFromPath(ctx, devsyConfigPath) - if err != nil { - return fmt.Errorf("error initializing platform client: %w", err) - } - - version, err := baseClient.Version() - if err != nil { - return fmt.Errorf("error retrieving platform version: %w", err) - } - - parsedVersion, err := semver.Parse(strings.TrimPrefix(version.DevsyVersion, "v")) - if err != nil { - return fmt.Errorf("error parsing platform version: %w", err) - } - - if parsedVersion.GE(semver.MustParse("0.6.99")) { - return fmt.Errorf( - "you are using an outdated provider version for this platform. " + - "Disconnect and reconnect the platform to update the provider", - ) - } - - return nil -} - func (s *proxyClient) Delete(ctx context.Context, opt client.DeleteOptions) error { s.m.Lock() defer s.m.Unlock() @@ -407,6 +350,63 @@ func (s *proxyClient) Status( return client.ParseStatus(status.State) } +func (s *proxyClient) initLock() { + s.workspaceLockOnce.Do(func() { + s.m.Lock() + defer s.m.Unlock() + + // get locks dir + workspaceLocksDir, err := provider.GetLocksDir(s.workspace.Context) + if err != nil { + panic(fmt.Errorf("get workspaces dir: %w", err)) + } + // #nosec G301 -- TODO Consider using a more secure permission setting and ownership if needed. + if err = os.MkdirAll(workspaceLocksDir, 0o755); err != nil { + panic(fmt.Errorf("create workspace locks dir: %w", err)) + } + + // create workspace lock + s.workspaceLock = flock.New( + filepath.Join(workspaceLocksDir, s.workspace.ID+".workspace.lock"), + ) + }) +} + +// checkPlatformVersion validates the platform provider version compatibility. +func (s *proxyClient) checkPlatformVersion( + ctx context.Context, + providerOptions map[string]config.OptionValue, +) error { + devsyConfigPath := providerOptions["DEVSY_CONFIG"].Value + if devsyConfigPath == "" { + return nil + } + + baseClient, err := platformclient.InitClientFromPath(ctx, devsyConfigPath) + if err != nil { + return fmt.Errorf("error initializing platform client: %w", err) + } + + version, err := baseClient.Version() + if err != nil { + return fmt.Errorf("error retrieving platform version: %w", err) + } + + parsedVersion, err := semver.Parse(strings.TrimPrefix(version.DevsyVersion, "v")) + if err != nil { + return fmt.Errorf("error parsing platform version: %w", err) + } + + if parsedVersion.GE(semver.MustParse("0.6.99")) { + return fmt.Errorf( + "you are using an outdated provider version for this platform. " + + "Disconnect and reconnect the platform to update the provider", + ) + } + + return nil +} + func (s *proxyClient) updateInstance(ctx context.Context) error { if !terminal.IsTerminalIn { return fmt.Errorf("unable to update instance through CLI if stdin is not a terminal") diff --git a/pkg/daemon/platform/workspace_watcher.go b/pkg/daemon/platform/workspace_watcher.go index ebdd39f99..80104d613 100644 --- a/pkg/daemon/platform/workspace_watcher.go +++ b/pkg/daemon/platform/workspace_watcher.go @@ -215,10 +215,6 @@ func newStore( } } -func (s *instanceStore) key(namespace, name string) string { - return fmt.Sprintf("%s/%s", namespace, name) -} - func (s *instanceStore) Add(instance *managementv1.DevsyWorkspaceInstance) { if s.ownerFilter == platform.SelfOwnerFilter && !platform.IsOwner(s.self, instance.GetOwner()) { return @@ -295,6 +291,10 @@ func (s *instanceStore) List() []*ProWorkspaceInstance { return instanceList } +func (s *instanceStore) key(namespace, name string) string { + return fmt.Sprintf("%s/%s", namespace, name) +} + func (s *instanceStore) convert(instance *ProWorkspaceInstance) *ProWorkspaceInstance { if instance == nil { return nil diff --git a/pkg/dockerfile/parse.go b/pkg/dockerfile/parse.go index 298a69ffb..2553a7054 100644 --- a/pkg/dockerfile/parse.go +++ b/pkg/dockerfile/parse.go @@ -102,22 +102,6 @@ func (d *Dockerfile) BuildContextFiles() []string { var defaultShellLexer = shell.NewLex('\\') -func (d *Dockerfile) expandVariables( - val string, - buildArgs, baseImageEnv map[string]string, - stage *BaseStage, - _ int, -) string { - result, _, err := defaultShellLexer.ProcessWord( - val, - &environmentResolver{d, buildArgs, baseImageEnv, stage, 0}, - ) - if err != nil { - return val - } - return result -} - type environmentResolver struct { dockerfile *Dockerfile buildArgs map[string]string @@ -142,6 +126,118 @@ func (e *environmentResolver) Keys() []string { return keys } +func RemoveSyntaxVersion(dockerfileContent string) string { + return syntaxDirectiveRegex.ReplaceAllString(dockerfileContent, "") +} + +func EnsureFinalStageName(dockerfileContent, defaultLastStageName string) (string, string, error) { + result, err := parser.Parse(strings.NewReader(dockerfileContent)) + if err != nil { + return "", "", err + } + + lastChild := lastFromNode(result.AST.Children) + if lastChild == nil { + return "", "", fmt.Errorf("no FROM statement in dockerfile") + } + if lastChild.Next == nil { + return "", "", fmt.Errorf("cannot parse FROM statement in dockerfile") + } + + if hasStageAlias(lastChild) { + return lastChild.Next.Next.Next.Value, "", nil + } + + lastChild.Next.Next = &parser.Node{ + Value: "AS", + Next: &parser.Node{Value: defaultLastStageName}, + } + return defaultLastStageName, ReplaceInDockerfile(dockerfileContent, lastChild), nil +} + +func lastFromNode(children []*parser.Node) *parser.Node { + var lastChild *parser.Node + for _, child := range children { + if strings.ToLower(child.Value) == command.From { + lastChild = child + } + } + return lastChild +} + +func hasStageAlias(node *parser.Node) bool { + return node.Next.Next != nil && node.Next.Next.Next != nil && + strings.EqualFold(node.Next.Next.Value, "as") +} + +func ReplaceInDockerfile(dockerfileContent string, node *parser.Node) string { + scan := scanner.NewScanner(strings.NewReader(dockerfileContent)) + var lines []string + for lineNumber := 1; scan.Scan(); lineNumber++ { + if lineNumber >= node.StartLine && lineNumber <= node.EndLine { + lines = append(lines, FormatNode(node)) + } else { + lines = append(lines, scan.Text()) + } + } + return strings.Join(lines, "\n") +} + +type Dockerfile struct { + Raw string + + Directives []*parser.Directive + Preamble *Preamble + Syntax string // https://docs.docker.com/build/concepts/dockerfile/#dockerfile-syntax + + Stages []*Stage + StagesByTarget map[string]*Stage +} + +type Preamble struct { + BaseStage +} + +type Stage struct { + BaseStage + Users []instructions.KeyValuePair +} + +type BaseStage struct { + Image string + Target string + + Envs []instructions.KeyValuePair + Args []instructions.KeyValuePairOptional + Instructions []*parser.Node +} + +func (d *Dockerfile) Dump() string { + result := make([]string, 0, len(d.Stages)) + for _, stage := range d.Stages { + if dump := FormatNodes(stage.Instructions); dump != "" { + result = append(result, dump) + } + } + return strings.Join(result, "\n") +} + +func (d *Dockerfile) expandVariables( + val string, + buildArgs, baseImageEnv map[string]string, + stage *BaseStage, + _ int, +) string { + result, _, err := defaultShellLexer.ProcessWord( + val, + &environmentResolver{d, buildArgs, baseImageEnv, stage, 0}, + ) + if err != nil { + return val + } + return result +} + func (d *Dockerfile) resolveVariable( buildArgs, baseImageEnv map[string]string, variable string, @@ -249,102 +345,6 @@ func (d *Dockerfile) getParentStage( return &d.Preamble.BaseStage } -func RemoveSyntaxVersion(dockerfileContent string) string { - return syntaxDirectiveRegex.ReplaceAllString(dockerfileContent, "") -} - -func EnsureFinalStageName(dockerfileContent, defaultLastStageName string) (string, string, error) { - result, err := parser.Parse(strings.NewReader(dockerfileContent)) - if err != nil { - return "", "", err - } - - lastChild := lastFromNode(result.AST.Children) - if lastChild == nil { - return "", "", fmt.Errorf("no FROM statement in dockerfile") - } - if lastChild.Next == nil { - return "", "", fmt.Errorf("cannot parse FROM statement in dockerfile") - } - - if hasStageAlias(lastChild) { - return lastChild.Next.Next.Next.Value, "", nil - } - - lastChild.Next.Next = &parser.Node{ - Value: "AS", - Next: &parser.Node{Value: defaultLastStageName}, - } - return defaultLastStageName, ReplaceInDockerfile(dockerfileContent, lastChild), nil -} - -func lastFromNode(children []*parser.Node) *parser.Node { - var lastChild *parser.Node - for _, child := range children { - if strings.ToLower(child.Value) == command.From { - lastChild = child - } - } - return lastChild -} - -func hasStageAlias(node *parser.Node) bool { - return node.Next.Next != nil && node.Next.Next.Next != nil && - strings.EqualFold(node.Next.Next.Value, "as") -} - -func ReplaceInDockerfile(dockerfileContent string, node *parser.Node) string { - scan := scanner.NewScanner(strings.NewReader(dockerfileContent)) - var lines []string - for lineNumber := 1; scan.Scan(); lineNumber++ { - if lineNumber >= node.StartLine && lineNumber <= node.EndLine { - lines = append(lines, FormatNode(node)) - } else { - lines = append(lines, scan.Text()) - } - } - return strings.Join(lines, "\n") -} - -type Dockerfile struct { - Raw string - - Directives []*parser.Directive - Preamble *Preamble - Syntax string // https://docs.docker.com/build/concepts/dockerfile/#dockerfile-syntax - - Stages []*Stage - StagesByTarget map[string]*Stage -} - -type Preamble struct { - BaseStage -} - -type Stage struct { - BaseStage - Users []instructions.KeyValuePair -} - -type BaseStage struct { - Image string - Target string - - Envs []instructions.KeyValuePair - Args []instructions.KeyValuePairOptional - Instructions []*parser.Node -} - -func (d *Dockerfile) Dump() string { - result := make([]string, 0, len(d.Stages)) - for _, stage := range d.Stages { - if dump := FormatNodes(stage.Instructions); dump != "" { - result = append(result, dump) - } - } - return strings.Join(result, "\n") -} - func Parse(dockerfileContent string) (*Dockerfile, error) { result, err := parser.Parse(strings.NewReader(dockerfileContent)) if err != nil { diff --git a/pkg/driver/kubernetes/driver.go b/pkg/driver/kubernetes/driver.go index 67432a195..8bb6d8de2 100644 --- a/pkg/driver/kubernetes/driver.go +++ b/pkg/driver/kubernetes/driver.go @@ -86,35 +86,6 @@ func (k *KubernetesDriver) SupportsMountType(mountType string) bool { } } -func (k *KubernetesDriver) getDevContainerPvc( - ctx context.Context, - id string, -) (*corev1.PersistentVolumeClaim, *DevContainerInfo, error) { - // try to find pvc - pvc, err := k.client.Client(). - CoreV1(). - PersistentVolumeClaims(k.namespace). - Get(ctx, id, metav1.GetOptions{}) - if err != nil { - if kerrors.IsNotFound(err) { - return nil, nil, nil - } - - return nil, nil, err - } else if pvc.Annotations == nil || pvc.Annotations[DevsyInfoAnnotation] == "" { - return nil, nil, fmt.Errorf("pvc is missing dev container info annotation") - } - - // get container info - containerInfo := &DevContainerInfo{} - err = json.Unmarshal([]byte(pvc.GetAnnotations()[DevsyInfoAnnotation]), containerInfo) - if err != nil { - return nil, nil, fmt.Errorf("decode dev container info: %w", err) - } - - return pvc, containerInfo, nil -} - func (k *KubernetesDriver) StopDevContainer(ctx context.Context, workspaceId string) error { log.Debugf("Stopping devcontainer for workspace %q", workspaceId) defer log.Debugf("Done stopping devcontainer for workspace %q", workspaceId) @@ -219,6 +190,35 @@ func (k *KubernetesDriver) GetDevContainerLogs( return nil } +func (k *KubernetesDriver) getDevContainerPvc( + ctx context.Context, + id string, +) (*corev1.PersistentVolumeClaim, *DevContainerInfo, error) { + // try to find pvc + pvc, err := k.client.Client(). + CoreV1(). + PersistentVolumeClaims(k.namespace). + Get(ctx, id, metav1.GetOptions{}) + if err != nil { + if kerrors.IsNotFound(err) { + return nil, nil, nil + } + + return nil, nil, err + } else if pvc.Annotations == nil || pvc.Annotations[DevsyInfoAnnotation] == "" { + return nil, nil, fmt.Errorf("pvc is missing dev container info annotation") + } + + // get container info + containerInfo := &DevContainerInfo{} + err = json.Unmarshal([]byte(pvc.GetAnnotations()[DevsyInfoAnnotation]), containerInfo) + if err != nil { + return nil, nil, fmt.Errorf("decode dev container info: %w", err) + } + + return pvc, containerInfo, nil +} + func (k *KubernetesDriver) deletePersistentVolumeClaim( ctx context.Context, workspaceId string, diff --git a/pkg/ide/fleet/fleet.go b/pkg/ide/fleet/fleet.go index 30dc34709..87edf4998 100644 --- a/pkg/ide/fleet/fleet.go +++ b/pkg/ide/fleet/fleet.go @@ -49,6 +49,11 @@ var Options = ide.Options{ }, } +type FleetServer struct { + values map[string]config.OptionValue + userName string +} + func NewFleetServer( userName string, values map[string]config.OptionValue, @@ -59,11 +64,6 @@ func NewFleetServer( } } -type FleetServer struct { - values map[string]config.OptionValue - userName string -} - func (o *FleetServer) Install(projectDir string) error { location, err := prepareFleetServerLocation(o.userName) if err != nil { diff --git a/pkg/ide/jetbrains/generic.go b/pkg/ide/jetbrains/generic.go index 5be88d926..3912137f8 100644 --- a/pkg/ide/jetbrains/generic.go +++ b/pkg/ide/jetbrains/generic.go @@ -108,10 +108,6 @@ func (o *GenericJetBrainsServer) GetVolume() string { return fmt.Sprintf("type=volume,src=devsy-%s,dst=%s", o.options.ID, o.getDownloadFolder()) } -func (o *GenericJetBrainsServer) getDownloadFolder() string { - return filepath.Join(config2.ContainerDataDir, o.options.ID) -} - func (o *GenericJetBrainsServer) Install(setupInfo *config.Result) error { log.Infof("setup backend: displayName=%s, id=%s", o.options.DisplayName, o.options.ID) baseFolder, err := getBaseFolder(o.userName) @@ -151,6 +147,10 @@ func (o *GenericJetBrainsServer) Install(setupInfo *config.Result) error { return o.installPlugins(setupInfo, targetLocation, baseFolder) } +func (o *GenericJetBrainsServer) getDownloadFolder() string { + return filepath.Join(config2.ContainerDataDir, o.options.ID) +} + func (o *GenericJetBrainsServer) installPlugins( setupInfo *config.Result, targetLocation, baseFolder string, diff --git a/pkg/ide/openvscode/openvscode.go b/pkg/ide/openvscode/openvscode.go index d087be096..9cd4561e3 100644 --- a/pkg/ide/openvscode/openvscode.go +++ b/pkg/ide/openvscode/openvscode.go @@ -64,6 +64,15 @@ var Options = ide.Options{ const DefaultVSCodePort = 10800 +type OpenVSCodeServer struct { + values map[string]config.OptionValue + extensions []string + settings string + userName string + host string + port string +} + func NewOpenVSCodeServer( extensions []string, settings string, @@ -81,15 +90,6 @@ func NewOpenVSCodeServer( } } -type OpenVSCodeServer struct { - values map[string]config.OptionValue - extensions []string - settings string - userName string - host string - port string -} - func (o *OpenVSCodeServer) InstallExtensions() error { // install extensions err := o.installExtensions() @@ -144,6 +144,45 @@ func chownIfNeeded(userName, location string) error { return nil } +func (o *OpenVSCodeServer) Start() error { + location, err := prepareOpenVSCodeServerLocation(o.userName) + if err != nil { + return err + } + + if o.host == "" { + o.host = "0.0.0.0" + } + if o.port == "" { + o.port = strconv.Itoa(DefaultVSCodePort) + } + + binaryPath := filepath.Join(location, "bin", "openvscode-server") + _, err = os.Stat(binaryPath) + if err != nil { + return fmt.Errorf("find binary: %w", err) + } + + return command.StartBackgroundOnce("openvscode", func() (*exec.Cmd, error) { + log.Infof("Starting openvscode in background") + runCommand := fmt.Sprintf( + "%s server-local --without-connection-token --host '%s' --port '%s'", + binaryPath, + o.host, + o.port, + ) + args := []string{} + if o.userName != "" { + args = append(args, "su", o.userName, "-c", runCommand) + } else { + args = append(args, "sh", "-c", runCommand) + } + cmd := exec.Command(args[0], args[1:]...) + cmd.Dir = location + return cmd, nil + }) +} + func (o *OpenVSCodeServer) getReleaseUrl() string { var url string version := Options.GetValue(o.values, VersionOption) @@ -230,45 +269,6 @@ func (o *OpenVSCodeServer) installSettings() error { return nil } -func (o *OpenVSCodeServer) Start() error { - location, err := prepareOpenVSCodeServerLocation(o.userName) - if err != nil { - return err - } - - if o.host == "" { - o.host = "0.0.0.0" - } - if o.port == "" { - o.port = strconv.Itoa(DefaultVSCodePort) - } - - binaryPath := filepath.Join(location, "bin", "openvscode-server") - _, err = os.Stat(binaryPath) - if err != nil { - return fmt.Errorf("find binary: %w", err) - } - - return command.StartBackgroundOnce("openvscode", func() (*exec.Cmd, error) { - log.Infof("Starting openvscode in background") - runCommand := fmt.Sprintf( - "%s server-local --without-connection-token --host '%s' --port '%s'", - binaryPath, - o.host, - o.port, - ) - args := []string{} - if o.userName != "" { - args = append(args, "su", o.userName, "-c", runCommand) - } else { - args = append(args, "sh", "-c", runCommand) - } - cmd := exec.Command(args[0], args[1:]...) - cmd.Dir = location - return cmd, nil - }) -} - func prepareOpenVSCodeServerLocation(userName string) (string, error) { var err error homeFolder := "" diff --git a/pkg/ide/rstudio/rstudio.go b/pkg/ide/rstudio/rstudio.go index 3cb5b5085..4506f2ef2 100644 --- a/pkg/ide/rstudio/rstudio.go +++ b/pkg/ide/rstudio/rstudio.go @@ -50,6 +50,12 @@ type preferences struct { InitialWorkingDirectory string `json:"initial_working_directory,omitempty"` // RStudio expects snake_case } +type RStudioServer struct { + values map[string]config.OptionValue + workspaceFolder string + userName string +} + func NewRStudioServer( workspaceFolder string, userName string, @@ -62,12 +68,6 @@ func NewRStudioServer( } } -type RStudioServer struct { - values map[string]config.OptionValue - workspaceFolder string - userName string -} - var codenameRegEx = regexp.MustCompile(`\nUBUNTU_CODENAME=(.*)\n`) func (o *RStudioServer) Install() error { diff --git a/pkg/platform/client/client.go b/pkg/platform/client/client.go index 2759cb585..44fa3fb1b 100644 --- a/pkg/platform/client/client.go +++ b/pkg/platform/client/client.go @@ -180,36 +180,6 @@ func (c *client) Logout(ctx context.Context) error { return nil } -func (c *client) initConfig() error { - var retErr error - c.configOnce.Do(func() { - // load the config or create new one if not found - content, err := os.ReadFile(c.configPath) - if err != nil { - if os.IsNotExist(err) { - c.config = NewConfig() - return - } - - retErr = err - return - } - - config := &Config{ - VirtualClusterAccessPointCertificates: make(map[string]VirtualClusterCertificatesEntry), - } - err = json.Unmarshal(content, config) - if err != nil { - retErr = err - return - } - - c.config = config - }) - - return retErr -} - func (c *client) Save() error { if c.configPath == "" { return nil @@ -381,6 +351,36 @@ func (c *client) LoginWithAccessKey(host, accessKey string, insecure bool, force return c.Save() } +func (c *client) initConfig() error { + var retErr error + c.configOnce.Do(func() { + // load the config or create new one if not found + content, err := os.ReadFile(c.configPath) + if err != nil { + if os.IsNotExist(err) { + c.config = NewConfig() + return + } + + retErr = err + return + } + + config := &Config{ + VirtualClusterAccessPointCertificates: make(map[string]VirtualClusterCertificatesEntry), + } + err = json.Unmarshal(content, config) + if err != nil { + retErr = err + return + } + + c.config = config + }) + + return retErr +} + func (c *client) deleteOldAccessKey() { managementClient, err := c.Management() if err != nil { diff --git a/pkg/platform/remotecommand/stream.go b/pkg/platform/remotecommand/stream.go index 9b6c97820..39e83d8e2 100644 --- a/pkg/platform/remotecommand/stream.go +++ b/pkg/platform/remotecommand/stream.go @@ -10,6 +10,13 @@ import ( "k8s.io/klog/v2" ) +type Stream struct { + ws *WebsocketConn + + dataType MessageType + closeType MessageType +} + func NewStream(ws *WebsocketConn, dataType, closeType MessageType) *Stream { return &Stream{ ws: ws, @@ -18,13 +25,6 @@ func NewStream(ws *WebsocketConn, dataType, closeType MessageType) *Stream { } } -type Stream struct { - ws *WebsocketConn - - dataType MessageType - closeType MessageType -} - func (s *Stream) Write(ctx context.Context, writer io.WriteCloser) error { if writer == nil { return nil diff --git a/pkg/platform/remotecommand/websocket.go b/pkg/platform/remotecommand/websocket.go index 0f53eace6..377080f8d 100644 --- a/pkg/platform/remotecommand/websocket.go +++ b/pkg/platform/remotecommand/websocket.go @@ -14,14 +14,6 @@ const ( PingWaitDuration = 60 * time.Second ) -func NewWebsocketConn(ws *websocket.Conn) *WebsocketConn { - conn := &WebsocketConn{ - ws: ws, - } - conn.setupDeadline() - return conn -} - type WebsocketConn struct { m sync.Mutex @@ -31,30 +23,12 @@ type WebsocketConn struct { closeError error } -func (w *WebsocketConn) setupDeadline() { - _ = w.ws.SetReadDeadline(time.Now().Add(PingWaitDuration)) - w.ws.SetPingHandler(func(string) error { - w.m.Lock() - err := w.ws.WriteControl( - websocket.PongMessage, - []byte(""), - time.Now().Add(PingWaitDuration), - ) - w.m.Unlock() - if err != nil { - return err - } - if err := w.ws.SetReadDeadline(time.Now().Add(PingWaitDuration)); err != nil { - return err - } - return w.ws.SetWriteDeadline(time.Now().Add(PingWaitDuration)) - }) - w.ws.SetPongHandler(func(string) error { - if err := w.ws.SetReadDeadline(time.Now().Add(PingWaitDuration)); err != nil { - return err - } - return w.ws.SetWriteDeadline(time.Now().Add(PingWaitDuration)) - }) +func NewWebsocketConn(ws *websocket.Conn) *WebsocketConn { + conn := &WebsocketConn{ + ws: ws, + } + conn.setupDeadline() + return conn } func (w *WebsocketConn) ReadMessage() (messageType int, p []byte, err error) { @@ -84,3 +58,29 @@ func (w *WebsocketConn) Close() error { return w.closeError } + +func (w *WebsocketConn) setupDeadline() { + _ = w.ws.SetReadDeadline(time.Now().Add(PingWaitDuration)) + w.ws.SetPingHandler(func(string) error { + w.m.Lock() + err := w.ws.WriteControl( + websocket.PongMessage, + []byte(""), + time.Now().Add(PingWaitDuration), + ) + w.m.Unlock() + if err != nil { + return err + } + if err := w.ws.SetReadDeadline(time.Now().Add(PingWaitDuration)); err != nil { + return err + } + return w.ws.SetWriteDeadline(time.Now().Add(PingWaitDuration)) + }) + w.ws.SetPongHandler(func(string) error { + if err := w.ws.SetReadDeadline(time.Now().Add(PingWaitDuration)); err != nil { + return err + } + return w.ws.SetWriteDeadline(time.Now().Add(PingWaitDuration)) + }) +} diff --git a/pkg/stdio/listener.go b/pkg/stdio/listener.go index dd7da16fd..bfa1d4963 100644 --- a/pkg/stdio/listener.go +++ b/pkg/stdio/listener.go @@ -5,6 +5,11 @@ import ( "net" ) +// StdioListener implements the listener interface. +type StdioListener struct { + connChan chan net.Conn +} + // NewStdioListener creates a new stdio listener. func NewStdioListener(reader io.Reader, writer io.WriteCloser, exitOnClose bool) *StdioListener { conn := NewStdioStream(reader, writer, exitOnClose, 0) @@ -18,11 +23,6 @@ func NewStdioListener(reader io.Reader, writer io.WriteCloser, exitOnClose bool) } } -// StdioListener implements the listener interface. -type StdioListener struct { - connChan chan net.Conn -} - // Ready implements interface. func (lis *StdioListener) Ready(conn net.Conn) { } diff --git a/pkg/types/time.go b/pkg/types/time.go index c38f80173..b76092c03 100644 --- a/pkg/types/time.go +++ b/pkg/types/time.go @@ -32,6 +32,11 @@ type Time struct { time.Time `protobuf:"-"` } +// NewTime returns a wrapped instance of the provided time. +func NewTime(time time.Time) Time { + return Time{time} +} + // DeepCopyInto creates a deep-copy of the Time value. The underlying time.Time // type is effectively immutable in the time API, so it is safe to // copy-by-assign, despite the presence of (unexported) Pointer fields. @@ -39,11 +44,6 @@ func (t *Time) DeepCopyInto(out *Time) { *out = *t } -// NewTime returns a wrapped instance of the provided time. -func NewTime(time time.Time) Time { - return Time{time} -} - // Date returns the Time corresponding to the supplied parameters // by wrapping time.Date. func Date(year int, month time.Month, day, hour, min, sec, nsec int, loc *time.Location) Time { From 782efaddaf46d47e3bb51d58bc7d7a09dc558900 Mon Sep 17 00:00:00 2001 From: Samuel K Date: Mon, 3 Aug 2026 18:28:51 +0000 Subject: [PATCH 2/2] fix(lint): resolve pre-existing argument-limit and G204 findings CI's only-new-issues flagged these because the funcorder reorder touched their lines, but both predate this PR: - pkg/dockerfile/parse.go: expandVariables and resolveVariable each carried an unused trailing int parameter (a vestigial compatibility placeholder), pushing them to 5 args. Removed it and updated all call sites; two of them were passing a real value that was always discarded. - pkg/ide/openvscode/openvscode.go: two exec.Command(args[0], args[1:]...) calls build args from a fixed "su"/"sh" shell and an internally constructed command string, not external input -- same trust level already accepted for the equivalent pattern in pkg/ssh/server/ssh.go. Suppressed with the same //nolint:gosec convention used there. --- pkg/dockerfile/parse.go | 11 +++-------- pkg/ide/openvscode/openvscode.go | 2 ++ 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/pkg/dockerfile/parse.go b/pkg/dockerfile/parse.go index 2553a7054..5db222f7a 100644 --- a/pkg/dockerfile/parse.go +++ b/pkg/dockerfile/parse.go @@ -38,7 +38,6 @@ func (d *Dockerfile) FindUserStatement( buildArgs, baseImageEnv, &stage.BaseStage, - 0, ) } @@ -51,7 +50,6 @@ func (d *Dockerfile) FindUserStatement( buildArgs, baseImageEnv, &d.Preamble.BaseStage, - d.Stages[0].Instructions[0].StartLine, ) stage, ok = d.StagesByTarget[image] if !ok { @@ -74,7 +72,7 @@ func (d *Dockerfile) FindBaseImage(buildArgs map[string]string, target string) s return "" } - image := d.expandVariables(stage.Image, buildArgs, nil, &d.Preamble.BaseStage, 0) + image := d.expandVariables(stage.Image, buildArgs, nil, &d.Preamble.BaseStage) // If image is a stage reference, resolve it recursively if _, ok := d.StagesByTarget[image]; ok { @@ -111,7 +109,7 @@ type environmentResolver struct { } func (e *environmentResolver) Get(key string) (string, bool) { - val, ok := e.dockerfile.resolveVariable(e.buildArgs, e.baseImageEnv, key, e.stage, 0) + val, ok := e.dockerfile.resolveVariable(e.buildArgs, e.baseImageEnv, key, e.stage) return val, ok } @@ -226,7 +224,6 @@ func (d *Dockerfile) expandVariables( val string, buildArgs, baseImageEnv map[string]string, stage *BaseStage, - _ int, ) string { result, _, err := defaultShellLexer.ProcessWord( val, @@ -242,7 +239,6 @@ func (d *Dockerfile) resolveVariable( buildArgs, baseImageEnv map[string]string, variable string, stage *BaseStage, - _ int, ) (string, bool) { if buildArgs == nil { buildArgs = make(map[string]string) @@ -309,7 +305,7 @@ func (d *Dockerfile) resolveFromEnvs( continue } if env.Value != "" { - return d.expandVariables(env.Value, buildArgs, baseImageEnv, stage, 0), true + return d.expandVariables(env.Value, buildArgs, baseImageEnv, stage), true } return "", true } @@ -337,7 +333,6 @@ func (d *Dockerfile) getParentStage( buildArgs, baseImageEnv, &d.Preamble.BaseStage, - d.Stages[0].Instructions[0].StartLine, ) if foundStage, ok := d.StagesByTarget[image]; ok { return &foundStage.BaseStage diff --git a/pkg/ide/openvscode/openvscode.go b/pkg/ide/openvscode/openvscode.go index 9cd4561e3..fc5f5a4ac 100644 --- a/pkg/ide/openvscode/openvscode.go +++ b/pkg/ide/openvscode/openvscode.go @@ -177,6 +177,7 @@ func (o *OpenVSCodeServer) Start() error { } else { args = append(args, "sh", "-c", runCommand) } + //nolint:gosec // G204: shell/su fixed above, runCommand built from internal binaryPath and config, not external input cmd := exec.Command(args[0], args[1:]...) cmd.Dir = location return cmd, nil @@ -225,6 +226,7 @@ func (o *OpenVSCodeServer) installExtensions() error { } else { args = append(args, "sh", "-c", runCommand) } + //nolint:gosec // G204: shell/su fixed above, runCommand built from internal binaryPath and config, not external input cmd := exec.Command(args[0], args[1:]...) cmd.Stdout = out cmd.Stderr = out