github_repo_statistics 2.2.14 → 2.2.16
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/bin/release_merge_report +57 -0
- data/lib/github_repo_statistics/force_merge_report.rb +2 -2
- data/lib/github_repo_statistics/github_repo_statistics.rb +46 -31
- data/lib/github_repo_statistics/release_merge_report.rb +112 -0
- data/lib/github_repo_statistics/review_report.rb +2 -0
- data/lib/github_repo_statistics/version.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b4d3e1e79a329a849e3dc178cd55be3d0b420738ca81f04e8ac54775f1c92e4c
|
4
|
+
data.tar.gz: e609fbeea3149601a2a14d823d4e5fd47b4f09626b651bab2e419261482018f7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ba6a269abb17d017b184baf8f68df4f7ed749d9dca1d7a361baa2a1bba3e17de6255cd10379177089e3ac2f2d660ad01ce7862b37f2c67b02a268cac17e8625f
|
7
|
+
data.tar.gz: 5ab0ae384a28af25dde05d51853969213fa941f7f4add7edbe7718d71d4c03f08955c316c8ebe753be77c72756aaec6775c67187724eb72d4055664575ba1bf1
|
data/Gemfile.lock
CHANGED
@@ -0,0 +1,57 @@
|
|
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/release_merge_report'
|
9
|
+
|
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
|
+
ReleaseMergeReport.new(token: options[:github_token], repo: options[:github_repo], branch_prefix: "release/").report
|
@@ -30,8 +30,6 @@ class ForceMergeReport
|
|
30
30
|
weekly_summaries = Hash.new { |hash, key| hash[key] = { total: 0, failed: 0, workflows: Hash.new(0) } }
|
31
31
|
|
32
32
|
weeks = @duration_in_days / 7
|
33
|
-
require 'pry'
|
34
|
-
binding.pry
|
35
33
|
|
36
34
|
(weekly_pull_requests.keys.sort[-weeks..] || []).each do |month|
|
37
35
|
pull_requests_for_month = weekly_pull_requests[month]
|
@@ -93,6 +91,8 @@ class ForceMergeReport
|
|
93
91
|
puts " - #{workflow}: #{count}"
|
94
92
|
end
|
95
93
|
|
94
|
+
# ENV['BQ_CREDENTIALS'] = `cat /Users/serghei.moret/.config/gcloud/application_default_credentials.json`
|
95
|
+
|
96
96
|
if ENV['BQ_CREDENTIALS']
|
97
97
|
require "google/cloud/bigquery"
|
98
98
|
require "json"
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'pry'
|
4
4
|
require 'date'
|
5
|
+
require 'csv'
|
5
6
|
|
6
7
|
class GithubRepoStatistics
|
7
8
|
def initialize(directory_path:, duration_in_days:, begin_time:, debug: nil, steps: 1)
|
@@ -145,6 +146,10 @@ class GithubRepoStatistics
|
|
145
146
|
count
|
146
147
|
end
|
147
148
|
|
149
|
+
def count_lines_of_code(file)
|
150
|
+
File.foreach(file).reject { |line| line.match(%r{^\s*(//|/\*.*\*/|\s*$)}) }.count
|
151
|
+
end
|
152
|
+
|
148
153
|
def filter_existing_code_files(files)
|
149
154
|
files.select do |f|
|
150
155
|
next unless File.exist?(f)
|
@@ -242,7 +247,9 @@ class GithubRepoStatistics
|
|
242
247
|
files_with_single_contributor_percentage = (100 - ((files_changed_by_many_teams.to_f / file_count) * 100)).round(2)
|
243
248
|
hotspot_lines = count_hotspot_lines(filtered_files.keys)
|
244
249
|
big_files_count = count_big_files(@directory_path)
|
245
|
-
|
250
|
+
|
251
|
+
# ENV['BQ_CREDENTIALS'] = `cat /Users/serghei.moret/.config/gcloud/application_default_credentials.json`
|
252
|
+
|
246
253
|
if ENV['BQ_CREDENTIALS']
|
247
254
|
require "google/cloud/bigquery"
|
248
255
|
require "json"
|
@@ -251,38 +258,39 @@ class GithubRepoStatistics
|
|
251
258
|
project_id: "hellofresh-android",
|
252
259
|
credentials: creds
|
253
260
|
)
|
254
|
-
dataset = bigquery.dataset "
|
261
|
+
dataset = bigquery.dataset "github_data"
|
255
262
|
|
256
263
|
files_with_multiple_contributor = file_team_map.count
|
257
264
|
big_files_with_multiple_contributors = filtered_top_touched_files.count
|
258
265
|
total_files_changed = uniq_code_files_with_changes.count
|
259
266
|
|
267
|
+
platform = if @directory_path == 'HelloFresh/HelloFresh/'
|
268
|
+
'ios'
|
269
|
+
elsif @directory_path == 'features/legacy/'
|
270
|
+
'android'
|
271
|
+
end
|
272
|
+
|
260
273
|
query = <<~SQL
|
261
|
-
|
262
|
-
|
263
|
-
ON target.date = source.date AND target.platform = source.platform
|
264
|
-
WHEN MATCHED THEN
|
265
|
-
UPDATE SET
|
266
|
-
target.platform = '#{@directory_path}',
|
267
|
-
target.single_contributor_percentage = #{files_with_single_contributor_percentage},
|
268
|
-
target.files_changed_by_many_teams = #{files_changed_by_many_teams},
|
269
|
-
target.file_count = #{file_count},
|
270
|
-
target.cross_teams_count = #{cross_teams_count},
|
271
|
-
target.single_ownership_teams_count = #{single_ownership_teams_count},
|
272
|
-
target.hotspot_changes_percentage = #{hotspot_changes_percentage},
|
273
|
-
target.churn_count = #{churn_count},
|
274
|
-
target.total_changes = #{total_changes},
|
275
|
-
target.files_with_multiple_contributor = #{files_with_multiple_contributor},
|
276
|
-
target.big_files_with_multiple_contributors = #{big_files_with_multiple_contributors},
|
277
|
-
target.total_files_changed = #{total_files_changed},
|
278
|
-
target.hotspot_lines = #{hotspot_lines},
|
279
|
-
target.big_files_count = #{big_files_count}
|
280
|
-
WHEN NOT MATCHED THEN
|
281
|
-
INSERT (date,platform,single_contributor_percentage,files_changed_by_many_teams,file_count,cross_teams_count,single_ownership_teams_count,hotspot_changes_percentage,churn_count,total_changes,files_with_multiple_contributor,big_files_with_multiple_contributors,total_files_changed,hotspot_lines,big_files_count)
|
282
|
-
VALUES (#{@begin_time}, #{@directory_path}, #{files_with_single_contributor_percentage}, #{files_changed_by_many_teams}, #{file_count}, ##{cross_teams_count}, #{single_ownership_teams_count}, #{hotspot_changes_percentage}, #{churn_count}, #{total_changes}, #{files_with_multiple_contributor}, #{big_files_with_multiple_contributors}, #{total_files_changed}, #{hotspot_lines}, #{big_files_count});
|
274
|
+
INSERT INTO modularization (date, platform, single_contributor_percentage, files_changed_by_many_teams, file_count, cross_teams_count, single_ownership_teams_count, hotspot_changes_percentage, churn_count, total_changes, files_with_multiple_contributor, big_files_with_multiple_contributors, total_files_changed, hotspot_lines, big_files_count)
|
275
|
+
VALUES ('#{@begin_time}', '#{platform}', #{files_with_single_contributor_percentage}, #{files_changed_by_many_teams}, #{file_count}, #{cross_teams_count}, #{single_ownership_teams_count}, #{hotspot_changes_percentage}, #{churn_count}, #{total_changes}, #{files_with_multiple_contributor}, #{big_files_with_multiple_contributors}, #{total_files_changed}, #{hotspot_lines}, #{big_files_count});
|
283
276
|
SQL
|
284
277
|
|
285
278
|
dataset.query(query)
|
279
|
+
|
280
|
+
# delete_query = <<~SQL
|
281
|
+
# DELETE FROM modularization
|
282
|
+
# WHERE CONCAT(DATE(date), ' ', TIME(date)) NOT IN (
|
283
|
+
# SELECT CONCAT(DATE(date), ' ', TIME(date))
|
284
|
+
# FROM modularization AS m1
|
285
|
+
# WHERE TIME(date) = (
|
286
|
+
# SELECT MAX(TIME(date))
|
287
|
+
# FROM modularization AS m2
|
288
|
+
# WHERE DATE(m1.date) = DATE(m2.date)
|
289
|
+
# )
|
290
|
+
# );
|
291
|
+
# SQL
|
292
|
+
|
293
|
+
# dataset.query(delete_query)
|
286
294
|
end
|
287
295
|
|
288
296
|
puts ''
|
@@ -306,23 +314,30 @@ class GithubRepoStatistics
|
|
306
314
|
puts " *Contributors:* #{contributors}"
|
307
315
|
|
308
316
|
if HOTSPOT
|
309
|
-
hotspot_output =
|
317
|
+
hotspot_output = []
|
310
318
|
|
311
319
|
filtered_top_touched_files.each do |line|
|
312
|
-
hotspot_output += "\n"
|
313
320
|
file = line.first
|
314
321
|
contributors = line.last.first
|
322
|
+
lines_of_code = count_lines_of_code(file)
|
315
323
|
commits = line.last.last
|
316
|
-
|
317
|
-
|
324
|
+
owner = find_owner(file:)
|
325
|
+
|
326
|
+
hotspot_output << [file.gsub(@directory_path, ''), contributors, lines_of_code, commits, owner]
|
318
327
|
end
|
319
328
|
|
320
329
|
if FILE_OUTPUT
|
321
|
-
|
322
|
-
|
330
|
+
CSV.open('hotspot.csv', 'w') do |csv|
|
331
|
+
csv << ['File', 'Contributors', 'Lines', 'Commits', 'Owner']
|
332
|
+
hotspot_output.each do |row|
|
333
|
+
csv << row
|
334
|
+
end
|
323
335
|
end
|
324
336
|
else
|
325
|
-
puts
|
337
|
+
puts "\n *Hotspot files(#{filtered_top_touched_files.count}):*\n"
|
338
|
+
hotspot_output.each do |row|
|
339
|
+
puts " #{row[0]} Contributors: #{row[1]} Lines: #{row[2]} Commits: #{row[3]} Owner: #{row[4]}"
|
340
|
+
end
|
326
341
|
end
|
327
342
|
end
|
328
343
|
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'octokit'
|
2
|
+
require 'json'
|
3
|
+
require 'google/cloud/bigquery'
|
4
|
+
|
5
|
+
class ReleaseMergeReport
|
6
|
+
def initialize(token:, repo:, branch_prefix:)
|
7
|
+
@token = token
|
8
|
+
@repo = repo
|
9
|
+
@branch_prefix = branch_prefix
|
10
|
+
end
|
11
|
+
|
12
|
+
def report
|
13
|
+
branch_counts = count_merged_pull_requests_per_branch
|
14
|
+
grouped_branch_counts = group_branch_counts(branch_counts)
|
15
|
+
require 'pry'
|
16
|
+
binding.pry
|
17
|
+
|
18
|
+
# Print the grouped branch counts
|
19
|
+
puts 'Branches with Merged Pull Requests:'
|
20
|
+
grouped_branch_counts.each do |branch, count|
|
21
|
+
puts "#{branch}: #{count}"
|
22
|
+
end
|
23
|
+
|
24
|
+
ENV['BQ_CREDENTIALS'] = `cat /Users/serghei.moret/.config/gcloud/application_default_credentials.json`
|
25
|
+
|
26
|
+
export_to_bigquery(grouped_branch_counts) if ENV['BQ_CREDENTIALS']
|
27
|
+
|
28
|
+
grouped_branch_counts
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def count_merged_pull_requests_per_branch
|
34
|
+
client = Octokit::Client.new(access_token: @token)
|
35
|
+
client.auto_paginate = true
|
36
|
+
|
37
|
+
tags = client.tags(@repo)
|
38
|
+
branch_counts = Hash.new(0)
|
39
|
+
|
40
|
+
tags.each do |tag|
|
41
|
+
next if !tag.name.match?(/^(v23|v24)\./) && !tag.name.match?(/^(23|24)\./)
|
42
|
+
|
43
|
+
# Extract release version from the tag name
|
44
|
+
release_version = tag.name.gsub('v', '')
|
45
|
+
|
46
|
+
# Construct branch name with the specified prefix
|
47
|
+
branch_name = "#{@branch_prefix}#{release_version}"
|
48
|
+
|
49
|
+
# Count merged pull requests associated with the branch
|
50
|
+
pull_requests = client.pull_requests(@repo, state: 'closed', sort: 'updated', direction: 'desc', base: branch_name)
|
51
|
+
.select { |pr| pr.merged_at }
|
52
|
+
|
53
|
+
branch_counts[branch_name] = pull_requests.size
|
54
|
+
end
|
55
|
+
|
56
|
+
branch_counts
|
57
|
+
end
|
58
|
+
|
59
|
+
def group_branch_counts(branch_counts)
|
60
|
+
patch_counts = Hash.new(0)
|
61
|
+
hotfix_counts = Hash.new { |hash, key| hash[key] = Hash.new(0) }
|
62
|
+
|
63
|
+
branch_counts.each do |branch, count|
|
64
|
+
major_minor_version, patch_version = branch.match(/^#{@branch_prefix}(\d+\.\d+)(?:\.(\d+))?/)&.captures
|
65
|
+
|
66
|
+
if patch_version.nil?
|
67
|
+
# Branch is a patch version
|
68
|
+
patch_counts[major_minor_version] += count
|
69
|
+
elsif count > 0
|
70
|
+
# Branch is a hotfix version
|
71
|
+
hotfix_counts[major_minor_version][patch_version] += count
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Sum up the counts for hotfix versions within the same major and minor version
|
76
|
+
hotfix_counts.each do |major_minor_version, hotfixes|
|
77
|
+
hotfix_counts[major_minor_version] = hotfixes.values.sum
|
78
|
+
end
|
79
|
+
|
80
|
+
{ patch_versions: patch_counts, hotfix_versions: hotfix_counts }
|
81
|
+
end
|
82
|
+
|
83
|
+
def export_to_bigquery(branch_counts)
|
84
|
+
require "google/cloud/bigquery"
|
85
|
+
require "json"
|
86
|
+
creds = JSON.parse(ENV['BQ_CREDENTIALS'])
|
87
|
+
bigquery = Google::Cloud::Bigquery.new(
|
88
|
+
project_id: "hellofresh-android",
|
89
|
+
credentials: creds
|
90
|
+
)
|
91
|
+
dataset = bigquery.dataset "github_data"
|
92
|
+
|
93
|
+
date = DateTime.now
|
94
|
+
|
95
|
+
branch_counts.each do |branch, count|
|
96
|
+
query = <<~SQL
|
97
|
+
MERGE INTO release_merges AS target
|
98
|
+
USING (SELECT '#{branch}' AS release, '#{@repo}' AS platform) AS source
|
99
|
+
ON target.release = source.release AND target.platform = source.platform
|
100
|
+
WHEN MATCHED THEN
|
101
|
+
UPDATE SET
|
102
|
+
target.merge_count = #{count},
|
103
|
+
target.timestamp = '#{date}'
|
104
|
+
WHEN NOT MATCHED THEN
|
105
|
+
INSERT (release, merge_count, platform, timestamp)
|
106
|
+
VALUES ('#{branch}', #{count}, '#{@repo}', '#{date}');
|
107
|
+
SQL
|
108
|
+
date -= 7
|
109
|
+
dataset.query(query)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -87,6 +87,8 @@ class ReviewReport
|
|
87
87
|
puts " Reviews with comments: #{reviews_with_comments}"
|
88
88
|
puts " Change requested reviews: #{change_requested_reviews}"
|
89
89
|
|
90
|
+
# ENV['BQ_CREDENTIALS'] = `cat /Users/serghei.moret/.config/gcloud/application_default_credentials.json`
|
91
|
+
|
90
92
|
if ENV['BQ_CREDENTIALS']
|
91
93
|
require "google/cloud/bigquery"
|
92
94
|
require "json"
|
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.2.
|
4
|
+
version: 2.2.16
|
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-
|
11
|
+
date: 2024-04-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: date
|
@@ -87,6 +87,7 @@ executables:
|
|
87
87
|
- console
|
88
88
|
- force_merge_report
|
89
89
|
- github_repo_statistics
|
90
|
+
- release_merge_report
|
90
91
|
- review_report
|
91
92
|
- setup
|
92
93
|
extensions: []
|
@@ -108,12 +109,14 @@ files:
|
|
108
109
|
- bin/console
|
109
110
|
- bin/force_merge_report
|
110
111
|
- bin/github_repo_statistics
|
112
|
+
- bin/release_merge_report
|
111
113
|
- bin/review_report
|
112
114
|
- bin/setup
|
113
115
|
- github_repo_statistics.gemspec
|
114
116
|
- lib/github_repo_statistics.rb
|
115
117
|
- lib/github_repo_statistics/force_merge_report.rb
|
116
118
|
- lib/github_repo_statistics/github_repo_statistics.rb
|
119
|
+
- lib/github_repo_statistics/release_merge_report.rb
|
117
120
|
- lib/github_repo_statistics/review_report.rb
|
118
121
|
- lib/github_repo_statistics/version.rb
|
119
122
|
- sig/github_repo_statistics.rbs
|