Skip to content

refactor(client): extract common client implementations to pkg#381

Merged
skevetter merged 2 commits intomainfrom
refactor-ssh-agent-build
Jan 27, 2026
Merged

refactor(client): extract common client implementations to pkg#381
skevetter merged 2 commits intomainfrom
refactor-ssh-agent-build

Conversation

@skevetter
Copy link
Owner

@skevetter skevetter commented Jan 27, 2026

Signed-off-by: Samuel K skevetter@pm.me

Summary by CodeRabbit

  • New Features

    • Adds a background services daemon to enable credential forwarding and optional port forwarding during SSH/workspace sessions.
    • Exposes a standardized wait mechanism to reliably wait for workspace readiness.
  • Refactor

    • Consolidates build and service orchestration into a centralized client-facing implementation, simplifying startup and maintenance while preserving existing user workflows.

✏️ Tip: You can customize this high-level summary in your review settings.

Signed-off-by: Samuel K <skevetter@pm.me>
@coderabbitai
Copy link

coderabbitai bot commented Jan 27, 2026

📝 Walkthrough

Walkthrough

Delegates build-agent orchestration, service-daemon startup, and workspace start-wait logic from cmd/* into pkg/client/clientimplementation via three new public APIs: BuildAgentClient, StartServicesDaemon, and StartWait, each accepting options structs and consolidating previously in-file plumbing.

Changes

Cohort / File(s) Summary
Command layer refactoring
cmd/build.go, cmd/ssh.go, cmd/up.go
Replaced local implementations with calls into clientimplementation APIs (BuildAgentClient, StartServicesDaemon, StartWait) using options structs; removed direct agent/tunnelserver and daemon/platform plumbing from these command files.
Client implementation — services
pkg/client/clientimplementation/services.go
New StartServicesDaemon and StartServicesDaemonOptions; resolves workspace, merges credential-forwarding config, and invokes tunnel.RunServices for credential and optional port forwarding.
Client implementation — workspace & agent
pkg/client/clientimplementation/workspace_client.go
New BuildAgentClient with BuildAgentClientOptions (agent cmd, workspace wiring, IPC pipes, agent injection, tunnel server orchestration) and StartWait (workspace readiness polling with Busy/Stopped/NotFound handling).

Sequence Diagram(s)

mermaid
sequenceDiagram
participant CLI as "CLI (cmd/*)"
participant CI as "clientimplementation"
participant WC as "WorkspaceClient (daemon)"
participant AG as "Agent process / Injection"
participant TS as "TunnelServer / tunnel.RunServices"

CLI->>CI: BuildAgentClient(ctx, opts)
CI->>WC: Ensure workspace / StartWait
WC-->>CI: workspace status / metadata
CI->>AG: spawn agent (create pipes, inject)
CI->>TS: start tunnel server for agent comms
AG-->>TS: connect via pipes/tunnel
TS-->>CLI: tunnels/forwarding ready
CI-->>CLI: return build result

mermaid
sequenceDiagram
participant CLI as "CLI (cmd/ssh/start)"
participant CI as "clientimplementation"
participant WC as "WorkspaceClient (daemon)"
participant TS as "tunnel.RunServices"
participant SSH as "SSH client"

CLI->>CI: StartServicesDaemon(ctx, opts)
CI->>WC: resolve workspace (getWorkspace)
WC-->>CI: workspace config/state
CI->>TS: RunServices(with credential/port options, SSH)
TS-->>SSH: forward credentials/ports
CI-->>CLI: return

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main objective of the pull request: extracting common client implementations from cmd/ files to the pkg/client/clientimplementation package.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@pkg/client/clientimplementation/services.go`:
- Around line 64-84: The function getCredentialConfig reads
workspace.Status.Instance and instance.CredentialForwarding before verifying
workspace and Status are non-nil; move the accesses so you first check workspace
!= nil and workspace.Status != nil (and workspace.Status.Instance != nil) before
assigning instance := workspace.Status.Instance and instanceCredentialForwarding
:= instance.CredentialForwarding, and guard usages of
instanceCredentialForwarding.Docker / .Git with nil checks; ensure
credentialConfig default values are still derived from
devPodConfig.ContextOption and only overridden when the instance and its
CredentialForwarding are present.

In `@pkg/client/clientimplementation/workspace_client.go`:
- Around line 849-883: BuildAgentClient leaks the read end of the stdin pipe
returned by createPipes: stdinReader is passed to runAgentInjection but never
closed. After the createPipes call in BuildAgentClient, add a deferred close for
stdinReader (or ensure runAgentInjection closes it on completion/failure) so the
file descriptor is always released; reference the createPipes call and the
stdinReader passed into runAgentInjection to locate where to add the defer.
🧹 Nitpick comments (2)
pkg/client/clientimplementation/workspace_client.go (2)

776-808: Potential infinite loop when workspace status remains Stopped or NotFound without create=true.

When create is false and status is StatusStopped or StatusNotFound, the handler functions return an error immediately (lines 826, 837). However, when create is true and the Create/Start call succeeds, the loop continues without a status change check, potentially causing repeated Create/Start attempts if the status doesn't transition quickly.

Consider adding a brief sleep after successful Create/Start to allow the workspace to transition, or return after initiating the operation:

♻️ Suggested improvement
 func handleStoppedStatus(ctx context.Context, workspaceClient client.WorkspaceClient, create bool) error {
 	if create {
 		err := workspaceClient.Start(ctx, client.StartOptions{})
 		if err != nil {
 			return fmt.Errorf("start workspace %w", err)
 		}
-		return nil
+		time.Sleep(pollInterval)
+		return nil // Continue polling for status change
 	}
 	return fmt.Errorf("workspace is stopped")
 }

 func handleNotFoundStatus(ctx context.Context, workspaceClient client.WorkspaceClient, create bool) error {
 	if create {
 		err := workspaceClient.Create(ctx, client.CreateOptions{})
 		if err != nil {
 			return err
 		}
-		return nil
+		time.Sleep(pollInterval)
+		return nil // Continue polling for status change
 	}
 	return fmt.Errorf("workspace not found")
 }

918-949: Error from agent injection goroutine may be silently ignored on tunnel server failure.

In BuildAgentClient, if runTunnelServer returns an error (line 878), the function returns immediately without waiting for errChan. This means the goroutine in runAgentInjection continues running until it completes or the context is cancelled, but its error is never consumed.

Additionally, if the tunnel server succeeds but agent injection fails, the error from errChan is returned (line 882), but there's no aggregation of both errors.

Consider ensuring both errors are properly handled:

♻️ Suggested improvement
 	result, err := runTunnelServer(cancelCtx, opts, stdoutReader, stdinWriter)
-	if err != nil {
-		return nil, err
-	}
-
-	return result, <-errChan
+	agentErr := <-errChan
+	if err != nil {
+		// Tunnel server failed; agent error is secondary
+		return nil, err
+	}
+	if agentErr != nil {
+		return nil, agentErr
+	}
+	return result, nil

Signed-off-by: Samuel K <skevetter@pm.me>
@skevetter skevetter merged commit 9baccde into main Jan 27, 2026
39 checks passed
@skevetter skevetter deleted the refactor-ssh-agent-build branch January 27, 2026 10:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant