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
13 changes: 12 additions & 1 deletion cmd/goose/github.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,11 +204,22 @@ type prResult struct {
// It returns GitHub data immediately and starts Turn API queries in the background (when waitForTurn=false),
// or waits for Turn data to complete (when waitForTurn=true).
func (app *App) fetchPRsInternal(ctx context.Context, waitForTurn bool) (incoming []PR, outgoing []PR, _ error) {
// Check if we have a client
if app.client == nil {
return nil, nil, fmt.Errorf("no GitHub client available: %s", app.authError)
}

// Use targetUser if specified, otherwise use authenticated user
user := app.currentUser.GetLogin()
user := ""
if app.currentUser != nil {
user = app.currentUser.GetLogin()
}
if app.targetUser != "" {
user = app.targetUser
}
if user == "" {
return nil, nil, errors.New("no user specified and current user not loaded")
}

const perPage = 100
opts := &github.SearchOptions{
Expand Down
116 changes: 74 additions & 42 deletions cmd/goose/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,11 @@ type App struct {
previousBlockedPRs map[string]bool
targetUser string
cacheDir string
authError string
incoming []PR
outgoing []PR
lastMenuTitles []string
pendingTurnResults []TurnResult
incoming []PR
updateInterval time.Duration
consecutiveFailures int
mu sync.RWMutex
Expand All @@ -93,6 +94,52 @@ type App struct {
enableAudioCues bool
}

func loadCurrentUser(ctx context.Context, app *App) {
log.Println("Loading current user...")

if app.client == nil {
log.Println("Skipping user load - no GitHub client available")
return
}

var user *github.User
err := retry.Do(func() error {
var retryErr error
user, _, retryErr = app.client.Users.Get(ctx, "")
if retryErr != nil {
log.Printf("GitHub Users.Get failed (will retry): %v", retryErr)
return retryErr
}
return nil
},
retry.Attempts(maxRetries),
retry.DelayType(retry.BackOffDelay),
retry.MaxDelay(maxRetryDelay),
retry.OnRetry(func(n uint, err error) {
log.Printf("GitHub Users.Get retry %d/%d: %v", n+1, maxRetries, err)
}),
retry.Context(ctx),
)
if err != nil {
log.Printf("Warning: Failed to load current user after %d retries: %v", maxRetries, err)
if app.authError == "" {
app.authError = fmt.Sprintf("Failed to load user: %v", err)
}
return
}

if user == nil {
log.Print("Warning: GitHub API returned nil user")
return
}

app.currentUser = user
// Log if we're using a different target user (sanitized)
if app.targetUser != "" && app.targetUser != user.GetLogin() {
log.Printf("Querying PRs for user '%s' instead of authenticated user", sanitizeForLog(app.targetUser))
}
}

func main() {
// Parse command line flags
var targetUser string
Expand Down Expand Up @@ -150,40 +197,13 @@ func main() {
log.Println("Initializing GitHub clients...")
err = app.initClients(ctx)
if err != nil {
log.Fatalf("Failed to initialize clients: %v", err)
}

log.Println("Loading current user...")
var user *github.User
err = retry.Do(func() error {
var retryErr error
user, _, retryErr = app.client.Users.Get(ctx, "")
if retryErr != nil {
log.Printf("GitHub Users.Get failed (will retry): %v", retryErr)
return retryErr
}
return nil
},
retry.Attempts(maxRetries),
retry.DelayType(retry.BackOffDelay),
retry.MaxDelay(maxRetryDelay),
retry.OnRetry(func(n uint, err error) {
log.Printf("GitHub Users.Get retry %d/%d: %v", n+1, maxRetries, err)
}),
retry.Context(ctx),
)
if err != nil {
log.Fatalf("Failed to load current user after %d retries: %v", maxRetries, err)
}
if user == nil {
log.Fatal("GitHub API returned nil user")
log.Printf("Warning: Failed to initialize clients: %v", err)
app.authError = err.Error()
// Continue running with auth error - will show error in UI
}
app.currentUser = user

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

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

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

// Set tooltip based on whether we're using a custom user
tooltip := "GitHub PR Monitor"
if app.targetUser != "" {
tooltip = fmt.Sprintf("GitHub PR Monitor - @%s", app.targetUser)
}
systray.SetTooltip(tooltip)

// Set up click handlers
// Set up click handlers first (needed for both success and error states)
systray.SetOnClick(func(menu systray.IMenu) {
log.Println("Icon clicked")
if menu != nil {
Expand All @@ -227,6 +239,26 @@ func (app *App) onReady(ctx context.Context) {
}
})

// Check if we have an auth error
if app.authError != "" {
systray.SetTitle("⚠️")
systray.SetTooltip("GitHub PR Monitor - Authentication Error")
// Create initial error menu
app.rebuildMenu(ctx)
// Clean old cache on startup
app.cleanupOldCache()
return
}

systray.SetTitle("Loading PRs...")

// Set tooltip based on whether we're using a custom user
tooltip := "GitHub PR Monitor"
if app.targetUser != "" {
tooltip = fmt.Sprintf("GitHub PR Monitor - @%s", app.targetUser)
}
systray.SetTooltip(tooltip)

// Clean old cache on startup
app.cleanupOldCache()

Expand Down
42 changes: 42 additions & 0 deletions cmd/goose/ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,48 @@ func (app *App) rebuildMenu(ctx context.Context) {
// Clear all existing menu items
systray.ResetMenu()

// Check for auth error first
if app.authError != "" {
// Show authentication error message
errorTitle := systray.AddMenuItem("⚠️ Authentication Error", "")
errorTitle.Disable()

systray.AddSeparator()

// Add error details
errorMsg := systray.AddMenuItem(app.authError, "Click to see setup instructions")
errorMsg.Click(func() {
if err := openURL(ctx, "https://cli.github.com/manual/gh_auth_login"); err != nil {
log.Printf("failed to open setup instructions: %v", err)
}
})

systray.AddSeparator()

// Add setup instructions
setupInstr := systray.AddMenuItem("To fix this issue:", "")
setupInstr.Disable()

option1 := systray.AddMenuItem("1. Install GitHub CLI: brew install gh", "")
option1.Disable()

option2 := systray.AddMenuItem("2. Run: gh auth login", "")
option2.Disable()

option3 := systray.AddMenuItem("3. Or set GITHUB_TOKEN environment variable", "")
option3.Disable()

systray.AddSeparator()

// Add quit option
quitItem := systray.AddMenuItem("Quit", "")
quitItem.Click(func() {
systray.Quit()
})

return
}

// Update tray title
app.setTrayTitle()

Expand Down
Loading