@@ -44,8 +44,19 @@ func (app *App) initClients(ctx context.Context) error {
4444 return nil
4545}
4646
47- // githubToken retrieves the GitHub token using gh CLI.
47+ // githubToken retrieves the GitHub token from GITHUB_TOKEN env var or gh CLI.
4848func (* App ) githubToken (ctx context.Context ) (string , error ) {
49+ // First check for GITHUB_TOKEN environment variable
50+ if token := os .Getenv ("GITHUB_TOKEN" ); token != "" {
51+ token = strings .TrimSpace (token )
52+ if err := validateGitHubToken (token ); err != nil {
53+ return "" , fmt .Errorf ("invalid GITHUB_TOKEN: %w" , err )
54+ }
55+ log .Println ("Using GitHub token from GITHUB_TOKEN environment variable" )
56+ return token , nil
57+ }
58+
59+ // Fall back to gh CLI if GITHUB_TOKEN is not set
4960 // Only check absolute paths for security - never use PATH
5061 var trustedPaths []string
5162 switch runtime .GOOS {
@@ -85,8 +96,11 @@ func (*App) githubToken(ctx context.Context) (string, error) {
8596 const executableMask = 0o111
8697 if info .Mode ().IsRegular () && info .Mode ()& executableMask != 0 {
8798 // Verify it's actually the gh binary by running version command
88- cmd := exec .Command (path , "version" ) //nolint:noctx // Quick version check doesn't need context
99+ // Use timeout to prevent hanging
100+ versionCtx , cancel := context .WithTimeout (ctx , 2 * time .Second )
101+ cmd := exec .CommandContext (versionCtx , path , "version" )
89102 output , err := cmd .Output ()
103+ cancel () // Call cancel immediately after command execution
90104 if err == nil && strings .Contains (string (output ), "gh version" ) {
91105 log .Printf ("Found and verified gh at: %s" , path )
92106 ghPath = path
@@ -97,25 +111,21 @@ func (*App) githubToken(ctx context.Context) (string, error) {
97111 }
98112
99113 if ghPath == "" {
100- return "" , errors .New ("gh cli not found in trusted locations" )
114+ return "" , errors .New ("gh cli not found in trusted locations and GITHUB_TOKEN not set " )
101115 }
102116
103117 log .Printf ("Executing command: %s auth token" , ghPath )
104118 cmd := exec .CommandContext (ctx , ghPath , "auth" , "token" )
105119 output , err := cmd .CombinedOutput ()
106120 if err != nil {
107- log .Printf ("gh command failed with output : %s " , string ( output ) )
108- return "" , fmt .Errorf ("exec 'gh auth token': %w (output: %s) " , err , string ( output ) )
121+ log .Printf ("gh command failed: %v " , err )
122+ return "" , fmt .Errorf ("exec 'gh auth token': %w" , err )
109123 }
110124 token := strings .TrimSpace (string (output ))
111- if token == "" {
112- return "" , errors .New ("empty github token" )
113- }
114- const minTokenLength = 20
115- if len (token ) < minTokenLength {
116- return "" , fmt .Errorf ("invalid github token length: %d" , len (token ))
125+ if err := validateGitHubToken (token ); err != nil {
126+ return "" , fmt .Errorf ("invalid token from gh CLI: %w" , err )
117127 }
118- log .Println ("Successfully obtained GitHub token" )
128+ log .Println ("Successfully obtained GitHub token from gh CLI " )
119129 return token , nil
120130}
121131
@@ -200,9 +210,9 @@ func (app *App) fetchPRsInternal(ctx context.Context, waitForTurn bool) (incomin
200210
201211 // Run both queries in parallel
202212 type queryResult struct {
213+ err error
203214 query string
204215 issues []* github.Issue
205- err error
206216 }
207217
208218 queryResults := make (chan queryResult , 2 )
@@ -236,11 +246,13 @@ func (app *App) fetchPRsInternal(ctx context.Context, waitForTurn bool) (incomin
236246 // Collect results from both queries
237247 var allIssues []* github.Issue
238248 seenURLs := make (map [string ]bool )
249+ var queryErrors []error
239250
240251 for range 2 {
241252 result := <- queryResults
242253 if result .err != nil {
243254 log .Printf ("[GITHUB] Query failed: %s - %v" , result .query , result .err )
255+ queryErrors = append (queryErrors , result .err )
244256 // Continue processing other query results even if one fails
245257 continue
246258 }
@@ -257,6 +269,11 @@ func (app *App) fetchPRsInternal(ctx context.Context, waitForTurn bool) (incomin
257269 }
258270 log .Printf ("[GITHUB] Both searches completed in %v, found %d unique PRs" , time .Since (searchStart ), len (allIssues ))
259271
272+ // If both queries failed, return an error
273+ if len (queryErrors ) == 2 {
274+ return nil , nil , fmt .Errorf ("all GitHub queries failed: %v" , queryErrors )
275+ }
276+
260277 // Limit PRs for performance
261278 if len (allIssues ) > maxPRsToProcess {
262279 log .Printf ("Limiting to %d PRs for performance (total: %d)" , maxPRsToProcess , len (allIssues ))
0 commit comments