* pr-checks: extract webMode * pr-checks: extract checks information collection * pr-checks: extract output utilities * pr-checks: implement watch flag * pr-checks: remove SIGINT interceptor * pr-checks: exit with error if some task has failed * update flags help text * update default interval to 10s * move interval flag parse to RunE * refactor checksRunWatchMode to use infinite loop * Refactor printTable function * Refactor collect function * Set up checksRun to use new refactored functions and simplify logic a bit * Add tests * Always set interval in opts * use Duration flag * Revert back to using int flag for consistency with run watch * Use run watch screen clearing mechanism * Re-add pager support Co-authored-by: Sam Coe <samcoe@users.noreply.github.com>
110 lines
2.5 KiB
Go
110 lines
2.5 KiB
Go
package checks
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
"time"
|
|
|
|
"github.com/cli/cli/v2/api"
|
|
)
|
|
|
|
type check struct {
|
|
Name string `json:"name"`
|
|
State string `json:"state"`
|
|
StartedAt time.Time `json:"startedAt"`
|
|
CompletedAt time.Time `json:"completedAt"`
|
|
Link string `json:"link"`
|
|
Bucket string `json:"bucket"`
|
|
}
|
|
|
|
type checkCounts struct {
|
|
Failed int
|
|
Passed int
|
|
Pending int
|
|
Skipping int
|
|
}
|
|
|
|
func aggregateChecks(pr *api.PullRequest) ([]check, checkCounts, error) {
|
|
checks := []check{}
|
|
counts := checkCounts{}
|
|
|
|
if len(pr.StatusCheckRollup.Nodes) == 0 {
|
|
return checks, counts, fmt.Errorf("no commit found on the pull request")
|
|
}
|
|
|
|
rollup := pr.StatusCheckRollup.Nodes[0].Commit.StatusCheckRollup.Contexts.Nodes
|
|
if len(rollup) == 0 {
|
|
return checks, counts, fmt.Errorf("no checks reported on the '%s' branch", pr.HeadRefName)
|
|
}
|
|
|
|
checkContexts := pr.StatusCheckRollup.Nodes[0].Commit.StatusCheckRollup.Contexts.Nodes
|
|
for _, c := range eliminateDuplicates(checkContexts) {
|
|
state := c.State
|
|
if state == "" {
|
|
if c.Status == "COMPLETED" {
|
|
state = c.Conclusion
|
|
} else {
|
|
state = c.Status
|
|
}
|
|
}
|
|
|
|
link := c.DetailsURL
|
|
if link == "" {
|
|
link = c.TargetURL
|
|
}
|
|
|
|
name := c.Name
|
|
if name == "" {
|
|
name = c.Context
|
|
}
|
|
|
|
item := check{
|
|
Name: name,
|
|
State: state,
|
|
StartedAt: c.StartedAt,
|
|
CompletedAt: c.CompletedAt,
|
|
Link: link,
|
|
}
|
|
switch state {
|
|
case "SUCCESS":
|
|
item.Bucket = "pass"
|
|
counts.Passed++
|
|
case "SKIPPED", "NEUTRAL":
|
|
item.Bucket = "skipping"
|
|
counts.Skipping++
|
|
case "ERROR", "FAILURE", "CANCELLED", "TIMED_OUT", "ACTION_REQUIRED":
|
|
item.Bucket = "fail"
|
|
counts.Failed++
|
|
default: // "EXPECTED", "REQUESTED", "WAITING", "QUEUED", "PENDING", "IN_PROGRESS", "STALE"
|
|
item.Bucket = "pending"
|
|
counts.Pending++
|
|
}
|
|
|
|
checks = append(checks, item)
|
|
}
|
|
|
|
return checks, counts, nil
|
|
}
|
|
|
|
func eliminateDuplicates(checkContexts []api.CheckContext) []api.CheckContext {
|
|
// To return the most recent check, sort in descending order by StartedAt.
|
|
sort.Slice(checkContexts, func(i, j int) bool { return checkContexts[i].StartedAt.After(checkContexts[j].StartedAt) })
|
|
|
|
m := make(map[string]struct{})
|
|
unique := make([]api.CheckContext, 0, len(checkContexts))
|
|
|
|
// Eliminate duplicates using Name or Context.
|
|
for _, ctx := range checkContexts {
|
|
key := ctx.Name
|
|
if key == "" {
|
|
key = ctx.Context
|
|
}
|
|
if _, ok := m[key]; ok {
|
|
continue
|
|
}
|
|
unique = append(unique, ctx)
|
|
m[key] = struct{}{}
|
|
}
|
|
|
|
return unique
|
|
}
|