Skip to content

Commit fa40146

Browse files
committed
Fix coder workspaces refresh
Change codespaces and workspaces checks to check if gh and coder are in path and authenticated. Instead of waiting for workspaces to be configured.
1 parent 66f5398 commit fa40146

6 files changed

Lines changed: 51 additions & 67 deletions

File tree

internal/config/config.go

Lines changed: 0 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -382,57 +382,6 @@ func (c *Config) effectiveWorkspaceProviderLocked() string {
382382
return c.WorkspaceProvider
383383
}
384384

385-
// IsCoderConfigured reports whether Coder is in use anywhere in the
386-
// config: as the effective provider, or via any target that declares a
387-
// `coder` block. Used to keep the Coder menu visible whenever the user
388-
// has wired up Coder, even if the API call failed on the last poll.
389-
func (c *Config) IsCoderConfigured() bool {
390-
if c == nil {
391-
return false
392-
}
393-
c.mu.RLock()
394-
defer c.mu.RUnlock()
395-
// NOTE: cannot import provider package due to import cycle
396-
// (provider imports config). Keep this literal in sync with
397-
// provider.NameCoder.
398-
if c.effectiveWorkspaceProviderLocked() == "coder" {
399-
return true
400-
}
401-
for _, t := range c.Targets {
402-
if t.Coder != nil {
403-
return true
404-
}
405-
}
406-
return false
407-
}
408-
409-
// IsGitHubConfigured reports whether GitHub Codespaces is in use:
410-
// either as the effective provider (the default), or via any target
411-
// that doesn't declare a `coder` block.
412-
//
413-
// Note the asymmetry with IsCoderConfigured: GitHub is the default
414-
// provider, so the absence of an explicit `coder` block on a target
415-
// makes that target an implicit GitHub target. A nil Config (no file
416-
// loaded yet) is therefore treated as "GitHub configured", which lets
417-
// the Codespaces tray submenu appear during early startup. Coder, by
418-
// contrast, only counts when targets opt in explicitly via t.Coder.
419-
func (c *Config) IsGitHubConfigured() bool {
420-
if c == nil {
421-
return true
422-
}
423-
c.mu.RLock()
424-
defer c.mu.RUnlock()
425-
if c.effectiveWorkspaceProviderLocked() == "github" {
426-
return true
427-
}
428-
for _, t := range c.Targets {
429-
if t.Coder == nil {
430-
return true
431-
}
432-
}
433-
return false
434-
}
435-
436385
func (t Target) ExplicitWorkspaceName(provider string) string {
437386
// NOTE: cannot import provider package due to import cycle
438387
// (provider imports config). Keep this literal in sync with

internal/daemon/poller.go

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -179,12 +179,9 @@ func (d *Daemon) runPoll() {
179179
return codespacesToWorkspaces(cs), nil
180180
})
181181

182-
var coderWorkspaces []provider.Workspace
183-
if d.Cfg != nil && d.Cfg.IsCoderConfigured() {
184-
coderWorkspaces = d.pollProvider(provider.NameCoder, "coder", func(ctx context.Context) ([]provider.Workspace, error) {
185-
return provider.NewCoderManager(d.Cfg).ListAllWorkspacesCtx(ctx)
186-
})
187-
}
182+
coderWorkspaces := d.pollProvider(provider.NameCoder, "coder", func(ctx context.Context) ([]provider.Workspace, error) {
183+
return provider.NewCoderManager(d.Cfg).ListAllWorkspacesCtx(ctx)
184+
})
188185

189186
workspaces := append(ghWorkspaces, coderWorkspaces...)
190187

