Merge remote-tracking branch 'origin/master' into pr-create
This commit is contained in:
commit
933086bae9
4 changed files with 196 additions and 25 deletions
140
api/queries.go
140
api/queries.go
|
|
@ -17,6 +17,99 @@ type PullRequest struct {
|
||||||
State string
|
State string
|
||||||
URL string
|
URL string
|
||||||
HeadRefName 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 {
|
type Repo interface {
|
||||||
|
|
@ -147,19 +240,50 @@ func PullRequests(client *Client, ghRepo Repo, currentBranch, currentUsername st
|
||||||
}
|
}
|
||||||
|
|
||||||
query := `
|
query := `
|
||||||
fragment pr on PullRequest {
|
fragment pr on PullRequest {
|
||||||
number
|
number
|
||||||
title
|
title
|
||||||
url
|
url
|
||||||
headRefName
|
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) {
|
query($owner: String!, $repo: String!, $headRefName: String!, $viewerQuery: String!, $reviewerQuery: String!, $per_page: Int = 10) {
|
||||||
repository(owner: $owner, name: $repo) {
|
repository(owner: $owner, name: $repo) {
|
||||||
pullRequests(headRefName: $headRefName, states: OPEN, first: 1) {
|
pullRequests(headRefName: $headRefName, states: OPEN, first: 1) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
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) {
|
viewerCreated: search(query: $viewerQuery, type: ISSUE, first: $per_page) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
...pr
|
...prWithReviews
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pageInfo {
|
pageInfo {
|
||||||
|
|
|
||||||
|
|
@ -249,7 +249,37 @@ func prView(cmd *cobra.Command, args []string) error {
|
||||||
|
|
||||||
func printPrs(prs ...api.PullRequest) {
|
func printPrs(prs ...api.PullRequest) {
|
||||||
for _, pr := range prs {
|
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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,9 @@ var apiClientForContext = func(ctx context.Context) (*api.Client, error) {
|
||||||
opts := []api.ClientOption{
|
opts := []api.ClientOption{
|
||||||
api.AddHeader("Authorization", fmt.Sprintf("token %s", token)),
|
api.AddHeader("Authorization", fmt.Sprintf("token %s", token)),
|
||||||
api.AddHeader("User-Agent", fmt.Sprintf("GitHub CLI %s", Version)),
|
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 != "" {
|
if verbose := os.Getenv("DEBUG"); verbose != "" {
|
||||||
opts = append(opts, api.VerboseLog(os.Stderr))
|
opts = append(opts, api.VerboseLog(os.Stderr))
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,39 @@
|
||||||
package utils
|
package utils
|
||||||
|
|
||||||
import "github.com/mgutz/ansi"
|
import (
|
||||||
|
"github.com/mattn/go-isatty"
|
||||||
|
"github.com/mgutz/ansi"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
var Black = ansi.ColorFunc("black")
|
func makeColorFunc(color string) func(string) string {
|
||||||
var White = ansi.ColorFunc("white")
|
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 output
|
||||||
return ansi.Color(ansi.LightBlack+arg, "")
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var Red = ansi.ColorFunc("red")
|
var Black = makeColorFunc(ansi.Black)
|
||||||
var Green = ansi.ColorFunc("green")
|
var White = makeColorFunc(ansi.White)
|
||||||
var Yellow = ansi.ColorFunc("yellow")
|
var Magenta = makeColorFunc(ansi.Magenta)
|
||||||
var Blue = ansi.ColorFunc("blue")
|
var Cyan = makeColorFunc(ansi.Cyan)
|
||||||
var Magenta = ansi.ColorFunc("magenta")
|
var Red = makeColorFunc(ansi.Red)
|
||||||
var Cyan = ansi.ColorFunc("cyan")
|
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 {
|
func Bold(arg string) string {
|
||||||
// This is really annoying. If you just define Bold as ColorFunc("+b") it will properly bold but
|
output := arg
|
||||||
// will not use the default color, resulting in black and probably unreadable text. This forces
|
if isatty.IsTerminal(os.Stdout.Fd()) {
|
||||||
// the default color before bolding.
|
// This is really annoying. If you just define Bold as ColorFunc("+b") it will properly bold but
|
||||||
return ansi.Color(ansi.DefaultFG+arg, "+b")
|
// 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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue