getLatestWorkflowRunWithRetry gets information about the most recent run of the specified workflow with retry logic to handle timing issues when a workflow has just been triggered
(lockFileName string, repo string, verbose bool)
| 27 | // getLatestWorkflowRunWithRetry gets information about the most recent run of the specified workflow |
| 28 | // with retry logic to handle timing issues when a workflow has just been triggered |
| 29 | func getLatestWorkflowRunWithRetry(lockFileName string, repo string, verbose bool) (*WorkflowRunInfo, error) { |
| 30 | runWorkflowTrackingLog.Printf("Getting latest workflow run: workflow=%s, repo=%s, max_retries=6", lockFileName, repo) |
| 31 | const maxRetries = 6 |
| 32 | const initialDelay = 2 * time.Second |
| 33 | const maxDelay = 10 * time.Second |
| 34 | |
| 35 | if repo != "" { |
| 36 | console.LogVerbose(verbose, fmt.Sprintf("Getting latest run for workflow: %s in repo: %s (with retry logic)", lockFileName, repo)) |
| 37 | } else { |
| 38 | console.LogVerbose(verbose, fmt.Sprintf("Getting latest run for workflow: %s (with retry logic)", lockFileName)) |
| 39 | } |
| 40 | |
| 41 | // Capture the current time before we start polling |
| 42 | // This helps us identify runs that were created after the workflow was triggered |
| 43 | startTime := time.Now().UTC() |
| 44 | runWorkflowTrackingLog.Printf("Start time for polling: %s", startTime.Format(time.RFC3339)) |
| 45 | |
| 46 | // Create spinner outside the loop so we can update it |
| 47 | var spinner *console.SpinnerWrapper |
| 48 | if !verbose { |
| 49 | spinner = console.NewSpinner("Waiting for workflow run to appear...") |
| 50 | } |
| 51 | |
| 52 | var lastErr error |
| 53 | for attempt := range maxRetries { |
| 54 | if attempt > 0 { |
| 55 | // Calculate delay with exponential backoff, capped at maxDelay |
| 56 | delay := min(time.Duration(attempt)*initialDelay, maxDelay) |
| 57 | |
| 58 | // Calculate elapsed time since start |
| 59 | elapsed := time.Since(startTime).Round(time.Second) |
| 60 | |
| 61 | console.LogVerbose(verbose, fmt.Sprintf("Waiting %v before retry attempt %d/%d...", delay, attempt+1, maxRetries)) |
| 62 | |
| 63 | if !verbose { |
| 64 | // Show spinner starting from second attempt to avoid flickering |
| 65 | if attempt == 1 && spinner != nil { |
| 66 | spinner.Start() |
| 67 | } |
| 68 | // Update spinner with progress information |
| 69 | if spinner != nil { |
| 70 | spinner.UpdateMessage(fmt.Sprintf("Waiting for workflow run... (attempt %d/%d, %v elapsed)", attempt+1, maxRetries, elapsed)) |
| 71 | } |
| 72 | } |
| 73 | time.Sleep(delay) |
| 74 | } |
| 75 | |
| 76 | // Build command with optional repo parameter |
| 77 | var cmd *exec.Cmd |
| 78 | if repo != "" { |
| 79 | cmd = workflow.ExecGH("run", "list", "--repo", repo, "--workflow", lockFileName, "--limit", "1", "--json", "url,databaseId,status,conclusion,createdAt") |
| 80 | } else { |
| 81 | cmd = workflow.ExecGH("run", "list", "--workflow", lockFileName, "--limit", "1", "--json", "url,databaseId,status,conclusion,createdAt") |
| 82 | } |
| 83 | |
| 84 | output, err := cmd.Output() |
| 85 | if err != nil { |
| 86 | lastErr = fmt.Errorf("failed to get workflow runs: %w", err) |