fix(daemon): normalize repo URL and clarify reposVersion intent

- TrimSpace incoming repoURL in ensureRepoReady to prevent unnecessary
  server refreshes when CLI passes URLs with whitespace
- Add comment on reposVersion field clarifying it is stored for future
  version-based skip optimization
- Add concurrency safety comment on syncWorkspacesFromAPI skip logic
- Add test for URL trimming fast-path behavior
This commit is contained in:
Jiang Bohan
2026-04-15 19:12:56 +08:00
parent 0427fd8cc7
commit 8e2b74ae2e
2 changed files with 27 additions and 2 deletions

View File

@@ -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
@@ -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()
@@ -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 {

View File

@@ -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()