github_repo_statistics 2.0.8 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 259f604375dde894d7103813f052aeb59b5317afc190856ed3cbea8eccc2d1d3
4
- data.tar.gz: 1b5ae5061a41f3cc8985691c25cce3615bd93b477be8c2c1917fe0565dd8b4d7
3
+ metadata.gz: 7625d04f395885b90b8f2faad896127df5e16604818d095f3e5455bf49d9c899
4
+ data.tar.gz: e6fec8b96326f6aff20e8ba63ccde41d3605381a692df51a3e7dceac8f0b218c
5
5
  SHA512:
6
- metadata.gz: 66bdf997d8853d016513c603b5264d6b30816d99c419c73d9c2ab2a6ebe7e6a36f1e25c9d2faf58ad12b4e16c2da83dabe2ede7183404bb8f880555655d4b920
7
- data.tar.gz: 9c6b4d5d36eeeb509294542b36bd9ce636b8d0adbfd4808d3a87d7de05fb0b1001a763ddc234116e9879a70c4c8e4370f666bb3d696e013d2b36b2784de741a1
6
+ metadata.gz: c943a3541d786ca121518c4545a0d82f036d71492ad83bec46ffffd187e4f6536475688acd1f3841e20ded27b29eb4759675e40ef86d55a66ef2ae147487e8fe
7
+ data.tar.gz: 76c00e2a9cc37a21026cb31229f6c7c71733428b1fc1d06fd24f5c8e9d5c8777d85d23e45c4af73ddaf699d6c8afd63dab38dc40af2780c0f73f704e32b0b4b2
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ .idea/*
2
+ *.gem
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.8)
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
- TODO: Delete this and the text below, and describe your gem
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
- TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org.
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
- TODO: Write usage instructions here
22
-
23
- ## Development
24
-
25
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
26
-
27
- To install this gem onto your local machine, run `bundle exec rake install`. 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).
28
-
29
- ## Contributing
30
-
31
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/github_repo_statistics. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/github_repo_statistics/blob/main/CODE_OF_CONDUCT.md).
32
-
33
- ## License
34
-
35
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
36
-
37
- ## Code of Conduct
38
-
39
- Everyone interacting in the GitOwnershipInsights project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/github_repo_statistics/blob/main/CODE_OF_CONDUCT.md).
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
@@ -25,5 +25,7 @@ Gem::Specification.new do |spec|
25
25
  spec.require_paths = ['lib']
26
26
 
27
27
  spec.add_dependency 'date'
28
+ spec.add_dependency 'octokit'
28
29
  spec.add_dependency 'pry'
30
+ spec.add_dependency 'faraday-retry'
29
31
  end
@@ -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, file_team_map]
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(uniq_code_files_with_changes:, start_date:, end_date:)
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, '')} Contributors: #{contributors} Commits: #{commits} Owner: #{find_owner(file:)}\n"
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class GithubRepoStatistics
4
- VERSION = '2.0.8'
4
+ VERSION = '2.1.0'
5
5
  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 = '.github/CODEOWNERS'
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.8
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-23 00:00:00.000000000 Z
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