Skip to content

Commit c308cad

Browse files
authored
Merge pull request #28 from codeGROOVE-dev/gh-err
Show an error in the menubar when gh isn't available
2 parents f025f14 + 2ec0c00 commit c308cad

File tree

3 files changed

+128
-43
lines changed

3 files changed

+128
-43
lines changed

cmd/goose/github.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,11 +216,22 @@ type prResult struct {
216216
// It returns GitHub data immediately and starts Turn API queries in the background (when waitForTurn=false),
217217
// or waits for Turn data to complete (when waitForTurn=true).
218218
func (app *App) fetchPRsInternal(ctx context.Context, waitForTurn bool) (incoming []PR, outgoing []PR, _ error) {
219+
// Check if we have a client
220+
if app.client == nil {
221+
return nil, nil, fmt.Errorf("no GitHub client available: %s", app.authError)
222+
}
223+
219224
// Use targetUser if specified, otherwise use authenticated user
220-
user := app.currentUser.GetLogin()
225+
user := ""
226+
if app.currentUser != nil {
227+
user = app.currentUser.GetLogin()
228+
}
221229
if app.targetUser != "" {
222230
user = app.targetUser
223231
}
232+
if user == "" {
233+
return nil, nil, errors.New("no user specified and current user not loaded")
234+
}
224235

225236
const perPage = 100
226237
opts := &github.SearchOptions{

cmd/goose/main.go

Lines changed: 74 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,11 @@ type App struct {
7777
previousBlockedPRs map[string]bool
7878
targetUser string
7979
cacheDir string
80+
authError string
81+
incoming []PR
8082
outgoing []PR
8183
lastMenuTitles []string
8284
pendingTurnResults []TurnResult
83-
incoming []PR
8485
updateInterval time.Duration
8586
consecutiveFailures int
8687
mu sync.RWMutex
@@ -93,6 +94,52 @@ type App struct {
9394
enableAudioCues bool
9495
}
9596

97+
func loadCurrentUser(ctx context.Context, app *App) {
98+
log.Println("Loading current user...")
99+
100+
if app.client == nil {
101+
log.Println("Skipping user load - no GitHub client available")
102+
return
103+
}
104+
105+
var user *github.User
106+
err := retry.Do(func() error {
107+
var retryErr error
108+
user, _, retryErr = app.client.Users.Get(ctx, "")
109+
if retryErr != nil {
110+
log.Printf("GitHub Users.Get failed (will retry): %v", retryErr)
111+
return retryErr
112+
}
113+
return nil
114+
},
115+
retry.Attempts(maxRetries),
116+
retry.DelayType(retry.BackOffDelay),
117+
retry.MaxDelay(maxRetryDelay),
118+
retry.OnRetry(func(n uint, err error) {
119+
log.Printf("GitHub Users.Get retry %d/%d: %v", n+1, maxRetries, err)
120+
}),
121+
retry.Context(ctx),
122+
)
123+
if err != nil {
124+
log.Printf("Warning: Failed to load current user after %d retries: %v", maxRetries, err)
125+
if app.authError == "" {
126+
app.authError = fmt.Sprintf("Failed to load user: %v", err)
127+
}
128+
return
129+
}
130+
131+
if user == nil {
132+
log.Print("Warning: GitHub API returned nil user")
133+
return
134+
}
135+
136+
app.currentUser = user
137+
// Log if we're using a different target user (sanitized)
138+
if app.targetUser != "" && app.targetUser != user.GetLogin() {
139+
log.Printf("Querying PRs for user '%s' instead of authenticated user", sanitizeForLog(app.targetUser))
140+
}
141+
}
142+
96143
func main() {
97144
// Parse command line flags
98145
var targetUser string
@@ -150,40 +197,13 @@ func main() {
150197
log.Println("Initializing GitHub clients...")
151198
err = app.initClients(ctx)
152199
if err != nil {
153-
log.Fatalf("Failed to initialize clients: %v", err)
154-
}
155-
156-
log.Println("Loading current user...")
157-
var user *github.User
158-
err = retry.Do(func() error {
159-
var retryErr error
160-
user, _, retryErr = app.client.Users.Get(ctx, "")
161-
if retryErr != nil {
162-
log.Printf("GitHub Users.Get failed (will retry): %v", retryErr)
163-
return retryErr
164-
}
165-
return nil
166-
},
167-
retry.Attempts(maxRetries),
168-
retry.DelayType(retry.BackOffDelay),
169-
retry.MaxDelay(maxRetryDelay),
170-
retry.OnRetry(func(n uint, err error) {
171-
log.Printf("GitHub Users.Get retry %d/%d: %v", n+1, maxRetries, err)
172-
}),
173-
retry.Context(ctx),
174-
)
175-
if err != nil {
176-
log.Fatalf("Failed to load current user after %d retries: %v", maxRetries, err)
177-
}
178-
if user == nil {
179-
log.Fatal("GitHub API returned nil user")
200+
log.Printf("Warning: Failed to initialize clients: %v", err)
201+
app.authError = err.Error()
202+
// Continue running with auth error - will show error in UI
180203
}
181-
app.currentUser = user
182204

183-
// Log if we're using a different target user (sanitized)
184-
if app.targetUser != "" && app.targetUser != user.GetLogin() {
185-
log.Printf("Querying PRs for user '%s' instead of authenticated user", sanitizeForLog(app.targetUser))
186-
}
205+
// Load current user if we have a client
206+
loadCurrentUser(ctx, app)
187207

188208
log.Println("Starting systray...")
189209
// Create a cancellable context for the application
@@ -199,16 +219,8 @@ func main() {
199219

200220
func (app *App) onReady(ctx context.Context) {
201221
log.Println("System tray ready")
202-
systray.SetTitle("Loading PRs...")
203222

204-
// Set tooltip based on whether we're using a custom user
205-
tooltip := "GitHub PR Monitor"
206-
if app.targetUser != "" {
207-
tooltip = fmt.Sprintf("GitHub PR Monitor - @%s", app.targetUser)
208-
}
209-
systray.SetTooltip(tooltip)
210-
211-
// Set up click handlers
223+
// Set up click handlers first (needed for both success and error states)
212224
systray.SetOnClick(func(menu systray.IMenu) {
213225
log.Println("Icon clicked")
214226
if menu != nil {
@@ -227,6 +239,26 @@ func (app *App) onReady(ctx context.Context) {
227239
}
228240
})
229241

242+
// Check if we have an auth error
243+
if app.authError != "" {
244+
systray.SetTitle("⚠️")
245+
systray.SetTooltip("GitHub PR Monitor - Authentication Error")
246+
// Create initial error menu
247+
app.rebuildMenu(ctx)
248+
// Clean old cache on startup
249+
app.cleanupOldCache()
250+
return
251+
}
252+
253+
systray.SetTitle("Loading PRs...")
254+
255+
// Set tooltip based on whether we're using a custom user
256+
tooltip := "GitHub PR Monitor"
257+
if app.targetUser != "" {
258+
tooltip = fmt.Sprintf("GitHub PR Monitor - @%s", app.targetUser)
259+
}
260+
systray.SetTooltip(tooltip)
261+
230262
// Clean old cache on startup
231263
app.cleanupOldCache()
232264

cmd/goose/ui.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,48 @@ func (app *App) rebuildMenu(ctx context.Context) {
226226
// Clear all existing menu items
227227
systray.ResetMenu()
228228

229+
// Check for auth error first
230+
if app.authError != "" {
231+
// Show authentication error message
232+
errorTitle := systray.AddMenuItem("⚠️ Authentication Error", "")
233+
errorTitle.Disable()
234+
235+
systray.AddSeparator()
236+
237+
// Add error details
238+
errorMsg := systray.AddMenuItem(app.authError, "Click to see setup instructions")
239+
errorMsg.Click(func() {
240+
if err := openURL(ctx, "https://cli.github.com/manual/gh_auth_login"); err != nil {
241+
log.Printf("failed to open setup instructions: %v", err)
242+
}
243+
})
244+
245+
systray.AddSeparator()
246+
247+
// Add setup instructions
248+
setupInstr := systray.AddMenuItem("To fix this issue:", "")
249+
setupInstr.Disable()
250+
251+
option1 := systray.AddMenuItem("1. Install GitHub CLI: brew install gh", "")
252+
option1.Disable()
253+
254+
option2 := systray.AddMenuItem("2. Run: gh auth login", "")
255+
option2.Disable()
256+
257+
option3 := systray.AddMenuItem("3. Or set GITHUB_TOKEN environment variable", "")
258+
option3.Disable()
259+
260+
systray.AddSeparator()
261+
262+
// Add quit option
263+
quitItem := systray.AddMenuItem("Quit", "")
264+
quitItem.Click(func() {
265+
systray.Quit()
266+
})
267+
268+
return
269+
}
270+
229271
// Update tray title
230272
app.setTrayTitle()
231273

0 commit comments

Comments
 (0)