(ctx context.Context, resource string, verbose bool)
| 256 | } |
| 257 | |
| 258 | func waitForOrgRateLimit(ctx context.Context, resource string, verbose bool) error { |
| 259 | output, err := workflow.RunGHContext(ctx, "Checking rate limit...", "api", "rate_limit") |
| 260 | if err != nil { |
| 261 | return err |
| 262 | } |
| 263 | |
| 264 | var response orgRateLimitResponse |
| 265 | if err := json.Unmarshal(output, &response); err != nil { |
| 266 | return err |
| 267 | } |
| 268 | |
| 269 | limit := response.Resources.Core |
| 270 | buffer := orgUpdateCoreBuffer |
| 271 | if resource == "search" { |
| 272 | limit = response.Resources.Search |
| 273 | buffer = orgUpdateSearchBuffer |
| 274 | } |
| 275 | if limit.Limit == 0 { |
| 276 | return nil |
| 277 | } |
| 278 | |
| 279 | orgUpdateLog.Printf("GitHub %s rate limit: %d/%d remaining (reset at %s)", resource, limit.Remaining, limit.Limit, time.Unix(limit.Reset, 0).Format(time.RFC3339)) |
| 280 | |
| 281 | // Critical level: once consumption reaches limit-1000 API units, stop processing |
| 282 | // additional work rather than risking quota exhaustion or a long reset wait. The |
| 283 | // search resource has a tiny quota, so the critical threshold only applies to core. |
| 284 | if resource != "search" { |
| 285 | criticalRemaining := limit.Limit - orgUpdateCriticalConsumed |
| 286 | if criticalRemaining > buffer && limit.Remaining <= criticalRemaining { |
| 287 | orgUpdateLog.Printf("GitHub %s rate limit critical: %d/%d remaining (<= %d)", resource, limit.Remaining, limit.Limit, criticalRemaining) |
| 288 | fmt.Fprintln(os.Stderr, console.FormatWarningMessage( |
| 289 | fmt.Sprintf("GitHub %s API budget critical: %d/%d remaining (consumed %d API units)", resource, limit.Remaining, limit.Limit, limit.Limit-limit.Remaining), |
| 290 | )) |
| 291 | return errOrgRateLimitCritical |
| 292 | } |
| 293 | } |
| 294 | |
| 295 | if limit.Remaining <= buffer { |
| 296 | resetAt := time.Unix(limit.Reset, 0) |
| 297 | waitFor := time.Until(resetAt) + rateLimitResetBuffer |
| 298 | if waitFor > 0 { |
| 299 | fmt.Fprintln(os.Stderr, console.FormatWarningMessage( |
| 300 | fmt.Sprintf("Applying a %s delay to avoid reaching the GitHub %s rate limit (%d/%d remaining)", waitFor.Round(time.Second), resource, limit.Remaining, limit.Limit), |
| 301 | )) |
| 302 | timer := time.NewTimer(waitFor) |
| 303 | defer timer.Stop() |
| 304 | select { |
| 305 | case <-ctx.Done(): |
| 306 | return ctx.Err() |
| 307 | case <-timer.C: |
| 308 | } |
| 309 | return nil |
| 310 | } |
| 311 | } |
| 312 | |
| 313 | if verbose { |
| 314 | fmt.Fprintln(os.Stderr, console.FormatVerboseMessage( |
| 315 | fmt.Sprintf("GitHub %s rate limit OK: %d/%d remaining", resource, limit.Remaining, limit.Limit), |
nothing calls this directly
no test coverage detected