@@ -59,6 +59,91 @@ func (app *App) initClients(ctx context.Context) error {
5959 return nil
6060}
6161
62+ // initSprinklerOrgs fetches the user's organizations and starts sprinkler monitoring.
63+ func (app * App ) initSprinklerOrgs (ctx context.Context ) error {
64+ if app .client == nil || app .sprinklerMonitor == nil {
65+ return fmt .Errorf ("client or sprinkler not initialized" )
66+ }
67+
68+ // Get current user
69+ user := ""
70+ if app .currentUser != nil {
71+ user = app .currentUser .GetLogin ()
72+ }
73+ if app .targetUser != "" {
74+ user = app .targetUser
75+ }
76+ if user == "" {
77+ return fmt .Errorf ("no user configured" )
78+ }
79+
80+ slog .Info ("[SPRINKLER] Fetching user's organizations" , "user" , user )
81+
82+ // Fetch all orgs the user is a member of with retry
83+ opts := & github.ListOptions {PerPage : 100 }
84+ var allOrgs []string
85+
86+ for {
87+ var orgs []* github.Organization
88+ var resp * github.Response
89+
90+ err := retry .Do (func () error {
91+ // Create timeout context for API call
92+ apiCtx , cancel := context .WithTimeout (ctx , 30 * time .Second )
93+ defer cancel ()
94+
95+ var retryErr error
96+ orgs , resp , retryErr = app .client .Organizations .List (apiCtx , user , opts )
97+ if retryErr != nil {
98+ slog .Debug ("[SPRINKLER] Organizations.List failed (will retry)" , "error" , retryErr , "page" , opts .Page )
99+ return retryErr
100+ }
101+ return nil
102+ },
103+ retry .Attempts (maxRetries ),
104+ retry .DelayType (retry .CombineDelay (retry .BackOffDelay , retry .RandomDelay )),
105+ retry .MaxDelay (maxRetryDelay ),
106+ retry .OnRetry (func (n uint , err error ) {
107+ slog .Warn ("[SPRINKLER] Organizations.List retry" , "attempt" , n + 1 , "error" , err , "page" , opts .Page )
108+ }),
109+ retry .Context (ctx ),
110+ )
111+ if err != nil {
112+ // Gracefully degrade - continue without sprinkler if org fetch fails
113+ slog .Warn ("[SPRINKLER] Failed to fetch organizations after retries, sprinkler will not start" ,
114+ "error" , err ,
115+ "maxRetries" , maxRetries )
116+ return nil // Return nil to avoid blocking startup
117+ }
118+
119+ for _ , org := range orgs {
120+ if org .Login != nil {
121+ allOrgs = append (allOrgs , * org .Login )
122+ }
123+ }
124+
125+ if resp .NextPage == 0 {
126+ break
127+ }
128+ opts .Page = resp .NextPage
129+ }
130+
131+ slog .Info ("[SPRINKLER] Discovered user organizations" ,
132+ "user" , user ,
133+ "orgs" , allOrgs ,
134+ "count" , len (allOrgs ))
135+
136+ // Update sprinkler with all orgs at once
137+ if len (allOrgs ) > 0 {
138+ app .sprinklerMonitor .updateOrgs (allOrgs )
139+ if err := app .sprinklerMonitor .start (); err != nil {
140+ return fmt .Errorf ("start sprinkler: %w" , err )
141+ }
142+ }
143+
144+ return nil
145+ }
146+
62147// token retrieves the GitHub token from GITHUB_TOKEN env var or gh CLI.
63148func (* App ) token (ctx context.Context ) (string , error ) {
64149 // Check GITHUB_TOKEN environment variable first
@@ -410,22 +495,6 @@ func (app *App) fetchPRsInternal(ctx context.Context) (incoming []PR, outgoing [
410495 // Only log summary, not individual PRs
411496 slog .Info ("[GITHUB] GitHub PR summary" , "incoming" , len (incoming ), "outgoing" , len (outgoing ))
412497
413- // Update sprinkler monitor with discovered orgs
414- app .mu .RLock ()
415- orgs := make ([]string , 0 , len (app .seenOrgs ))
416- for org := range app .seenOrgs {
417- orgs = append (orgs , org )
418- }
419- app .mu .RUnlock ()
420-
421- if app .sprinklerMonitor != nil && len (orgs ) > 0 {
422- app .sprinklerMonitor .updateOrgs (orgs )
423- // Start monitor if not already running
424- if err := app .sprinklerMonitor .start (); err != nil {
425- slog .Warn ("[SPRINKLER] Failed to start monitor" , "error" , err )
426- }
427- }
428-
429498 // Fetch Turn API data
430499 // Always synchronous now for simplicity - Turn API calls are fast with caching
431500 app .fetchTurnDataSync (ctx , allIssues , user , & incoming , & outgoing )
0 commit comments