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
6 changes: 4 additions & 2 deletions server/internal/daemon/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ var ErrRepoNotConfigured = errors.New("repo is not configured for this workspace
type workspaceState struct {
workspaceID string
runtimeIDs []string
reposVersion string
reposVersion string // stored for future use: skip refresh when version unchanged
allowedRepoURLs map[string]struct{}
lastRepoSyncErr string
repoRefreshMu sync.Mutex
Expand Down Expand Up @@ -318,6 +318,8 @@ func (d *Daemon) ensureRepoReady(ctx context.Context, workspaceID, repoURL strin
return fmt.Errorf("repo cache not initialized")
}

repoURL = strings.TrimSpace(repoURL)

d.mu.Lock()
ws, ok := d.workspaces[workspaceID]
d.mu.Unlock()
Expand Down Expand Up @@ -406,7 +408,7 @@ func (d *Daemon) syncWorkspacesFromAPI(ctx context.Context) error {
var registered int
for id, name := range apiIDs {
if currentIDs[id] {
continue
continue // important: never replace existing workspaceState; ensureRepoReady holds ws.repoRefreshMu from the original pointer
}
resp, err := d.registerRuntimesForWorkspace(ctx, id)
if err != nil {
Expand Down
23 changes: 23 additions & 0 deletions server/internal/daemon/daemon_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,29 @@ func TestEnsureRepoReadyFastPathDoesNotRefresh(t *testing.T) {
}
}

func TestEnsureRepoReadyTrimsURL(t *testing.T) {
t.Parallel()

sourceRepo := createDaemonTestRepo(t)
var refreshCalls atomic.Int32
d := newRepoReadyTestDaemon(t, func(w http.ResponseWriter, r *http.Request) {
refreshCalls.Add(1)
http.Error(w, "unexpected refresh", http.StatusInternalServerError)
})
if err := d.repoCache.Sync("ws-1", []repocache.RepoInfo{{URL: sourceRepo}}); err != nil {
t.Fatalf("seed repo cache: %v", err)
}
d.workspaces["ws-1"] = newWorkspaceState("ws-1", nil, "v1", []RepoData{{URL: sourceRepo}})

// URL with trailing whitespace should still hit the fast path.
if err := d.ensureRepoReady(context.Background(), "ws-1", " "+sourceRepo+" "); err != nil {
t.Fatalf("ensureRepoReady with padded URL: %v", err)
}
if got := refreshCalls.Load(); got != 0 {
t.Fatalf("expected no refresh calls for trimmed URL, got %d", got)
}
}

func TestEnsureRepoReadyRefreshesOnMiss(t *testing.T) {
t.Parallel()

Expand Down
Loading