@@ -44,24 +44,51 @@ func (app *App) initClients(ctx context.Context) error {
4444 tc := oauth2 .NewClient (ctx , ts )
4545 app .client = github .NewClient (tc )
4646
47- // Initialize Turn client using default backend
48- turnClient , err := turn .NewDefaultClient ()
49- if err != nil {
50- return fmt .Errorf ("create turn client: %w" , err )
47+ // Check for custom turn server hostname (for self-hosting)
48+ // Set TURNSERVER=disabled to run without Turn API
49+ turnServer := os .Getenv ("TURNSERVER" )
50+ if turnServer == "disabled" {
51+ slog .Info ("Turn API disabled via TURNSERVER=disabled" )
52+ } else {
53+ var turnClient * turn.Client
54+ if turnServer != "" {
55+ slog .Info ("Using custom turn server" , "hostname" , turnServer )
56+ turnClient , err = turn .NewClient ("https://" + turnServer )
57+ } else {
58+ turnClient , err = turn .NewDefaultClient ()
59+ }
60+ if err != nil {
61+ return fmt .Errorf ("create turn client: %w" , err )
62+ }
63+ turnClient .SetAuthToken (token )
64+ app .turnClient = turnClient
5165 }
52- turnClient .SetAuthToken (token )
53- app .turnClient = turnClient
5466
5567 // Initialize sprinkler monitor for real-time events
56- app .sprinklerMonitor = newSprinklerMonitor (app , token )
68+ // Check for custom sprinkler server hostname (for self-hosting)
69+ // Set SPRINKLER=disabled to run without real-time events
70+ sprinklerServer := os .Getenv ("SPRINKLER" )
71+ if sprinklerServer == "disabled" {
72+ slog .Info ("Sprinkler disabled via SPRINKLER=disabled" )
73+ } else {
74+ if sprinklerServer != "" {
75+ slog .Info ("Using custom sprinkler server" , "hostname" , sprinklerServer )
76+ }
77+ app .sprinklerMonitor = newSprinklerMonitor (app , token , sprinklerServer )
78+ }
5779
5880 return nil
5981}
6082
6183// initSprinklerOrgs fetches the user's organizations and starts sprinkler monitoring.
6284func (app * App ) initSprinklerOrgs (ctx context.Context ) error {
63- if app .client == nil || app .sprinklerMonitor == nil {
64- return errors .New ("client or sprinkler not initialized" )
85+ if app .client == nil {
86+ return errors .New ("github client not initialized" )
87+ }
88+ // If sprinkler is disabled, skip silently
89+ if app .sprinklerMonitor == nil {
90+ slog .Debug ("[SPRINKLER] Sprinkler disabled, skipping org initialization" )
91+ return nil
6592 }
6693
6794 // Get current user
@@ -80,22 +107,21 @@ func (app *App) initSprinklerOrgs(ctx context.Context) error {
80107
81108 // Fetch all orgs the user is a member of with retry
82109 opts := & github.ListOptions {PerPage : 100 }
83- var allOrgs []string
110+ var orgs []string
84111
85112 for {
86- var orgs []* github.Organization
113+ var page []* github.Organization
87114 var resp * github.Response
88115
89116 err := retry .Do (func () error {
90- // Create timeout context for API call
91117 apiCtx , cancel := context .WithTimeout (ctx , 30 * time .Second )
92118 defer cancel ()
93119
94- var retryErr error
95- orgs , resp , retryErr = app .client .Organizations .List (apiCtx , user , opts )
96- if retryErr != nil {
97- slog .Debug ("[SPRINKLER] Organizations.List failed (will retry)" , "error" , retryErr , "page" , opts .Page )
98- return retryErr
120+ var err error
121+ page , resp , err = app .client .Organizations .List (apiCtx , user , opts )
122+ if err != nil {
123+ slog .Debug ("[SPRINKLER] Organizations.List failed (will retry)" , "error" , err , "page" , opts .Page )
124+ return err
99125 }
100126 return nil
101127 },
@@ -115,9 +141,9 @@ func (app *App) initSprinklerOrgs(ctx context.Context) error {
115141 return nil // Return nil to avoid blocking startup
116142 }
117143
118- for _ , org := range orgs {
119- if org .Login != nil {
120- allOrgs = append (allOrgs , * org .Login )
144+ for _ , o := range page {
145+ if o .Login != nil {
146+ orgs = append (orgs , * o .Login )
121147 }
122148 }
123149
@@ -129,12 +155,12 @@ func (app *App) initSprinklerOrgs(ctx context.Context) error {
129155
130156 slog .Info ("[SPRINKLER] Discovered user organizations" ,
131157 "user" , user ,
132- "orgs" , allOrgs ,
133- "count" , len (allOrgs ))
158+ "orgs" , orgs ,
159+ "count" , len (orgs ))
134160
135161 // Update sprinkler with all orgs at once
136- if len (allOrgs ) > 0 {
137- app .sprinklerMonitor .updateOrgs (allOrgs )
162+ if len (orgs ) > 0 {
163+ app .sprinklerMonitor .updateOrgs (orgs )
138164 if err := app .sprinklerMonitor .start (ctx ); err != nil {
139165 return fmt .Errorf ("start sprinkler: %w" , err )
140166 }
@@ -387,79 +413,78 @@ func (app *App) fetchPRsInternal(ctx context.Context) (incoming []PR, outgoing [
387413 searchStart := time .Now ()
388414
389415 // Run both queries in parallel
390- type queryResult struct {
416+ type qResult struct {
391417 err error
392418 query string
393419 issues []* github.Issue
394420 }
395421
396- queryResults := make (chan queryResult , 2 )
422+ results := make (chan qResult , 2 )
397423
398424 // Query 1: PRs involving the user
399425 go func () {
400- query := fmt .Sprintf ("is:open is:pr involves:%s archived:false" , user )
401- slog .Debug ("[GITHUB] Searching for PRs" , "query" , query )
426+ q := fmt .Sprintf ("is:open is:pr involves:%s archived:false" , user )
427+ slog .Debug ("[GITHUB] Searching for PRs" , "query" , q )
402428
403- result , err := app .executeGitHubQuery (ctx , query , opts )
429+ res , err := app .executeGitHubQuery (ctx , q , opts )
404430 if err != nil {
405- queryResults <- queryResult {err : err , query : query }
431+ results <- qResult {err : err , query : q }
406432 } else {
407- queryResults <- queryResult {issues : result .Issues , query : query }
433+ results <- qResult {issues : res .Issues , query : q }
408434 }
409435 }()
410436
411437 // Query 2: PRs in user-owned repos with no reviewers
412438 go func () {
413- query := fmt .Sprintf ("is:open is:pr user:%s review:none archived:false" , user )
414- slog .Debug ("[GITHUB] Searching for PRs" , "query" , query )
439+ q := fmt .Sprintf ("is:open is:pr user:%s review:none archived:false" , user )
440+ slog .Debug ("[GITHUB] Searching for PRs" , "query" , q )
415441
416- result , err := app .executeGitHubQuery (ctx , query , opts )
442+ res , err := app .executeGitHubQuery (ctx , q , opts )
417443 if err != nil {
418- queryResults <- queryResult {err : err , query : query }
444+ results <- qResult {err : err , query : q }
419445 } else {
420- queryResults <- queryResult {issues : result .Issues , query : query }
446+ results <- qResult {issues : res .Issues , query : q }
421447 }
422448 }()
423449
424450 // Collect results from both queries
425- var allIssues []* github.Issue
426- seenURLs := make (map [string ]bool )
427- var queryErrors []error
451+ var issues []* github.Issue
452+ seen := make (map [string ]bool )
453+ var errs []error
428454
429455 for range 2 {
430- result := <- queryResults
431- if result .err != nil {
432- slog .Error ("[GITHUB] Query failed" , "query" , result .query , "error" , result .err )
433- queryErrors = append (queryErrors , result .err )
434- // Continue processing other query results even if one fails
456+ r := <- results
457+ if r .err != nil {
458+ slog .Error ("[GITHUB] Query failed" , "query" , r .query , "error" , r .err )
459+ errs = append (errs , r .err )
435460 continue
436461 }
437- slog .Debug ("[GITHUB] Query completed" , "query" , result .query , "prCount" , len (result .issues ))
462+ slog .Debug ("[GITHUB] Query completed" , "query" , r .query , "prCount" , len (r .issues ))
438463
439464 // Deduplicate PRs based on URL
440- for _ , issue := range result .issues {
465+ for _ , issue := range r .issues {
441466 url := issue .GetHTMLURL ()
442- if ! seenURLs [url ] {
443- seenURLs [url ] = true
444- allIssues = append (allIssues , issue )
467+ if ! seen [url ] {
468+ seen [url ] = true
469+ issues = append (issues , issue )
445470 }
446471 }
447472 }
448- slog .Info ("[GITHUB] Both searches completed" , "duration" , time .Since (searchStart ), "uniquePRs" , len (allIssues ))
473+ slog .Info ("[GITHUB] Both searches completed" , "duration" , time .Since (searchStart ), "uniquePRs" , len (issues ))
449474
450475 // If both queries failed, return an error
451- if len (queryErrors ) == 2 {
452- return nil , nil , fmt .Errorf ("all GitHub queries failed: %v" , queryErrors )
476+ if len (errs ) == 2 {
477+ return nil , nil , fmt .Errorf ("all GitHub queries failed: %v" , errs )
453478 }
454479
455480 // Limit PRs for performance
456- if len (allIssues ) > maxPRsToProcess {
457- slog .Info ("Limiting PRs for performance" , "limit" , maxPRsToProcess , "total" , len (allIssues ))
458- allIssues = allIssues [:maxPRsToProcess ]
481+ if len (issues ) > maxPRsToProcess {
482+ slog .Info ("Limiting PRs for performance" , "limit" , maxPRsToProcess , "total" , len (issues ))
483+ issues = issues [:maxPRsToProcess ]
459484 }
460485
461486 // Process GitHub results immediately
462- for _ , issue := range allIssues {
487+ for _ , issue := range issues {
463488 if ! issue .IsPullRequest () {
464489 continue
465490 }
@@ -503,7 +528,7 @@ func (app *App) fetchPRsInternal(ctx context.Context) (incoming []PR, outgoing [
503528
504529 // Fetch Turn API data
505530 // Always synchronous now for simplicity - Turn API calls are fast with caching
506- app .fetchTurnDataSync (ctx , allIssues , user , & incoming , & outgoing )
531+ app .fetchTurnDataSync (ctx , issues , user , & incoming , & outgoing )
507532
508533 return incoming , outgoing , nil
509534}
0 commit comments