github_repo_statistics 2.0.8 → 2.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 +4 -4
- data/.gitignore +2 -0
- data/.rubocop.yml +1 -0
- data/.rubocop_todo.yml +67 -0
- data/Gemfile.lock +19 -1
- data/README.md +99 -31
- data/bin/force_merge_report +61 -0
- data/bin/review_report +58 -0
- data/github_repo_statistics.gemspec +2 -0
- data/lib/github_repo_statistics/force_merge_report.rb +87 -0
- data/lib/github_repo_statistics/github_repo_statistics.rb +7 -3
- data/lib/github_repo_statistics/review_report.rb +94 -0
- data/lib/github_repo_statistics/version.rb +1 -1
- data/spec/github_repo_statistics.rb +1 -1
- metadata +41 -3
- /data/{.github → spec/fixtures}/CODEOWNERS +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7625d04f395885b90b8f2faad896127df5e16604818d095f3e5455bf49d9c899
|
|
4
|
+
data.tar.gz: e6fec8b96326f6aff20e8ba63ccde41d3605381a692df51a3e7dceac8f0b218c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c943a3541d786ca121518c4545a0d82f036d71492ad83bec46ffffd187e4f6536475688acd1f3841e20ded27b29eb4759675e40ef86d55a66ef2ae147487e8fe
|
|
7
|
+
data.tar.gz: 76c00e2a9cc37a21026cb31229f6c7c71733428b1fc1d06fd24f5c8e9d5c8777d85d23e45c4af73ddaf699d6c8afd63dab38dc40af2780c0f73f704e32b0b4b2
|
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
inherit_from: .rubocop_todo.yml
|
data/.rubocop_todo.yml
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# This configuration was generated by
|
|
2
|
+
# `rubocop --auto-gen-config`
|
|
3
|
+
# on 2024-02-27 08:25:07 UTC using RuboCop version 1.60.0.
|
|
4
|
+
# The point is for the user to remove these configuration records
|
|
5
|
+
# one by one as the offenses are removed from the code base.
|
|
6
|
+
# Note that changes in the inspected code, or installation of new
|
|
7
|
+
# versions of RuboCop, may require this file to be generated again.
|
|
8
|
+
|
|
9
|
+
# Offense count: 11
|
|
10
|
+
# Configuration parameters: AllowedMethods.
|
|
11
|
+
# AllowedMethods: enums
|
|
12
|
+
Lint/ConstantDefinitionInBlock:
|
|
13
|
+
Exclude:
|
|
14
|
+
- 'spec/github_repo_statistics.rb'
|
|
15
|
+
|
|
16
|
+
# Offense count: 8
|
|
17
|
+
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes.
|
|
18
|
+
Metrics/AbcSize:
|
|
19
|
+
Max: 96
|
|
20
|
+
|
|
21
|
+
# Offense count: 7
|
|
22
|
+
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
|
|
23
|
+
# AllowedMethods: refine
|
|
24
|
+
Metrics/BlockLength:
|
|
25
|
+
Max: 73
|
|
26
|
+
|
|
27
|
+
# Offense count: 1
|
|
28
|
+
# Configuration parameters: CountComments, CountAsOne.
|
|
29
|
+
Metrics/ClassLength:
|
|
30
|
+
Max: 229
|
|
31
|
+
|
|
32
|
+
# Offense count: 4
|
|
33
|
+
# Configuration parameters: AllowedMethods, AllowedPatterns.
|
|
34
|
+
Metrics/CyclomaticComplexity:
|
|
35
|
+
Max: 18
|
|
36
|
+
|
|
37
|
+
# Offense count: 8
|
|
38
|
+
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
|
|
39
|
+
Metrics/MethodLength:
|
|
40
|
+
Max: 62
|
|
41
|
+
|
|
42
|
+
# Offense count: 4
|
|
43
|
+
# Configuration parameters: AllowedMethods, AllowedPatterns.
|
|
44
|
+
Metrics/PerceivedComplexity:
|
|
45
|
+
Max: 18
|
|
46
|
+
|
|
47
|
+
# Offense count: 3
|
|
48
|
+
# Configuration parameters: AllowedConstants.
|
|
49
|
+
Style/Documentation:
|
|
50
|
+
Exclude:
|
|
51
|
+
- 'spec/**/*'
|
|
52
|
+
- 'test/**/*'
|
|
53
|
+
- 'lib/github_repo_statistics/force_merge_report.rb'
|
|
54
|
+
- 'lib/github_repo_statistics/github_repo_statistics.rb'
|
|
55
|
+
- 'lib/github_repo_statistics/review_report.rb'
|
|
56
|
+
|
|
57
|
+
# Offense count: 1
|
|
58
|
+
Style/MultilineBlockChain:
|
|
59
|
+
Exclude:
|
|
60
|
+
- 'lib/github_repo_statistics/github_repo_statistics.rb'
|
|
61
|
+
|
|
62
|
+
# Offense count: 13
|
|
63
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
64
|
+
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns.
|
|
65
|
+
# URISchemes: http, https
|
|
66
|
+
Layout/LineLength:
|
|
67
|
+
Max: 229
|
data/Gemfile.lock
CHANGED
|
@@ -1,20 +1,33 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
github_repo_statistics (2.0
|
|
4
|
+
github_repo_statistics (2.1.0)
|
|
5
5
|
date
|
|
6
|
+
faraday-retry
|
|
7
|
+
octokit
|
|
6
8
|
pry
|
|
7
9
|
|
|
8
10
|
GEM
|
|
9
11
|
remote: https://rubygems.org/
|
|
10
12
|
specs:
|
|
13
|
+
addressable (2.8.4)
|
|
14
|
+
public_suffix (>= 2.0.2, < 6.0)
|
|
11
15
|
ast (2.4.2)
|
|
12
16
|
coderay (1.1.3)
|
|
13
17
|
date (3.3.4)
|
|
14
18
|
diff-lcs (1.5.0)
|
|
19
|
+
faraday (2.7.9)
|
|
20
|
+
faraday-net_http (>= 2.0, < 3.1)
|
|
21
|
+
ruby2_keywords (>= 0.0.4)
|
|
22
|
+
faraday-net_http (3.0.2)
|
|
23
|
+
faraday-retry (2.2.0)
|
|
24
|
+
faraday (~> 2.0)
|
|
15
25
|
json (2.7.1)
|
|
16
26
|
language_server-protocol (3.17.0.3)
|
|
17
27
|
method_source (1.0.0)
|
|
28
|
+
octokit (8.0.0)
|
|
29
|
+
faraday (>= 1, < 3)
|
|
30
|
+
sawyer (~> 0.9)
|
|
18
31
|
parallel (1.24.0)
|
|
19
32
|
parser (3.3.0.4)
|
|
20
33
|
ast (~> 2.4.1)
|
|
@@ -22,6 +35,7 @@ GEM
|
|
|
22
35
|
pry (0.14.2)
|
|
23
36
|
coderay (~> 1.1)
|
|
24
37
|
method_source (~> 1.0)
|
|
38
|
+
public_suffix (5.0.1)
|
|
25
39
|
racc (1.7.3)
|
|
26
40
|
rainbow (3.1.1)
|
|
27
41
|
rake (13.1.0)
|
|
@@ -54,6 +68,10 @@ GEM
|
|
|
54
68
|
rubocop-ast (1.30.0)
|
|
55
69
|
parser (>= 3.2.1.0)
|
|
56
70
|
ruby-progressbar (1.13.0)
|
|
71
|
+
ruby2_keywords (0.0.5)
|
|
72
|
+
sawyer (0.9.2)
|
|
73
|
+
addressable (>= 2.3.5)
|
|
74
|
+
faraday (>= 0.17.3, < 3)
|
|
57
75
|
unicode-display_width (2.5.0)
|
|
58
76
|
|
|
59
77
|
PLATFORMS
|
data/README.md
CHANGED
|
@@ -1,39 +1,107 @@
|
|
|
1
1
|
# GithubRepoStatistics
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/github_repo_statistics`. To experiment with that code, run `bin/console` for an interactive prompt.
|
|
3
|
+
This is a standalone ruby executable that allows you to collect statistics
|
|
6
4
|
|
|
7
5
|
## Installation
|
|
6
|
+
*Prerequisite:* You will need at least Ruby 3.1.0 to run this gem. You can install it throug `rbenv` or `rvm`. (e.g. `brew install rvm && rvm install 3.2.2 && rvm use defaukt 3.2.2`)
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
Install the gem and add to the application's Gemfile by executing:
|
|
12
|
-
|
|
13
|
-
$ bundle add UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG
|
|
14
|
-
|
|
15
|
-
If bundler is not being used to manage dependencies, install the gem by executing:
|
|
16
|
-
|
|
17
|
-
$ gem install UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG
|
|
8
|
+
*Installing the gem:*
|
|
9
|
+
`gem install github_repo_statistics`
|
|
18
10
|
|
|
19
11
|
## Usage
|
|
20
12
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
13
|
+
```
|
|
14
|
+
github_repo_statistics -h
|
|
15
|
+
Usage: github_repo_statistics [options]
|
|
16
|
+
--debug Enable debug mode
|
|
17
|
+
--ci Do not print the info messages for better CI text parsing [default: false]
|
|
18
|
+
--codeowners Print CODEOWNERS info [default: false]
|
|
19
|
+
--hotspot-files Print the found hotspot files (big files touched by many) [default: false]
|
|
20
|
+
--excluded-contributors STRING
|
|
21
|
+
Comma-delimited list of excluded contributors [example: WEB,RAILS,MOBILE]
|
|
22
|
+
--excluded-files STRING Comma-delimited list of excluded keywords [example: ViewController,AppDelegate.swift]
|
|
23
|
+
--steps STRING Number of steps the script will go into the past [default: 1]
|
|
24
|
+
--duration-in-days STRING Number of days to aggregate the changes for [default: 30]
|
|
25
|
+
--path STRING Path to the directory or file to calculate the ownership [default: "."]
|
|
26
|
+
--team-regex STRING Regex that will identify the team name [default: "[A-Za-z]+"]
|
|
27
|
+
--codeowners-path STRING Path to CODEOWNERS file [default: .github/CODEOWNERS]
|
|
28
|
+
--big-file-size STRING The amount of lines in the file to be considered big [default: 250]
|
|
29
|
+
--default-branch STRING The default branch to pull and run metrics for [default: master]
|
|
30
|
+
--code-extensions STRING The file extensions that consider to be code [default: ".kt,.swift"]
|
|
31
|
+
--output-to-files Puts the output for hotspot and codeowners into files instead of the STDOUT (useful for CI and big amount of data) [default:false]
|
|
32
|
+
-v, --version Display the version of the gem
|
|
33
|
+
-h, --help Display this help message
|
|
34
|
+
|
|
35
|
+
Examples of usage:
|
|
36
|
+
github_repo_statistics --path src/test --exclusions WEB,RAILS --steps 2 --duration-in-days 90 --hotspot-files --debug
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
*Timeframe:* 2023-11-01 to 2024-01-30
|
|
40
|
+
*Code files with a single contributor:* 94.4%
|
|
41
|
+
*Existing files changed by many teams:* 101
|
|
42
|
+
*Current existing [".swift"] files:* 1805
|
|
43
|
+
*Cross-Squad Dependency:*
|
|
44
|
+
*Contributions by multiple squads to the same files:* 282
|
|
45
|
+
*Contributions by single squads contributing to single files:* 501
|
|
46
|
+
*Hotspot Code Changes:* 42.48%
|
|
47
|
+
*Churn count(commits to files by multiple teams):* 701
|
|
48
|
+
*Total amount of commits:* 1650
|
|
49
|
+
*Total lines of hotspot code:* 19965
|
|
50
|
+
*[".swift"] files with multiple contributors:* 101
|
|
51
|
+
*[".swift"] files exceeding 250 lines with multiple contributors:* 37
|
|
52
|
+
*Total amount of commits to [".swift"] files:* 1650
|
|
53
|
+
*Total [".swift"] files changed:* 602
|
|
54
|
+
*Current total number of code files longer than 250 lines:* 102
|
|
55
|
+
*Current total of [".swift"] files in the folder:* 1805
|
|
56
|
+
*Contributors:* {"CCE"=>245, "SQDREACT"=>145, "DSAT"=>124, "PROD"=>111, "ORGANIC"=>92, "CS"=>75, "SF"=>61, "PCON"=>55, "ER"=>48, "VIRRCH"=>45, "PACC"=>37, "MN"=>31, "CAWLAPP"=>28, "CMT"=>25, "PAYS"=>24, "EX"=>12, "REF"=>11, "ZEST"=>3, "UFI"=>2, "VRIRRCH"=>2, "CHANGED"=>1, "CLEANUPS"=>1, "DPLAN"=>1}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
force_merge_report --help
|
|
61
|
+
Usage: github_repo_statistics [options]
|
|
62
|
+
--ci Do not print the info messages for better CI text parsing [default: false]
|
|
63
|
+
--default-branch STRING The default branch to pull and run metrics for [default: master]
|
|
64
|
+
--github-token STRING GitHub API token [default: ""]
|
|
65
|
+
--duration-in-days STRING Number of days to aggregate the changes for [default: 90]
|
|
66
|
+
--github-repo STRING GitHub repository name and owner (e.g. octokit/octokit) [default: ""]
|
|
67
|
+
-v, --version Display the version of the gem
|
|
68
|
+
-h, --help Display this help message
|
|
69
|
+
|
|
70
|
+
Examples of usage:
|
|
71
|
+
force_merge_report --github-token ghp_SAKLGSJHG8asgiasKASGkhsa --github-repo octokit/octokit --duration-in-days 7
|
|
72
|
+
|
|
73
|
+
PR #12315 - Merged at: 2024-02-26 16:20:22 UTC
|
|
74
|
+
Failed Checks:
|
|
75
|
+
- Workflow: Mergeable: Size check, Conclusion: failure
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
PR #312455 - Merged at: 2024-02-26 15:01:09 UTC
|
|
79
|
+
Failed Checks:
|
|
80
|
+
- Workflow: Mergeable: PR Review, Conclusion: failure
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
Weekly Summary:
|
|
84
|
+
Calendar week 2022-05: Total PRs: 11, Force-merged PRs: 2
|
|
85
|
+
Failed Workflows:
|
|
86
|
+
- Mergeable: Size check: 1
|
|
87
|
+
- Mergeable: PR Review: 1
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
review_report -h
|
|
92
|
+
Usage: github_repo_statistics [options]
|
|
93
|
+
--duration-in-days STRING Number of days to aggregate the changes for [default: 90]
|
|
94
|
+
--default-branch STRING The default branch to pull and run metrics for [default: master]
|
|
95
|
+
--github-token STRING GitHub API token [default: ""]
|
|
96
|
+
--github-repo STRING GitHub repository name and owner (e.g. octokit/octokit) [default: ""]
|
|
97
|
+
-v, --version Display the version of the gem
|
|
98
|
+
-h, --help Display this help message
|
|
99
|
+
|
|
100
|
+
Examples of usage:
|
|
101
|
+
review_report --github-token ghp_SAKLGSJHG8asgiasKASGkhsa --github-repo octokit/octokit
|
|
102
|
+
|
|
103
|
+
Calendar week 2023-04: Average time taken for 24 pull requests is 2.49 hours.
|
|
104
|
+
Total reviews: 440
|
|
105
|
+
Reviews with comments: 247
|
|
106
|
+
Change requested reviews: 7
|
|
107
|
+
```
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'date'
|
|
5
|
+
require 'optparse'
|
|
6
|
+
require 'pry'
|
|
7
|
+
require_relative '../lib/github_repo_statistics/version'
|
|
8
|
+
require_relative '../lib/github_repo_statistics/force_merge_report'
|
|
9
|
+
|
|
10
|
+
options = {}
|
|
11
|
+
OptionParser.new do |opts|
|
|
12
|
+
opts.banner = 'Usage: github_repo_statistics [options]'
|
|
13
|
+
|
|
14
|
+
opts.on('--ci', 'Do not print the info messages for better CI text parsing [default: false]') do
|
|
15
|
+
options[:ci] = true
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
opts.on('--default-branch STRING',
|
|
19
|
+
'The default branch to pull and run metrics for [default: master]') do |default_branch|
|
|
20
|
+
options[:default_branch] = default_branch
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
opts.on('--github-token STRING',
|
|
24
|
+
'GitHub API token [default: ""]') do |github_token|
|
|
25
|
+
options[:github_token] = github_token
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
opts.on('--duration-in-days STRING',
|
|
29
|
+
'Number of days to aggregate the changes for [default: 90]') do |duration_in_days|
|
|
30
|
+
options[:duration_in_days] = duration_in_days.to_i
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
opts.on('--github-repo STRING',
|
|
34
|
+
'GitHub repository name and owner (e.g. octokit/octokit) [default: ""]') do |github_repo|
|
|
35
|
+
options[:github_repo] = github_repo
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
opts.on('-v', '--version', 'Display the version of the gem') do
|
|
39
|
+
puts "github_repo_statistics version #{GithubRepoStatistics::VERSION}"
|
|
40
|
+
exit
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
opts.on('-h', '--help', 'Display this help message') do
|
|
44
|
+
puts opts
|
|
45
|
+
puts <<~EXAMPLES
|
|
46
|
+
|
|
47
|
+
Examples:
|
|
48
|
+
force_merge_report --github-token ghp_bKasj29ndkfdksf3rjt2ngi43j --github-repo octokit/octokit
|
|
49
|
+
EXAMPLES
|
|
50
|
+
exit
|
|
51
|
+
end
|
|
52
|
+
end.parse!
|
|
53
|
+
|
|
54
|
+
DEFAULT_BRANCH = options[:default_branch] || 'master'
|
|
55
|
+
|
|
56
|
+
raise 'Please provide GitHub token using --github-token flag' if options[:github_token].nil?
|
|
57
|
+
raise 'Please provide GitHub repo name using --github-repo flag' if options[:github_repo].nil?
|
|
58
|
+
raise 'Please provide default GitHub branch using --default-branch flag' if DEFAULT_BRANCH.nil?
|
|
59
|
+
|
|
60
|
+
ForceMergeReport.new(token: options[:github_token], repo: options[:github_repo],
|
|
61
|
+
branch: DEFAULT_BRANCH, duration: options[:duration_in_days] || 30, ci: options[:ci]).report
|
data/bin/review_report
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'date'
|
|
5
|
+
require 'optparse'
|
|
6
|
+
require 'pry'
|
|
7
|
+
require_relative '../lib/github_repo_statistics/version'
|
|
8
|
+
require_relative '../lib/github_repo_statistics/force_merge_report'
|
|
9
|
+
require_relative '../lib/github_repo_statistics/review_report'
|
|
10
|
+
|
|
11
|
+
options = {}
|
|
12
|
+
OptionParser.new do |opts|
|
|
13
|
+
opts.banner = 'Usage: github_repo_statistics [options]'
|
|
14
|
+
|
|
15
|
+
opts.on('--duration-in-days STRING',
|
|
16
|
+
'Number of days to aggregate the changes for [default: 90]') do |duration_in_days|
|
|
17
|
+
options[:duration_in_days] = duration_in_days.to_i
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
opts.on('--default-branch STRING',
|
|
21
|
+
'The default branch to pull and run metrics for [default: master]') do |default_branch|
|
|
22
|
+
options[:default_branch] = default_branch
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
opts.on('--github-token STRING',
|
|
26
|
+
'GitHub API token [default: ""]') do |github_token|
|
|
27
|
+
options[:github_token] = github_token
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
opts.on('--github-repo STRING',
|
|
31
|
+
'GitHub repository name and owner (e.g. octokit/octokit) [default: ""]') do |github_repo|
|
|
32
|
+
options[:github_repo] = github_repo
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
opts.on('-v', '--version', 'Display the version of the gem') do
|
|
36
|
+
puts "github_repo_statistics version #{GithubRepoStatistics::VERSION}"
|
|
37
|
+
exit
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
opts.on('-h', '--help', 'Display this help message') do
|
|
41
|
+
puts opts
|
|
42
|
+
puts <<~EXAMPLES
|
|
43
|
+
|
|
44
|
+
Examples:
|
|
45
|
+
review_report --github-token ghp_bKasj29ndkfdksf3rjt2ngi43j --github-repo octokit/octokit
|
|
46
|
+
EXAMPLES
|
|
47
|
+
exit
|
|
48
|
+
end
|
|
49
|
+
end.parse!
|
|
50
|
+
|
|
51
|
+
DEFAULT_BRANCH = options[:default_branch] || 'master'
|
|
52
|
+
|
|
53
|
+
raise 'Please provide GitHub token using --github-token flag' if options[:github_token].nil?
|
|
54
|
+
raise 'Please provide GitHub repo name using --github-repo flag' if options[:github_repo].nil?
|
|
55
|
+
raise 'Please provide default GitHub branch using --default-branch flag' if DEFAULT_BRANCH.nil?
|
|
56
|
+
|
|
57
|
+
ReviewReport.new(token: options[:github_token], repo: options[:github_repo], branch: DEFAULT_BRANCH,
|
|
58
|
+
duration: options[:duration_in_days] || 30).report
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'octokit'
|
|
4
|
+
require 'date'
|
|
5
|
+
|
|
6
|
+
class ForceMergeReport
|
|
7
|
+
def initialize(token:, repo:, branch:, duration:, ci:)
|
|
8
|
+
@token = token
|
|
9
|
+
@repo = repo
|
|
10
|
+
@branch = branch
|
|
11
|
+
@duration_in_days = duration
|
|
12
|
+
@ci = ci
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def report
|
|
16
|
+
client = Octokit::Client.new(access_token: @token)
|
|
17
|
+
client.auto_paginate = true
|
|
18
|
+
|
|
19
|
+
# Get all pull requests created after the target date
|
|
20
|
+
pull_requests = client.list_issues(@repo, state: 'closed', since: DateTime.now - @duration_in_days)
|
|
21
|
+
|
|
22
|
+
# Sort PRs into monthly chunks
|
|
23
|
+
weekly_pull_requests = pull_requests.group_by { |pr| pr.closed_at.strftime('%Y-%U') }
|
|
24
|
+
|
|
25
|
+
# Initialize a hash to store monthly summaries
|
|
26
|
+
weekly_summaries = Hash.new { |hash, key| hash[key] = { total: 0, failed: 0, workflows: Hash.new(0) } }
|
|
27
|
+
|
|
28
|
+
weeks = @duration_in_days / 7
|
|
29
|
+
|
|
30
|
+
(weekly_pull_requests.keys.sort[-weeks..] || []).each do |month|
|
|
31
|
+
pull_requests_for_month = weekly_pull_requests[month]
|
|
32
|
+
# Iterate through pull requests for the current month
|
|
33
|
+
pull_requests_for_month.each do |issue|
|
|
34
|
+
# Get the pull request details
|
|
35
|
+
pr = client.pull_request(@repo, issue.number)
|
|
36
|
+
|
|
37
|
+
# Check if the pull request is merged
|
|
38
|
+
next unless pr.merged_at
|
|
39
|
+
|
|
40
|
+
# Get the check runs for the pull request's head commit
|
|
41
|
+
check_runs = client.check_runs_for_ref(@repo, pr.head.sha)
|
|
42
|
+
latest_check_runs = check_runs.check_runs.uniq(&:name)
|
|
43
|
+
|
|
44
|
+
statuses = client.statuses(@repo, pr.head.sha)
|
|
45
|
+
latest_statuses = statuses.uniq(&:context)
|
|
46
|
+
|
|
47
|
+
all_checks = latest_check_runs + latest_statuses
|
|
48
|
+
|
|
49
|
+
# Filter checks without meeting the required status checks
|
|
50
|
+
failed_checks = all_checks.reject { |check| check.conclusion == 'success' || check.state == 'success' }
|
|
51
|
+
|
|
52
|
+
# Update monthly summary
|
|
53
|
+
weekly_summaries[month][:total] += 1
|
|
54
|
+
weekly_summaries[month][:failed] += 1 unless failed_checks.empty?
|
|
55
|
+
|
|
56
|
+
# Update workflow counts
|
|
57
|
+
failed_checks.each do |check|
|
|
58
|
+
workflow_name = check.name || check.context
|
|
59
|
+
weekly_summaries[month][:workflows][workflow_name] += 1
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Print details of merged pull requests without meeting the required criteria for the last 6 months
|
|
63
|
+
next if failed_checks.empty?
|
|
64
|
+
|
|
65
|
+
unless @ci
|
|
66
|
+
puts "PR ##{pr.number} - Merged at: #{pr.merged_at}"
|
|
67
|
+
puts 'Failed Checks:'
|
|
68
|
+
failed_checks.each do |check|
|
|
69
|
+
puts "- Workflow: #{check.name || check.context}, Conclusion: #{check.conclusion || check.state}"
|
|
70
|
+
end
|
|
71
|
+
puts '---'
|
|
72
|
+
puts
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Print the weekly summary
|
|
78
|
+
puts 'Weekly Summary:'
|
|
79
|
+
weekly_summaries.each do |month, summary|
|
|
80
|
+
puts "Calendar week #{month}: Total PRs: #{summary[:total]}, Force-merged PRs: #{summary[:failed]}"
|
|
81
|
+
puts 'Failed Workflows:'
|
|
82
|
+
summary[:workflows].each do |workflow, count|
|
|
83
|
+
puts " - #{workflow}: #{count}"
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -205,7 +205,8 @@ class GithubRepoStatistics
|
|
|
205
205
|
|
|
206
206
|
puts "\n#{filename} [#{commit_count}]:#{teams}\n" if @debug
|
|
207
207
|
end
|
|
208
|
-
[all_teams, cross_teams_count, single_ownership_teams_count, files_changed_by_many_teams, total_changes,
|
|
208
|
+
[all_teams, cross_teams_count, single_ownership_teams_count, files_changed_by_many_teams, total_changes,
|
|
209
|
+
file_team_map]
|
|
209
210
|
end
|
|
210
211
|
|
|
211
212
|
def filter_files(file_team_map:)
|
|
@@ -226,7 +227,9 @@ class GithubRepoStatistics
|
|
|
226
227
|
all_files_with_changes = files_with_changes(directory_path: @directory_path, start_date:, end_date:).split.sort
|
|
227
228
|
code_files_with_changes = filter_existing_code_files(all_files_with_changes)
|
|
228
229
|
uniq_code_files_with_changes = code_files_with_changes.uniq
|
|
229
|
-
all_teams, cross_teams_count, single_ownership_teams_count, files_changed_by_many_teams, total_changes, file_team_map = analyze_changed_files(
|
|
230
|
+
all_teams, cross_teams_count, single_ownership_teams_count, files_changed_by_many_teams, total_changes, file_team_map = analyze_changed_files(
|
|
231
|
+
uniq_code_files_with_changes:, start_date:, end_date:
|
|
232
|
+
)
|
|
230
233
|
occurrences = all_teams.flatten.compact.tally
|
|
231
234
|
sorted_occurrences = occurrences.sort_by { |element, count| [-count, element] }
|
|
232
235
|
contributors = Hash[sorted_occurrences]
|
|
@@ -264,7 +267,8 @@ class GithubRepoStatistics
|
|
|
264
267
|
file = line.first
|
|
265
268
|
contributors = line.last.first
|
|
266
269
|
commits = line.last.last
|
|
267
|
-
hotspot_output += " #{file.gsub(@directory_path,
|
|
270
|
+
hotspot_output += " #{file.gsub(@directory_path,
|
|
271
|
+
'')} Contributors: #{contributors} Commits: #{commits} Owner: #{find_owner(file:)}\n"
|
|
268
272
|
end
|
|
269
273
|
|
|
270
274
|
if FILE_OUTPUT
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'octokit'
|
|
4
|
+
require 'date'
|
|
5
|
+
|
|
6
|
+
class ReviewReport
|
|
7
|
+
def initialize(token:, repo:, branch:, duration:)
|
|
8
|
+
@token = token
|
|
9
|
+
@repo = repo
|
|
10
|
+
@branch = branch
|
|
11
|
+
@duration_in_days = duration
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def percentile(array, percentile)
|
|
15
|
+
sorted_array = array.sort
|
|
16
|
+
k = (percentile * (sorted_array.length - 1)).floor
|
|
17
|
+
l = k + 1
|
|
18
|
+
q1 = sorted_array[k]
|
|
19
|
+
q2 = sorted_array[l]
|
|
20
|
+
q1 + (q2 - q1) * (percentile * (sorted_array.length - 1) - k)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def report
|
|
24
|
+
client = Octokit::Client.new(access_token: @token)
|
|
25
|
+
client.auto_paginate = true
|
|
26
|
+
|
|
27
|
+
# Fetch pull requests created after the target date
|
|
28
|
+
pull_requests = client.list_issues(@repo, state: 'closed', since: DateTime.now - @duration_in_days)
|
|
29
|
+
|
|
30
|
+
# Group pull requests by week
|
|
31
|
+
pull_requests_by_week = pull_requests.group_by { |pr| pr[:closed_at].strftime('%Y-%U') }
|
|
32
|
+
|
|
33
|
+
weeks = @duration_in_days / 7
|
|
34
|
+
|
|
35
|
+
# Iterate over months within the days duration limit
|
|
36
|
+
pull_requests_by_week.keys.sort[-weeks..].each do |week|
|
|
37
|
+
total_time_seconds = 0
|
|
38
|
+
total_count = 0
|
|
39
|
+
total_count_calc = 0
|
|
40
|
+
total_reviews = 0
|
|
41
|
+
reviews_with_comments = 0
|
|
42
|
+
change_requested_reviews = 0
|
|
43
|
+
|
|
44
|
+
# Iterate over pull requests in the week
|
|
45
|
+
pull_requests_by_week[week].each do |issue|
|
|
46
|
+
pull_request = client.pull_request(@repo, issue.number)
|
|
47
|
+
last_commit_sha = pull_request[:head][:sha]
|
|
48
|
+
last_commit = client.commit(@repo, last_commit_sha)
|
|
49
|
+
|
|
50
|
+
reviews = []
|
|
51
|
+
review_page = 1
|
|
52
|
+
loop do
|
|
53
|
+
response = client.pull_request_reviews(@repo, pull_request[:number], per_page: 100,
|
|
54
|
+
page: review_page)
|
|
55
|
+
break if response.empty?
|
|
56
|
+
|
|
57
|
+
reviews.concat(response)
|
|
58
|
+
review_page += 1
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
total_reviews += reviews.size
|
|
62
|
+
|
|
63
|
+
next unless reviews.any?
|
|
64
|
+
|
|
65
|
+
last_review = reviews.max_by { |review| review[:submitted_at] }
|
|
66
|
+
time_taken_seconds = (last_review[:submitted_at] - last_commit[:commit][:author][:date]).abs
|
|
67
|
+
|
|
68
|
+
if time_taken_seconds / 3600.0 < 8 && time_taken_seconds / 3600.0 > 0.3
|
|
69
|
+
total_time_seconds += time_taken_seconds
|
|
70
|
+
total_count_calc += 1
|
|
71
|
+
end
|
|
72
|
+
total_count += 1
|
|
73
|
+
# Count reviews with comments
|
|
74
|
+
reviews_with_comments += reviews.count do |review|
|
|
75
|
+
comments = client.pull_request_review_comments(@repo, pull_request[:number], review[:id])
|
|
76
|
+
comments.any?
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Count reviews with "change_requested" state
|
|
80
|
+
change_requested_reviews += reviews.count { |review| review[:state] == 'CHANGES_REQUESTED' }
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
if total_count.positive?
|
|
84
|
+
average_time_hours = total_time_seconds / total_count_calc / 3600.0 # Convert seconds to hours
|
|
85
|
+
puts "Calendar week #{week}: Average time taken for #{total_count} pull requests is #{average_time_hours.round(2)} hours."
|
|
86
|
+
puts " Total reviews: #{total_reviews}"
|
|
87
|
+
puts " Reviews with comments: #{reviews_with_comments}"
|
|
88
|
+
puts " Change requested reviews: #{change_requested_reviews}"
|
|
89
|
+
else
|
|
90
|
+
puts "Month #{week}: No pull requests with reviews."
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
@@ -5,7 +5,7 @@ require_relative '../lib/github_repo_statistics/github_repo_statistics'
|
|
|
5
5
|
RSpec.describe GithubRepoStatistics do
|
|
6
6
|
EXCLUSIONS = %w[exclusion1 exclusion2].freeze
|
|
7
7
|
TEAM_REGEX = '[A-Za-z]+'
|
|
8
|
-
CODEOWNERS_PATH = '
|
|
8
|
+
CODEOWNERS_PATH = 'spec/fixtures/CODEOWNERS'
|
|
9
9
|
BIG_FILE_SIZE = 250
|
|
10
10
|
CI = false
|
|
11
11
|
DEFAULT_BRANCH = 'main'
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: github_repo_statistics
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.0
|
|
4
|
+
version: 2.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Serghei Moret
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2024-02-
|
|
11
|
+
date: 2024-02-27 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: date
|
|
@@ -24,6 +24,20 @@ dependencies:
|
|
|
24
24
|
- - ">="
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
26
|
version: '0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: octokit
|
|
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'
|
|
27
41
|
- !ruby/object:Gem::Dependency
|
|
28
42
|
name: pry
|
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -38,20 +52,38 @@ dependencies:
|
|
|
38
52
|
- - ">="
|
|
39
53
|
- !ruby/object:Gem::Version
|
|
40
54
|
version: '0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: faraday-retry
|
|
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'
|
|
41
69
|
description:
|
|
42
70
|
email:
|
|
43
71
|
- serghei.moret@hellofresh.com
|
|
44
72
|
executables:
|
|
45
73
|
- console
|
|
74
|
+
- force_merge_report
|
|
46
75
|
- github_repo_statistics
|
|
76
|
+
- review_report
|
|
47
77
|
- setup
|
|
48
78
|
extensions: []
|
|
49
79
|
extra_rdoc_files: []
|
|
50
80
|
files:
|
|
51
|
-
- ".github/CODEOWNERS"
|
|
52
81
|
- ".github/workflows/hf_orc-gating.yaml"
|
|
53
82
|
- ".github/workflows/hf_validate-repository-ownership.yaml"
|
|
54
83
|
- ".github/workflows/hf_validate-tribe-squad-labels.yaml"
|
|
84
|
+
- ".gitignore"
|
|
85
|
+
- ".rubocop.yml"
|
|
86
|
+
- ".rubocop_todo.yml"
|
|
55
87
|
- CHANGELOG.md
|
|
56
88
|
- CODE_OF_CONDUCT.md
|
|
57
89
|
- Gemfile
|
|
@@ -60,13 +92,18 @@ files:
|
|
|
60
92
|
- README.md
|
|
61
93
|
- Rakefile
|
|
62
94
|
- bin/console
|
|
95
|
+
- bin/force_merge_report
|
|
63
96
|
- bin/github_repo_statistics
|
|
97
|
+
- bin/review_report
|
|
64
98
|
- bin/setup
|
|
65
99
|
- github_repo_statistics.gemspec
|
|
66
100
|
- lib/github_repo_statistics.rb
|
|
101
|
+
- lib/github_repo_statistics/force_merge_report.rb
|
|
67
102
|
- lib/github_repo_statistics/github_repo_statistics.rb
|
|
103
|
+
- lib/github_repo_statistics/review_report.rb
|
|
68
104
|
- lib/github_repo_statistics/version.rb
|
|
69
105
|
- sig/github_repo_statistics.rbs
|
|
106
|
+
- spec/fixtures/CODEOWNERS
|
|
70
107
|
- spec/fixtures/file.wrong
|
|
71
108
|
- spec/fixtures/file1.swift
|
|
72
109
|
- spec/fixtures/file2.kt
|
|
@@ -102,6 +139,7 @@ signing_key:
|
|
|
102
139
|
specification_version: 4
|
|
103
140
|
summary: This gem prints git ownership insights
|
|
104
141
|
test_files:
|
|
142
|
+
- spec/fixtures/CODEOWNERS
|
|
105
143
|
- spec/fixtures/file.wrong
|
|
106
144
|
- spec/fixtures/file1.swift
|
|
107
145
|
- spec/fixtures/file2.kt
|
|
File without changes
|