code_review_leaderboard 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +5 -0
- data/README.md +55 -0
- data/Rakefile +8 -0
- data/exe/code_review_leaderboard +11 -0
- data/lib/adapters/github.rb +25 -0
- data/lib/async/wait_all.rb +9 -0
- data/lib/async/wait_all_throttled.rb +23 -0
- data/lib/code_review_leaderboard/argument_parser.rb +25 -0
- data/lib/code_review_leaderboard/config.rb +41 -0
- data/lib/code_review_leaderboard/formatter.rb +35 -0
- data/lib/code_review_leaderboard/organization.rb +35 -0
- data/lib/code_review_leaderboard/pulls.rb +42 -0
- data/lib/code_review_leaderboard/repository.rb +19 -0
- data/lib/code_review_leaderboard/reviews.rb +46 -0
- data/lib/code_review_leaderboard/spinner.rb +19 -0
- data/lib/code_review_leaderboard/version.rb +3 -0
- data/lib/code_review_leaderboard.rb +64 -0
- metadata +161 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 3ea19e0fe90de7406b88709534c96a763242d73b1261da1a6a537d8cfad02177
|
4
|
+
data.tar.gz: 1cdb5d628849d5efd3fca8239877ffd5b35697c1ff5372f3b8d5bf0ef7f6fdfd
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e9f929c12080ad9225cc22355acc68d0c9e307280cde01a852c0f53548608c905e5bcd19996473100c8ebc741adac629b566508714a7c8667bd006ff05cfff67
|
7
|
+
data.tar.gz: 73d9e6e9d18e66e93319c1cd96a1ac95a04eef267b495ef67cef585ddce5df87bc8b244d98713eaacfdda1978be634473ed8db9e3070a7471f8d42e25c9303c3
|
data/CHANGELOG.md
ADDED
data/README.md
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
# Code Review Leaderboard
|
2
|
+
|
3
|
+
Create a leaderboard of code reviewers for your repository or organization.
|
4
|
+
|
5
|
+
Find out who is the most active reviewer in your team, who is the most thorough, and who is the most critical.
|
6
|
+
|
7
|
+
## Examples
|
8
|
+
|
9
|
+
<div align="center">
|
10
|
+
<img width="750" alt="Screenshot 2024-02-10 at 21 08 05" src="https://github.com/markokajzer/code-review-leaderboard/assets/9379317/4a774c51-5fc3-4c2d-bf0d-3d784dd4b04a">
|
11
|
+
</div>
|
12
|
+
|
13
|
+
Reviews are counted by the following rules:
|
14
|
+
1. Completed within the last 30 days
|
15
|
+
2. Multiple comments only count once.
|
16
|
+
3. If a reviewer both approves/rejects a pull request _and_ comments, the comments do not count extra.
|
17
|
+
4. Approvals and rejections always count.
|
18
|
+
|
19
|
+
For a repository of 718 repositories and 526 reviews, the script takes roughly 1 minute.
|
20
|
+
|
21
|
+
## Installation
|
22
|
+
|
23
|
+
Install the gem:
|
24
|
+
|
25
|
+
$ gem install code_review_leaderboard
|
26
|
+
|
27
|
+
## Usage
|
28
|
+
|
29
|
+
To use the gem, you will need to provide a GitHub access token. You can create a new access token by following the instructions [here](https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line). If you want to inspect the repositories of an organization, make sure the organization in question allows access to their repositories from personal access tokens. You can check that from `https://github.com/organizations/<name>/settings/personal-access-tokens`.
|
30
|
+
|
31
|
+
To inspect the reviews of a single repository, run:
|
32
|
+
|
33
|
+
$ code_review_leaderboard --access_token ACCESS_TOKEN --repository REPOSITORY
|
34
|
+
|
35
|
+
You can also provide multiple repositories:
|
36
|
+
|
37
|
+
$ code_review_leaderboard --access_token ACCESS_TOKEN --repository REPOSITORY1 REPOSITORY2
|
38
|
+
|
39
|
+
Finally, you can also inspect the repositories of an organization:
|
40
|
+
|
41
|
+
$ code_review_leaderboard --access_token ACCESS_TOKEN --organization ORGANIZATION
|
42
|
+
|
43
|
+
## Configuration
|
44
|
+
|
45
|
+
You can either configure the gem via command line arguments or by setting environment variables. Command line arguments take precedence.
|
46
|
+
|
47
|
+
## Development
|
48
|
+
|
49
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rspec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
50
|
+
|
51
|
+
To install this gem onto your local machine, run `bundle exec rake install`. You can then run it with `bundle exec <executable>`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
52
|
+
|
53
|
+
## Contributing
|
54
|
+
|
55
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/markokajzer/code-review-leaderboard.
|
data/Rakefile
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
base_path = File.expand_path("../lib", __dir__)
|
4
|
+
$LOAD_PATH.unshift(base_path)
|
5
|
+
|
6
|
+
require "active_support/core_ext/object/blank"
|
7
|
+
|
8
|
+
require "code_review_leaderboard"
|
9
|
+
|
10
|
+
CodeReviewLeaderboard.initialize!
|
11
|
+
CodeReviewLeaderboard.start
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require "active_support"
|
2
|
+
require "active_support/core_ext/module/delegation"
|
3
|
+
|
4
|
+
require "octokit"
|
5
|
+
|
6
|
+
module CodeReviewLeaderboard
|
7
|
+
module Adapters
|
8
|
+
module Github
|
9
|
+
extend self
|
10
|
+
|
11
|
+
delegate :organization_repositories,
|
12
|
+
:pull_requests,
|
13
|
+
:pull_request_reviews,
|
14
|
+
to: :client
|
15
|
+
|
16
|
+
def client
|
17
|
+
@client ||= Octokit::Client.new(access_token: Config.access_token)
|
18
|
+
end
|
19
|
+
|
20
|
+
def per_page
|
21
|
+
client.per_page || 30
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require "async"
|
2
|
+
require "async/barrier"
|
3
|
+
require "async/semaphore"
|
4
|
+
|
5
|
+
require_relative "wait_all"
|
6
|
+
|
7
|
+
def WaitAllThrottled(array, concurrency: 5, &block)
|
8
|
+
result = []
|
9
|
+
|
10
|
+
WaitAll do |barrier|
|
11
|
+
semaphore = Async::Semaphore.new(concurrency, parent: barrier)
|
12
|
+
|
13
|
+
Async do
|
14
|
+
array.each do |item|
|
15
|
+
semaphore.async(parent: barrier) do
|
16
|
+
result << yield(item)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
result.flatten
|
23
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require "optparse"
|
2
|
+
|
3
|
+
module CodeReviewLeaderboard
|
4
|
+
module ArgumentParser
|
5
|
+
extend self
|
6
|
+
|
7
|
+
def parse!(args: ARGV)
|
8
|
+
{}.tap do |options|
|
9
|
+
OptionParser.new do |opts|
|
10
|
+
opts.banner = "Usage: leaderboard [options]"
|
11
|
+
|
12
|
+
opts.on("-t", "--access-token ACCESS_TOKEN", "Specify the access token") { |token| options[:access_token] = token }
|
13
|
+
opts.on("-r", "--repo", "--repository repository,repository", Array, "Specify the repository") { |repositories| options[:repositories] = repositories }
|
14
|
+
opts.on("-o", "--org", "--organization organization", "Specify the organization") { |organization| options[:organization] = organization }
|
15
|
+
opts.on("-v", "--verbose", "Run verbosely") { options[:log_level] = :debug }
|
16
|
+
|
17
|
+
opts.on("-h", "--help", "Show this message") do
|
18
|
+
puts opts
|
19
|
+
exit
|
20
|
+
end
|
21
|
+
end.parse!(args)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require "active_support"
|
2
|
+
require "active_support/configurable"
|
3
|
+
require "active_support/core_ext/module/delegation"
|
4
|
+
|
5
|
+
require_relative "argument_parser"
|
6
|
+
|
7
|
+
module CodeReviewLeaderboard
|
8
|
+
ConfigurationError = Class.new(StandardError)
|
9
|
+
|
10
|
+
module Config
|
11
|
+
extend self
|
12
|
+
|
13
|
+
include ActiveSupport::Configurable
|
14
|
+
|
15
|
+
config_accessor :access_token
|
16
|
+
config_accessor :repositories, default: []
|
17
|
+
config_accessor :organization
|
18
|
+
config_accessor :log_level, default: :info
|
19
|
+
|
20
|
+
def initialize!
|
21
|
+
initialize_from_args!
|
22
|
+
initialize_from_env!
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize_from_args!
|
26
|
+
options = ArgumentParser.parse!
|
27
|
+
|
28
|
+
self.access_token = options[:access_token]
|
29
|
+
self.organization = options[:organization]
|
30
|
+
self.repositories = options[:repositories] if options[:repositories].present?
|
31
|
+
self.log_level = options[:log_level]
|
32
|
+
end
|
33
|
+
|
34
|
+
def initialize_from_env!
|
35
|
+
self.access_token ||= ENV["ACCESS_TOKEN"]
|
36
|
+
self.organization ||= ENV["ORGANIZATION"]
|
37
|
+
self.repositories = repositories.presence || ENV["REPOSITORY"].to_s.split(",")
|
38
|
+
self.log_level ||= ENV["LOG_LEVEL"]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require "terminal-table"
|
2
|
+
|
3
|
+
module CodeReviewLeaderboard
|
4
|
+
class Formatter
|
5
|
+
def initialize(tally)
|
6
|
+
@tally = tally
|
7
|
+
end
|
8
|
+
|
9
|
+
def to_table
|
10
|
+
Terminal::Table.new(headings:) do |rows|
|
11
|
+
totals.each do |user, reviews|
|
12
|
+
rows << [user, reviews[:total], reviews[:approved], reviews[:changes_requested], reviews[:commented]]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
attr_reader :tally
|
20
|
+
|
21
|
+
def totals
|
22
|
+
tally.each_with_object({}) do |review, totals|
|
23
|
+
totals[review[:user].to_sym] ||= Hash.new(0)
|
24
|
+
totals[review[:user].to_sym][review[:state]] += 1
|
25
|
+
totals[review[:user].to_sym][:total] += 1
|
26
|
+
end
|
27
|
+
.sort_by { _2[:total] }
|
28
|
+
.reverse
|
29
|
+
end
|
30
|
+
|
31
|
+
def headings
|
32
|
+
["Login", "Total", "Approved", "Changes Requested", "Commented"]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require "active_support"
|
2
|
+
require "active_support/core_ext/numeric/time"
|
3
|
+
|
4
|
+
require "adapters/github"
|
5
|
+
require_relative "config"
|
6
|
+
|
7
|
+
require_relative "repository"
|
8
|
+
|
9
|
+
module CodeReviewLeaderboard
|
10
|
+
class Organization
|
11
|
+
attr_reader :name
|
12
|
+
|
13
|
+
def initialize(name:)
|
14
|
+
@name = name
|
15
|
+
end
|
16
|
+
|
17
|
+
def repos(since: 30.days.ago)
|
18
|
+
fetch_repos(since:).map { Repository.new(name: _1.full_name) }
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def fetch_repos(since:)
|
24
|
+
(1..).each_with_object([]) do |page, repos|
|
25
|
+
repo_chunk =
|
26
|
+
Adapters::Github
|
27
|
+
.organization_repositories(name, type: "sources", sort: "pushed", page:)
|
28
|
+
.filter { _1.pushed_at > since }
|
29
|
+
repos.concat(repo_chunk)
|
30
|
+
|
31
|
+
return repos if repo_chunk.size < Adapters::Github.per_page
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require "active_support"
|
2
|
+
require "active_support/core_ext/numeric/time"
|
3
|
+
|
4
|
+
require "adapters/github"
|
5
|
+
require_relative "config"
|
6
|
+
|
7
|
+
module CodeReviewLeaderboard
|
8
|
+
class Pulls
|
9
|
+
PER_PAGE = 100
|
10
|
+
|
11
|
+
class << self
|
12
|
+
def for(repository:, since: 30.days.ago)
|
13
|
+
new(repository:, since:).pulls
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(repository:, since:)
|
18
|
+
@repository = repository
|
19
|
+
@since = since
|
20
|
+
end
|
21
|
+
|
22
|
+
def pulls
|
23
|
+
fetch_pulls
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
attr_reader :repository, :since
|
29
|
+
|
30
|
+
def fetch_pulls
|
31
|
+
(1..).each_with_object([]) do |page, pulls|
|
32
|
+
pulls_chunk =
|
33
|
+
Adapters::Github
|
34
|
+
.pull_requests(repository.name, state: "all", per_page: PER_PAGE, page:)
|
35
|
+
.filter { _1.created_at > since }
|
36
|
+
pulls.concat(pulls_chunk)
|
37
|
+
|
38
|
+
return pulls if pulls_chunk.size < PER_PAGE
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require_relative "pulls"
|
2
|
+
|
3
|
+
module CodeReviewLeaderboard
|
4
|
+
class Repository
|
5
|
+
attr_reader :name
|
6
|
+
|
7
|
+
def initialize(name:)
|
8
|
+
@name = name
|
9
|
+
end
|
10
|
+
|
11
|
+
def pulls
|
12
|
+
Pulls.for(repository: self)
|
13
|
+
end
|
14
|
+
|
15
|
+
def ==(other)
|
16
|
+
name == other.name
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require_relative "config"
|
2
|
+
|
3
|
+
module CodeReviewLeaderboard
|
4
|
+
class Reviews
|
5
|
+
class << self
|
6
|
+
def for(pull:, since: 30.days.ago)
|
7
|
+
new(pull:, since:).reviews
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(pull:, since: 30.days.ago)
|
12
|
+
@pull = pull
|
13
|
+
@since = since
|
14
|
+
end
|
15
|
+
|
16
|
+
def reviews
|
17
|
+
puts "Fetching reviews for #{repository}##{pull.number}..." if Config.log_level == :debug
|
18
|
+
|
19
|
+
comments, reviews =
|
20
|
+
fetch_reviews
|
21
|
+
.map { {user: _1.user.login, state: _1.state.downcase.to_sym} }
|
22
|
+
.uniq
|
23
|
+
.partition { _1[:state] == :commented }
|
24
|
+
|
25
|
+
# Do not count comments if already otherwise reviewed
|
26
|
+
comments.filter! do |commenter|
|
27
|
+
reviews.none? { |reviewer| reviewer[:user] == commenter[:user] }
|
28
|
+
end
|
29
|
+
|
30
|
+
reviews + comments
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
attr_reader :pull, :since
|
36
|
+
|
37
|
+
def fetch_reviews
|
38
|
+
Adapters::Github.pull_request_reviews(repository, pull.number)
|
39
|
+
.filter { _1.submitted_at > since }
|
40
|
+
end
|
41
|
+
|
42
|
+
def repository
|
43
|
+
pull.base.repo.full_name
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require "whirly"
|
2
|
+
|
3
|
+
module CodeReviewLeaderboard
|
4
|
+
module Spinner
|
5
|
+
extend self
|
6
|
+
|
7
|
+
delegate :status=, to: Whirly
|
8
|
+
|
9
|
+
def start
|
10
|
+
result = nil
|
11
|
+
|
12
|
+
Whirly.start(spinner: "dots", stop: "✔") do
|
13
|
+
result = yield
|
14
|
+
end
|
15
|
+
|
16
|
+
result
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require_relative "code_review_leaderboard/version"
|
2
|
+
|
3
|
+
require_relative "async/wait_all_throttled"
|
4
|
+
|
5
|
+
require "code_review_leaderboard/config"
|
6
|
+
require "code_review_leaderboard/formatter"
|
7
|
+
require "code_review_leaderboard/organization"
|
8
|
+
require "code_review_leaderboard/pulls"
|
9
|
+
require "code_review_leaderboard/repository"
|
10
|
+
require "code_review_leaderboard/reviews"
|
11
|
+
require "code_review_leaderboard/spinner"
|
12
|
+
|
13
|
+
module CodeReviewLeaderboard
|
14
|
+
extend self
|
15
|
+
|
16
|
+
def initialize!
|
17
|
+
Config.initialize!
|
18
|
+
|
19
|
+
raise ConfigurationError, "Access token is required" if Config.access_token.nil?
|
20
|
+
raise ConfigurationError, "Repository or owner is required" if Config.repositories.empty? && Config.organization.blank?
|
21
|
+
end
|
22
|
+
|
23
|
+
def start
|
24
|
+
puts "Found #{repositories.size} repositories" if Config.organization.present? && Config.log_level == :debug
|
25
|
+
puts "Found #{pulls.size} pull requests."
|
26
|
+
puts "Found #{reviews.size} reviews."
|
27
|
+
puts
|
28
|
+
|
29
|
+
puts Formatter.new(reviews).to_table
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def repositories
|
35
|
+
@@repositories ||= if Config.repositories.present?
|
36
|
+
Config.repositories.map { Repository.new(name: _1) }
|
37
|
+
else
|
38
|
+
Spinner.start do
|
39
|
+
Spinner.status = "Fetching repos for #{Config.organization}..."
|
40
|
+
Organization.new(name: Config.organization).repos
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
alias_method :load_repositories, :repositories
|
45
|
+
|
46
|
+
def pulls
|
47
|
+
# Load before we enter the next spinner so we do not end up nesting them
|
48
|
+
load_repositories
|
49
|
+
|
50
|
+
@@pulls ||= Spinner.start do
|
51
|
+
Spinner.status = "Fetching pull requests..."
|
52
|
+
|
53
|
+
WaitAllThrottled(repositories) { _1.pulls }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def reviews
|
58
|
+
@@reviews ||= Spinner.start do
|
59
|
+
Spinner.status = "Fetching reviews..."
|
60
|
+
|
61
|
+
WaitAllThrottled(pulls) { Reviews.for(pull: _1) }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
metadata
ADDED
@@ -0,0 +1,161 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: code_review_leaderboard
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- markokajzer
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2024-02-10 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: octokit
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: faraday-retry
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: activesupport
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: async
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: whirly
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: paint
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: terminal-table
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
description:
|
112
|
+
email:
|
113
|
+
- markokajzer91@gmail.com
|
114
|
+
executables:
|
115
|
+
- code_review_leaderboard
|
116
|
+
extensions: []
|
117
|
+
extra_rdoc_files: []
|
118
|
+
files:
|
119
|
+
- CHANGELOG.md
|
120
|
+
- README.md
|
121
|
+
- Rakefile
|
122
|
+
- exe/code_review_leaderboard
|
123
|
+
- lib/adapters/github.rb
|
124
|
+
- lib/async/wait_all.rb
|
125
|
+
- lib/async/wait_all_throttled.rb
|
126
|
+
- lib/code_review_leaderboard.rb
|
127
|
+
- lib/code_review_leaderboard/argument_parser.rb
|
128
|
+
- lib/code_review_leaderboard/config.rb
|
129
|
+
- lib/code_review_leaderboard/formatter.rb
|
130
|
+
- lib/code_review_leaderboard/organization.rb
|
131
|
+
- lib/code_review_leaderboard/pulls.rb
|
132
|
+
- lib/code_review_leaderboard/repository.rb
|
133
|
+
- lib/code_review_leaderboard/reviews.rb
|
134
|
+
- lib/code_review_leaderboard/spinner.rb
|
135
|
+
- lib/code_review_leaderboard/version.rb
|
136
|
+
homepage: https://github.com/markokajzer/code-review-leaderboard
|
137
|
+
licenses: []
|
138
|
+
metadata:
|
139
|
+
homepage_uri: https://github.com/markokajzer/code-review-leaderboard
|
140
|
+
source_code_uri: https://github.com/markokajzer/code-review-leaderboard
|
141
|
+
changelog_uri: https://github.com/markokajzer/code-review-leaderboard/blob/main/CHANGELOG.md
|
142
|
+
post_install_message:
|
143
|
+
rdoc_options: []
|
144
|
+
require_paths:
|
145
|
+
- lib
|
146
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
147
|
+
requirements:
|
148
|
+
- - ">="
|
149
|
+
- !ruby/object:Gem::Version
|
150
|
+
version: 3.3.0
|
151
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
152
|
+
requirements:
|
153
|
+
- - ">="
|
154
|
+
- !ruby/object:Gem::Version
|
155
|
+
version: '0'
|
156
|
+
requirements: []
|
157
|
+
rubygems_version: 3.5.4
|
158
|
+
signing_key:
|
159
|
+
specification_version: 4
|
160
|
+
summary: Create a leaderboard for code review in your repository or organization
|
161
|
+
test_files: []
|