cli/pkg/cmd/pr/checks/aggregate.go
endorama 825328031f
Add watch functionality to pr checks command (#4519)
* 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>
2022-02-08 08:33:42 +01:00

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
}