Merge remote-tracking branch 'origin/master' into pr-create

This commit is contained in:
Mislav Marohnić 2019-11-13 19:31:02 +01:00
commit 933086bae9
4 changed files with 196 additions and 25 deletions

View file

@ -17,6 +17,99 @@ type PullRequest struct {
State string
URL string
HeadRefName string
Reviews struct {
Nodes []struct {
State string
Author struct {
Login string
}
}
}
Commits struct {
Nodes []struct {
Commit struct {
Status struct {
Contexts []struct {
State string
}
}
CheckSuites struct {
Nodes []struct {
CheckRuns struct {
Nodes []struct {
Conclusion string
}
}
}
}
}
}
}
}
type PullRequestReviewStatus struct {
ChangesRequested bool
Approved bool
}
func (pr *PullRequest) ReviewStatus() PullRequestReviewStatus {
status := PullRequestReviewStatus{}
reviewMap := map[string]string{}
// Reviews will include every review on record, including consecutive ones
// from the same actor. Consolidate them into latest state per reviewer.
for _, review := range pr.Reviews.Nodes {
reviewMap[review.Author.Login] = review.State
}
for _, state := range reviewMap {
switch state {
case "CHANGES_REQUESTED":
status.ChangesRequested = true
case "APPROVED":
status.Approved = true
}
}
return status
}
type PullRequestChecksStatus struct {
Pending int
Failing int
Passing int
Total int
}
func (pr *PullRequest) ChecksStatus() (summary PullRequestChecksStatus) {
if len(pr.Commits.Nodes) == 0 {
return
}
commit := pr.Commits.Nodes[0].Commit
for _, status := range commit.Status.Contexts {
switch status.State {
case "SUCCESS":
summary.Passing++
case "EXPECTED", "ERROR", "FAILURE":
summary.Failing++
case "PENDING":
summary.Pending++
default:
panic(fmt.Errorf("unsupported status: %q", status.State))
}
summary.Total++
}
for _, checkSuite := range commit.CheckSuites.Nodes {
for _, checkRun := range checkSuite.CheckRuns.Nodes {
switch checkRun.Conclusion {
case "SUCCESS", "NEUTRAL":
summary.Passing++
case "FAILURE", "CANCELLED", "TIMED_OUT", "ACTION_REQUIRED":
summary.Failing++
default:
panic(fmt.Errorf("unsupported check conclusion: %q", checkRun.Conclusion))
}
summary.Total++
}
}
return
}
type Repo interface {
@ -147,19 +240,50 @@ func PullRequests(client *Client, ghRepo Repo, currentBranch, currentUsername st
}
query := `
fragment pr on PullRequest {
number
title
url
headRefName
}
fragment pr on PullRequest {
number
title
url
headRefName
commits(last: 1) {
nodes {
commit {
status {
contexts {
state
}
}
checkSuites(first: 50) {
nodes {
checkRuns(first: 50) {
nodes {
conclusion
}
}
}
}
}
}
}
}
fragment prWithReviews on PullRequest {
...pr
reviews(last: 20) {
nodes {
state
author {
login
}
}
}
}
query($owner: String!, $repo: String!, $headRefName: String!, $viewerQuery: String!, $reviewerQuery: String!, $per_page: Int = 10) {
repository(owner: $owner, name: $repo) {
pullRequests(headRefName: $headRefName, states: OPEN, first: 1) {
edges {
node {
...pr
...prWithReviews
}
}
}
@ -167,7 +291,7 @@ func PullRequests(client *Client, ghRepo Repo, currentBranch, currentUsername st
viewerCreated: search(query: $viewerQuery, type: ISSUE, first: $per_page) {
edges {
node {
...pr
...prWithReviews
}
}
pageInfo {

View file

@ -249,7 +249,37 @@ func prView(cmd *cobra.Command, args []string) error {
func printPrs(prs ...api.PullRequest) {
for _, pr := range prs {
fmt.Printf(" #%d %s %s\n", pr.Number, truncate(50, pr.Title), utils.Cyan("["+pr.HeadRefName+"]"))
prNumber := fmt.Sprintf("#%d", pr.Number)
fmt.Printf(" %s %s %s", utils.Yellow(prNumber), truncate(50, pr.Title), utils.Cyan("["+pr.HeadRefName+"]"))
checks := pr.ChecksStatus()
reviews := pr.ReviewStatus()
if checks.Total > 0 || reviews.ChangesRequested || reviews.Approved {
fmt.Printf("\n ")
}
if checks.Total > 0 {
var ratio string
if checks.Failing > 0 {
ratio = fmt.Sprintf("%d/%d", checks.Passing, checks.Total)
ratio = utils.Red(ratio)
} else if checks.Pending > 0 {
ratio = fmt.Sprintf("%d/%d", checks.Passing, checks.Total)
ratio = utils.Yellow(ratio)
} else if checks.Passing == checks.Total {
ratio = fmt.Sprintf("%d", checks.Total)
ratio = utils.Green(ratio)
}
fmt.Printf(" - checks: %s", ratio)
}
if reviews.ChangesRequested {
fmt.Printf(" - %s", utils.Red("changes requested"))
} else if reviews.Approved {
fmt.Printf(" - %s", utils.Green("approved"))
}
fmt.Printf("\n")
}
}

View file

@ -72,7 +72,9 @@ var apiClientForContext = func(ctx context.Context) (*api.Client, error) {
opts := []api.ClientOption{
api.AddHeader("Authorization", fmt.Sprintf("token %s", token)),
api.AddHeader("User-Agent", fmt.Sprintf("GitHub CLI %s", Version)),
api.AddHeader("Accept", "application/vnd.github.shadow-cat-preview+json"),
// antiope-preview: Checks
// shadow-cat-preview: Draft pull requests
api.AddHeader("Accept", "application/vnd.github.antiope-preview+json, application/vnd.github.shadow-cat-preview"),
}
if verbose := os.Getenv("DEBUG"); verbose != "" {
opts = append(opts, api.VerboseLog(os.Stderr))

View file

@ -1,24 +1,39 @@
package utils
import "github.com/mgutz/ansi"
import (
"github.com/mattn/go-isatty"
"github.com/mgutz/ansi"
"os"
)
var Black = ansi.ColorFunc("black")
var White = ansi.ColorFunc("white")
func makeColorFunc(color string) func(string) string {
return func(arg string) string {
output := arg
if isatty.IsTerminal(os.Stdout.Fd()) {
output = ansi.Color(color+arg+ansi.Reset, "")
}
func Gray(arg string) string {
return ansi.Color(ansi.LightBlack+arg, "")
return output
}
}
var Red = ansi.ColorFunc("red")
var Green = ansi.ColorFunc("green")
var Yellow = ansi.ColorFunc("yellow")
var Blue = ansi.ColorFunc("blue")
var Magenta = ansi.ColorFunc("magenta")
var Cyan = ansi.ColorFunc("cyan")
var Black = makeColorFunc(ansi.Black)
var White = makeColorFunc(ansi.White)
var Magenta = makeColorFunc(ansi.Magenta)
var Cyan = makeColorFunc(ansi.Cyan)
var Red = makeColorFunc(ansi.Red)
var Yellow = makeColorFunc(ansi.Yellow)
var Blue = makeColorFunc(ansi.Blue)
var Green = makeColorFunc(ansi.Green)
var Gray = makeColorFunc(ansi.LightBlack)
func Bold(arg string) string {
// This is really annoying. If you just define Bold as ColorFunc("+b") it will properly bold but
// will not use the default color, resulting in black and probably unreadable text. This forces
// the default color before bolding.
return ansi.Color(ansi.DefaultFG+arg, "+b")
output := arg
if isatty.IsTerminal(os.Stdout.Fd()) {
// This is really annoying. If you just define Bold as ColorFunc("+b") it will properly bold but
// will not use the default color, resulting in black and probably unreadable text. This forces
// the default color before bolding.
output = ansi.Color(ansi.DefaultFG+arg+ansi.Reset, "+b")
}
return output
}