internal/daemon/tray.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ func (d *Daemon) buildTrayMenu() *fyne.Menu {
118118

119119
func (d *Daemon) githubCodespacesMenu() *fyne.MenuItem {
120120
all := d.Codespaces()
121-
if len(all) == 0 && !d.Cfg.IsGitHubConfigured() {
121+
if len(all) == 0 && !provider.IsGitHubAvailable() {
122122
return nil
123123
}
124124

@@ -196,8 +196,7 @@ func githubStatusMessage(status ProviderStatus) string {
196196

197197
func (d *Daemon) coderWorkspaceMenu() *fyne.MenuItem {
198198
workspaces := filterWorkspacesByProvider(d.Workspaces(), provider.NameCoder)
199-
configured := d.Cfg != nil && d.Cfg.IsCoderConfigured()
200-
if len(workspaces) == 0 && !configured {
199+
if len(workspaces) == 0 && !provider.IsCoderAvailable() {
201200
return nil
202201
}
203202

internal/provider/availability.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package provider
2+
3+
import (
4+
"context"
5+
"os/exec"
6+
"time"
7+
)
8+
9+
// availabilityProbeTimeout caps the local auth probe so a misbehaving
10+
// CLI (network stall, hung SSO) can't pin a tray rebuild or UI gate.
11+
const availabilityProbeTimeout = 5 * time.Second
12+
13+
// IsGitHubAvailable reports whether the `gh` CLI is on PATH and the
14+
// user is authenticated for codespace operations. Implemented as
15+
// `gh auth status` because it's the cheapest signal that catches both
16+
// "CLI missing" and "not logged in" without making a Codespaces API
17+
// call. False also covers token-scope problems that gh auth surfaces.
18+
func IsGitHubAvailable() bool {
19+
if _, err := exec.LookPath("gh"); err != nil {
20+
return false
21+
}
22+
ctx, cancel := context.WithTimeout(context.Background(), availabilityProbeTimeout)
23+
defer cancel()
24+
return exec.CommandContext(ctx, "gh", "auth", "status").Run() == nil
25+
}
26+
27+
// IsCoderAvailable reports whether the `coder` CLI is on PATH and has
28+
// a working session. Implemented as `coder whoami` — fast, hits the
29+
// configured Coder server only enough to validate the session token.
30+
func IsCoderAvailable() bool {
31+
if _, err := exec.LookPath("coder"); err != nil {
32+
return false
33+
}
34+
ctx, cancel := context.WithTimeout(context.Background(), availabilityProbeTimeout)
35+
defer cancel()
36+
return exec.CommandContext(ctx, "coder", "whoami").Run() == nil
37+
}

internal/tui/applet_create.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -99,11 +99,13 @@ func newCreateModel(d *AppletData) createModel {
9999
}
100100
}
101101

102-
// Default provider selection. Prefer whichever is configured; if both
103-
// are, default to GitHub but allow toggling. The provider row is
104-
// hidden when only one is in play.
105-
gh := cfg.IsGitHubConfigured()
106-
cd := cfg.IsCoderConfigured() && len(m.coderTargets) > 0
102+
// Default provider selection. Prefer whichever provider is available
103+
// (CLI + auth); if both are, default to GitHub but allow toggling.
104+
// The provider row is hidden when only one is in play. Coder also
105+
// requires at least one Coder-tagged target since create needs a
106+
// template selector.
107+
gh := provider.IsGitHubAvailable()
108+
cd := provider.IsCoderAvailable() && len(m.coderTargets) > 0
107109
switch {
108110
case gh && cd:
109111
m.providerName = provider.NameGitHub

internal/tui/applet_data.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ func (d *AppletData) Poll() PollResult {
166166
var combined []provider.Workspace
167167
var firstErr error
168168

169-
if d.cfg.IsGitHubConfigured() {
169+
if provider.IsGitHubAvailable() {
170170
ws, cs, err := d.pollGitHub()
171171
d.setProviderStatus(provider.NameGitHub, "gh", err)
172172
if err != nil && firstErr == nil {
@@ -177,7 +177,7 @@ func (d *AppletData) Poll() PollResult {
177177
d.mu.Unlock()
178178
combined = append(combined, ws...)
179179
}
180-
if d.cfg.IsCoderConfigured() {
180+
if provider.IsCoderAvailable() {
181181
ws, err := d.pollCoder()
182182
d.setProviderStatus(provider.NameCoder, "coder", err)
183183
if err != nil && firstErr == nil {

0 commit comments

Comments
 (0)