Skip to content

Instantly share code, notes, and snippets.

@chipaca
Last active January 28, 2020 10:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chipaca/911a8a4905b091c10caa9854bf7ea4b4 to your computer and use it in GitHub Desktop.
Save chipaca/911a8a4905b091c10caa9854bf7ea4b4 to your computer and use it in GitHub Desktop.
Get summary of a repo's open pull requests reviews
package main
import (
"context"
"flag"
"fmt"
"math"
"net/http"
"os"
"strconv"
"strings"
"github.com/google/go-github/github"
"golang.org/x/oauth2"
)
func ck(err error) {
if err == nil {
return
}
fmt.Println("***", err)
os.Exit(1)
}
var (
milestone = flag.String("milestone", "", "only show things for this milestone")
)
const (
// the org or user
owner = "snapcore"
// the repo itself
repo = "snapd"
// the token; get your own at https://github.com/settings/tokens
// (otherwise you'll be rate limited very quickly)
token = ""
)
func label2term(label, color string) string {
if len(color) != 6 {
return label
}
r, re := strconv.ParseUint(color[:2], 16, 8)
g, ge := strconv.ParseUint(color[2:4], 16, 8)
b, be := strconv.ParseUint(color[4:], 16, 8)
if re != nil || ge != nil || be != nil {
return label
}
return fmt.Sprintf("\x1b[38;2;%d;%d;%dm%s\x1b[0m", r, g, b, label)
}
func avgAndTwo(titles []string) int {
tavg := 0.0
for _, title := range titles {
tavg += float64(len(title))
}
tavg /= float64(len(titles))
terr := 0.0
for _, title := range titles {
d := float64(len(title)) - tavg
terr += d * d
}
terr = math.Sqrt(terr / float64(len(titles)))
return int(tavg + 2*terr)
}
func appendMaybe(reviews []string, review string) ([]string, bool) {
if review == "" {
return reviews, false
}
for _, old := range reviews {
if old == review {
return reviews, false
}
}
return append(reviews, review), true
}
func onePull(ctx context.Context, client *github.Client, i, pullNum int, deltas []int, pullRevs [][]string, ch chan int) {
opts := &github.ListOptions{}
revLen := 0
lensum := 0
var allReviews []string
for {
reviews, response, err := client.PullRequests.ListReviews(ctx, owner, repo, pullNum, opts)
ck(err)
for _, review := range reviews {
var comment string
n := len(*review.User.Login)
switch *review.State {
case "COMMENTED", "DISMISSED":
// skip it
case "PENDING":
comment = fmt.Sprintf("\033[2m%s\033[0m", *review.User.Login)
case "APPROVED":
comment = fmt.Sprintf("\033[32m%s\033[0m", *review.User.Login)
case "CHANGES_REQUESTED":
comment = fmt.Sprintf("\033[31m%s\033[0m", *review.User.Login)
default:
n += len(`unhandled ""/`) + len(*review.State)
comment = fmt.Sprintf("\033[33munhandled %q/%s\033[0m", *review.State, *review.User.Login)
}
var added bool
allReviews, added = appendMaybe(allReviews, comment)
if added {
lensum += len(comment)
revLen += n
}
}
if response.NextPage == 0 {
break
}
opts.Page = response.NextPage
}
deltas[i] = lensum - revLen
pullRevs[i] = allReviews
revLen += 2 * len(allReviews)
ch <- revLen
}
func main() {
flag.Parse()
ctx := context.Background()
var tc *http.Client
if token != "" {
ts := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: token},
)
tc = oauth2.NewClient(ctx, ts)
}
client := github.NewClient(tc)
opts := &github.PullRequestListOptions{
ListOptions: github.ListOptions{},
Direction: "asc",
}
maxTit := 0
maxUser := 0
var pullLabels [][]string
var pullTitles []string
var pullUsers []string
var pullNums []int
for {
pulls, response, err := client.PullRequests.List(ctx, owner, repo, opts)
ck(err)
for _, pull := range pulls {
if *milestone != "" && (pull.Milestone == nil || pull.Milestone.Title == nil || (*(pull.Milestone.Title) != *milestone)) {
continue
}
pullNums = append(pullNums, *pull.Number)
labels := make([]string, len(pull.Labels))
for i, label := range pull.Labels {
labels[i] = label2term(*label.Name, *label.Color)
}
pullLabels = append(pullLabels, labels)
pullTitles = append(pullTitles, *pull.Title)
if len(*pull.Title) > maxTit {
maxTit = len(*pull.Title)
}
user := *pull.User.Login
pullUsers = append(pullUsers, user)
if len(user) > maxUser {
maxUser = len(user)
}
}
if response.NextPage == 0 {
break
}
opts.Page = response.NextPage
}
if d2 := avgAndTwo(pullTitles); d2 < maxTit {
maxTit = d2
}
ch := make(chan int)
deltas := make([]int, len(pullNums))
pullRevs := make([][]string, len(pullNums))
for i, pullNum := range pullNums {
go onePull(ctx, client, i, pullNum, deltas, pullRevs, ch)
}
maxRev := 0
for range pullNums {
revLen := <-ch
if revLen > maxRev {
maxRev = revLen
}
}
for i, pullNum := range pullNums {
fmt.Printf("%d %*s %*.*s %*s[%s]\n", pullNum, maxUser, pullUsers[i], -maxTit, maxTit, pullTitles[i], -maxRev-deltas[i], strings.Join(pullRevs[i], ", "), strings.Join(pullLabels[i], ","))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment