func evalWorkflow(client *circleci.Client, wfID string, jobName string) (anyRunning bool, anyFailed bool, anyBlocked bool, err error) {
	fmt.Printf("%s: polling for jobs: ", time.Now().Format(time.StampMilli))
	wfJobs, err := getJobs(client, wfID)
	if err != nil {
		fmt.Printf("error polling: %s\n", err.Error())
		return true, true, false, err
	}
	fmt.Println(summarizeJobList(wfJobs))
	anyRunning = false
	anyBlocked = false
	for _, job := range wfJobs {
		if job.Name == jobName {
			continue
		}
		switch job.Status {
		case "success":
			continue
		case "blocked":
			anyBlocked = true
			continue
		case "queued":
			anyRunning = true
		case "failed":
			anyFailed = true
			continue
		case "running":
			anyRunning = true
		}
	}
	return anyRunning, anyFailed, anyBlocked, nil
}
evalWorkflow looks at the CircleCI API for the list of jobs in this workflow
and decides whether the build has finished and if finished, whether it
failed. If an error is returned, it represents an error talking to the
CircleCI API, not an error with the workflow.
package main
import (
	"context"
	"fmt"
	"os"
	"sort"
	"strconv"
	"strings"
	"time"
	circleci "github.com/jszwedko/go-circleci"
	"github.com/spf13/cobra"
	libhoney "github.com/honeycombio/libhoney-go"
)
const numChecks = 24
type watchConfig struct {
	timeoutMin int
	circleKey  string
	workflowID string
	jobName    string
}
func commandWatch(cfg *libhoney.Config, filename *string, ciProvider *string, wcfg *watchConfig) *cobra.Command {
	watchCmd := &cobra.Command{
		Use:   "watch BUILD_ID",
		Short: "Polls the CircleCI API and waits until all jobs have finished.",
		Long: `
Polls the CircleCI API and waits until all jobs have finished (either
succeeded, failed, or are blocked). It then reports the final status of the
build with the appropriate timers.`,
		Args: composer(
			cobra.ExactArgs(1),
			func(cmd *cobra.Command, args []string) error {
				if *ciProvider != providerCircle {
					return fmt.Errorf("watch command only valid for %s", providerCircle)
				}
				return nil
			},
		),
		RunE: func(cmd *cobra.Command, args []string) error {
			traceID := strings.TrimSpace(args[0])
			ev := createEvent(cfg, *ciProvider, traceID)
			defer ev.Send()
			providerInfo(*ciProvider, ev)
			ok, startTime, endTime, err := waitCircle(context.Background(), *wcfg)
			if err != nil {
				fmt.Printf("buildevents - Error detected: %s\n", err.Error())
				return err
			}
			status := "failed"
			if ok {
				status = "success"
			}
			ev.Add(map[string]interface{}{
				"service_name":  "watch",
				"trace.span_id": traceID,
				"name":          "watch " + traceID,
				"status":        status,
				"duration_ms":   endTime.Sub(startTime) / time.Millisecond,
			})
			ev.Timestamp = startTime
			arbitraryFields(*filename, ev)  
			url, err := buildURL(cfg, traceID, startTime.Unix())
			if err != nil {
				fmt.Fprintf(os.Stderr, "Unable to create trace URL: %v\n", err)
				return err
			}
			fmt.Println(url)
			return nil
		},
	}
	watchCmd.Flags().IntVarP(&wcfg.timeoutMin, "timeout", "t", 10, "[env.BUILDEVENT_TIMEOUT] maximum time (in minutes) that watch should wait before timing out")
	if ts, ok := os.LookupEnv("BUILDEVENT_TIMEOUT"); ok {
		if _, err := strconv.Atoi(ts); err == nil {
			watchCmd.Flags().Lookup("timeout").Value.Set(ts)
		}
	}
	watchCmd.Flags().StringVarP(&wcfg.circleKey, "circlekey", "c", "", "[env.BUILDEVENT_CIRCLE_API_TOKEN] CircleCI API token used for watching builds for private repositories")
	if tok, ok := os.LookupEnv("BUILDEVENT_CIRCLE_API_TOKEN"); ok {
		watchCmd.Flags().Lookup("circlekey").Value.Set(tok)
	}
	watchCmd.Flags().StringVarP(&wcfg.workflowID, "workflowid", "w", "", "[env.CIRCLE_WORKFLOW_ID] CircleCI identifier for the current workflow")
	if wfid, ok := os.LookupEnv("CIRCLE_WORKFLOW_ID"); ok {
		watchCmd.Flags().Lookup("workflowid").Value.Set(wfid)
	}
	watchCmd.Flags().StringVarP(&wcfg.jobName, "jobname", "j", "", "[env.CIRCLE_JOB] CircleCI identifier for the current job")
	if jnm, ok := os.LookupEnv("CIRCLE_JOB"); ok {
		watchCmd.Flags().Lookup("jobname").Value.Set(jnm)
	}
	return watchCmd
}
func waitCircle(parent context.Context, cfg watchConfig) (passed bool, started, ended time.Time, err error) {
	if cfg.circleKey == "" {
		return false, time.Now(), time.Now().Add(time.Second), fmt.Errorf("circle token required to poll the API")
	}
	client := &circleci.Client{Token: cfg.circleKey}
	wf, err := client.GetWorkflowV2(cfg.workflowID)
	if err != nil {
		return false, time.Now(), time.Now().Add(time.Second), err
	}
	started = wf.CreatedAt
	ended = time.Now()  
	done := make(chan struct{})
	ctx, cxl := context.WithTimeout(parent, time.Duration(cfg.timeoutMin)*time.Minute)
	defer cxl()
	checksLeft := numChecks + 1  
	go func() {
		defer close(done)
		tk := time.NewTicker(5 * time.Second).C
		for range tk {
			select {
			case <-ctx.Done():
				fmt.Fprintf(os.Stderr, "Timeout reached waiting for the workflow to finish\n")
				ended = time.Now()
				return
			default:
			}
			anyRunning, anyFailed, anyBlocked, err := evalWorkflow(client, cfg.workflowID, cfg.jobName)
			if !anyRunning {
				if checksLeft >= numChecks {
					ended = time.Now()
				}
				if !anyBlocked && err == nil {
					passed = !anyFailed
					if passed {
						fmt.Println("Build passed!")
					} else {
						fmt.Println("Build failed!")
					}
					return
				}
				checksLeft--
				if checksLeft <= 0 {
					passed = !anyFailed
					if passed {
						fmt.Println("Build passed!")
					} else {
						fmt.Println("Build failed!")
					}
					return
				}
				if err != nil {
					fmt.Printf("Querying the CirlceCI API failed with %s; trying %d more times before giving up.\n", err.Error(), checksLeft)
					continue
				}
				if anyFailed {
					fmt.Printf("Build failed!\n")
					ended = time.Now()
					return
				}
				fmt.Printf("Build appears finished; checking %d more times to make sure.\n", checksLeft)
				continue
			}
			passed = false
			checksLeft = numChecks
		}
	}()
	<-done
	return passed, started, ended, nil
}