diff --git a/go.mod b/go.mod index 35dfc82..1779a3b 100644 --- a/go.mod +++ b/go.mod @@ -15,12 +15,14 @@ require ( github.com/go-openapi/strfmt v0.23.0 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/uuid v1.6.0 // indirect + github.com/jedib0t/go-pretty/v6 v6.5.6 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/oklog/ulid v1.3.1 // indirect github.com/rivo/uniseg v0.4.7 // indirect go.mongodb.org/mongo-driver v1.14.0 // indirect golang.org/x/sys v0.17.0 // indirect + golang.org/x/term v0.16.0 // indirect ) require ( diff --git a/go.sum b/go.sum index 4ae151d..c23aa60 100644 --- a/go.sum +++ b/go.sum @@ -23,6 +23,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/jedib0t/go-pretty v4.3.0+incompatible h1:CGs8AVhEKg/n9YbUenWmNStRW2PHJzaeDodcfvRAbIo= github.com/jedib0t/go-pretty v4.3.0+incompatible/go.mod h1:XemHduiw8R651AF9Pt4FwCTKeG3oo7hrHJAoznj9nag= +github.com/jedib0t/go-pretty/v6 v6.5.6 h1:nKXVLqPfAwY7sWcYXdNZZZ2fjqDpAtj9UeWupgfUxSg= +github.com/jedib0t/go-pretty/v6 v6.5.6/go.mod h1:5LQIxa52oJ/DlDSLv0HEkWOFMDGoWkJb9ss5KqPpJBg= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= @@ -44,6 +46,8 @@ go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= +golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= diff --git a/leaderboard.go b/leaderboard.go index 5b5baca..e780826 100644 --- a/leaderboard.go +++ b/leaderboard.go @@ -6,20 +6,25 @@ import ( "fmt" "log" "log/slog" + "net/http" "os" "regexp" "strconv" "strings" "sync" + "sync/atomic" "time" "github.com/cli/go-gh/pkg/auth" "github.com/gofri/go-github-ratelimit/github_ratelimit" + "github.com/jedib0t/go-pretty/progress" "github.com/jedib0t/go-pretty/table" "github.com/google/go-github/v60/github" ) +var amountOfRequests atomic.Uint64 + func fetchRepositories(client *github.Client, options *Options, clientNumber int, totalClients int, repos chan *github.Repository) { page := clientNumber for i := 0; true; i++ { @@ -103,9 +108,11 @@ func processPullRequest(client *github.Client, options *Options, repo *github.Re } } -func processRepository(client *github.Client, options *Options, repo *github.Repository, stats chan *Stats) { +func processRepository(client *github.Client, pw progress.Writer, options *Options, repo *github.Repository, stats chan *Stats) { slog.Debug("Fetch pull requests", "repository", repo.GetName()) + page := 0 + var tracker progress.Tracker for { pullRequests, response, err := client.PullRequests.List(context.Background(), repo.GetOwner().GetLogin(), repo.GetName(), &github.PullRequestListOptions{ State: "all", @@ -119,6 +126,12 @@ func processRepository(client *github.Client, options *Options, repo *github.Rep panic(err) } + if page == 0 { + tracker = progress.Tracker{Message: repo.GetName(), Total: int64(response.LastPage)} + pw.AppendTracker(&tracker) + } + tracker.Increment(1) + var wg sync.WaitGroup for _, pullRequest := range pullRequests { if pullRequest.GetUpdatedAt().After(options.Since) { @@ -132,6 +145,8 @@ func processRepository(client *github.Client, options *Options, repo *github.Rep wg.Wait() if response.NextPage == 0 { + tracker.Increment(1) + tracker.MarkAsDone() return } page = response.NextPage @@ -146,7 +161,7 @@ type Stats struct { CommentLinesWritten int } -func processRepositories(client *github.Client, options *Options, repos chan *github.Repository, stats chan *Stats) { +func processRepositories(client *github.Client, pw progress.Writer, options *Options, repos chan *github.Repository, stats chan *Stats) { fmt.Printf("Processing data since %v matching repository name pattern %s\n", options.Since, options.NamePattern) var wg sync.WaitGroup @@ -159,7 +174,7 @@ func processRepositories(client *github.Client, options *Options, repos chan *gi wg.Add(1) go func(repo *github.Repository) { defer wg.Done() - processRepository(client, options, repo, stats) + processRepository(client, pw, options, repo, stats) }(repo) } } @@ -167,6 +182,18 @@ func processRepositories(client *github.Client, options *Options, repos chan *gi close(stats) } +type CountingRoundTripper struct { + Proxied http.RoundTripper +} + +func (crt CountingRoundTripper) RoundTrip(req *http.Request) (res *http.Response, e error) { + res, e = crt.Proxied.RoundTrip(req) + + amountOfRequests.Add(1) + + return res, e +} + func createClient() *github.Client { host, _ := auth.DefaultHost() authToken, _ := auth.TokenForHost(host) @@ -174,7 +201,8 @@ func createClient() *github.Client { log.Fatalf("authentication token not found for host %s", host) } - rateLimiter, err := github_ratelimit.NewRateLimitWaiterClient(nil) + roundTrip := CountingRoundTripper{http.DefaultTransport} + rateLimiter, err := github_ratelimit.NewRateLimitWaiterClient(roundTrip) if err != nil { panic(err) } @@ -282,11 +310,18 @@ func showResults(statsPerUser map[string]*Stats) { t.AppendRow(table.Row{stats.Name, stats.PullRequests, stats.Reviews, stats.Comments, stats.CommentLinesWritten}) } + fmt.Printf("Sent overall %d requests to Github", amountOfRequests.Load()) fmt.Println() fmt.Println(t.Render()) } func main() { + pw := progress.NewWriter() + pw.SetSortBy(progress.SortByPercentDsc) + pw.SetStyle(progress.StyleDefault) + pw.Style().Colors = progress.StyleColorsDefault + go pw.Render() + initializedLogger() options := parseCliArgs() @@ -297,7 +332,7 @@ func main() { go fetchAllRepositories(client, options, repos, 5) stats := make(chan *Stats, 128) - go processRepositories(client, options, repos, stats) + go processRepositories(client, pw, options, repos, stats) accumulated := accumulateStatsPerUser(stats)