git_ownership_insights 2.0.3 → 2.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop_todo.yml +11 -11
- data/Gemfile.lock +1 -1
- data/lib/git_ownership_insights/git_ownership_insight.rb +77 -62
- data/lib/git_ownership_insights/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eb151470ef24ed0e040026ced3fb187e7cc02012ba0564dd062be4611b8219a8
|
4
|
+
data.tar.gz: 64b844eebef335350f0fa9fa939c0d338910b5a711652a8c9ba926a7caec3456
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f3ab9fa17feabb5b7774f61911ae34dd36605b412a365d3192fb76ec8585569635aa74121b31fb5fbbe67c77fe1b8e77f3983724839a4ff4261b0212f79ccf4d
|
7
|
+
data.tar.gz: 4a176d6a400141e7fda5697d53f54ad4f7d599ab1ec85178798ad70c0188a7145aa49a688be37ad3d7c0e33c20a77ae478211dd6a08718625e87de6f4d33455d
|
data/.rubocop_todo.yml
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# This configuration was generated by
|
2
2
|
# `rubocop --auto-gen-config`
|
3
|
-
# on 2024-01-30
|
3
|
+
# on 2024-01-30 16:41:47 UTC using RuboCop version 1.60.0.
|
4
4
|
# The point is for the user to remove these configuration records
|
5
5
|
# one by one as the offenses are removed from the code base.
|
6
6
|
# Note that changes in the inspected code, or installation of new
|
@@ -13,36 +13,36 @@ Lint/ConstantDefinitionInBlock:
|
|
13
13
|
Exclude:
|
14
14
|
- 'spec/git_ownership_insights_spec.rb'
|
15
15
|
|
16
|
-
# Offense count:
|
16
|
+
# Offense count: 6
|
17
17
|
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes.
|
18
18
|
Metrics/AbcSize:
|
19
|
-
Max:
|
19
|
+
Max: 92
|
20
20
|
|
21
21
|
# Offense count: 4
|
22
22
|
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
|
23
23
|
# AllowedMethods: refine
|
24
24
|
Metrics/BlockLength:
|
25
|
-
Max:
|
25
|
+
Max: 75
|
26
26
|
|
27
27
|
# Offense count: 1
|
28
28
|
# Configuration parameters: CountComments, CountAsOne.
|
29
29
|
Metrics/ClassLength:
|
30
|
-
Max:
|
30
|
+
Max: 207
|
31
31
|
|
32
|
-
# Offense count:
|
32
|
+
# Offense count: 2
|
33
33
|
# Configuration parameters: AllowedMethods, AllowedPatterns.
|
34
34
|
Metrics/CyclomaticComplexity:
|
35
|
-
Max:
|
35
|
+
Max: 12
|
36
36
|
|
37
|
-
# Offense count:
|
37
|
+
# Offense count: 6
|
38
38
|
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
|
39
39
|
Metrics/MethodLength:
|
40
|
-
Max:
|
40
|
+
Max: 49
|
41
41
|
|
42
42
|
# Offense count: 1
|
43
43
|
# Configuration parameters: AllowedMethods, AllowedPatterns.
|
44
44
|
Metrics/PerceivedComplexity:
|
45
|
-
Max:
|
45
|
+
Max: 12
|
46
46
|
|
47
47
|
# Offense count: 1
|
48
48
|
# Configuration parameters: AllowedConstants.
|
@@ -57,7 +57,7 @@ Style/MultilineBlockChain:
|
|
57
57
|
Exclude:
|
58
58
|
- 'lib/git_ownership_insights/git_ownership_insight.rb'
|
59
59
|
|
60
|
-
# Offense count:
|
60
|
+
# Offense count: 12
|
61
61
|
# This cop supports safe autocorrection (--autocorrect).
|
62
62
|
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns.
|
63
63
|
# URISchemes: http, https
|
data/Gemfile.lock
CHANGED
@@ -53,6 +53,55 @@ class GitOwnershipInsights
|
|
53
53
|
codeowners[best_match].split(' ')
|
54
54
|
end
|
55
55
|
|
56
|
+
def handle_codeowners(file_team_map:)
|
57
|
+
puts "\n"
|
58
|
+
puts '*Code ownership data:*'
|
59
|
+
codeowners = read_codeowners_file
|
60
|
+
|
61
|
+
owners_data = Hash.new do |hash, key|
|
62
|
+
hash[key] = { directories: Hash.new do |h, k|
|
63
|
+
h[k] = { files: [] }
|
64
|
+
end, churn_count: 0 }
|
65
|
+
end
|
66
|
+
|
67
|
+
file_team_map.each do |file, count|
|
68
|
+
owners = find_owners(file, codeowners)
|
69
|
+
owners.each do |owner|
|
70
|
+
owners_data[owner][:churn_count] += count.last
|
71
|
+
|
72
|
+
dir_path = File.dirname(file)
|
73
|
+
owners_data[owner][:directories][dir_path][:files] << { name: File.basename(file), count: }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Sort owners_data by total count in descending order
|
78
|
+
sorted_owners_data = owners_data.sort_by { |_, data| -data[:churn_count] }
|
79
|
+
|
80
|
+
# Take the last 5 elements
|
81
|
+
top_owners_data = sorted_owners_data.last(TOP_CONTRIBUTED_TEAMS.to_i)
|
82
|
+
|
83
|
+
converted_team_map = file_team_map.transform_keys { |key| File.basename(key) }
|
84
|
+
|
85
|
+
puts ' Codeownership data:'
|
86
|
+
top_owners_data.each do |owner, data|
|
87
|
+
puts " #{owner.split('/').last}:\n Total Count: #{data[:churn_count]}"
|
88
|
+
data[:directories].each do |dir, dir_data|
|
89
|
+
puts " Directory: #{dir}\n Top files:"
|
90
|
+
dir_data[:files].each do |file_data|
|
91
|
+
next if converted_team_map[File.basename(file_data[:name])].nil?
|
92
|
+
|
93
|
+
contributors = converted_team_map[file_data[:name]]&.first&.empty? ? ['Excluded contributor'] : converted_team_map[file_data[:name]].first
|
94
|
+
puts " #{File.basename(file_data[:name])} - #{file_data[:count].last} #{contributors}}"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def find_owner(file:)
|
101
|
+
codeowners = read_codeowners_file
|
102
|
+
find_owners(file, codeowners)
|
103
|
+
end
|
104
|
+
|
56
105
|
def count_big_files(directory_path, size: BIG_FILE_SIZE)
|
57
106
|
size = size.to_i
|
58
107
|
# Get a list of all files in the specified directory
|
@@ -125,21 +174,12 @@ class GitOwnershipInsights
|
|
125
174
|
`git log --pretty=format:"%s" --since="#{start_date}" --until="#{end_date}" --follow -- "#{file}"`
|
126
175
|
end
|
127
176
|
|
128
|
-
def
|
129
|
-
duration_in_days = @duration_in_days.to_i
|
177
|
+
def analyze_changed_files(uniq_code_files_with_changes:, start_date:, end_date:)
|
130
178
|
all_teams = []
|
131
179
|
cross_teams_count = 0
|
132
180
|
single_ownership_teams_count = 0
|
133
181
|
files_changed_by_many_teams = 0
|
134
182
|
total_changes = 0
|
135
|
-
start_date = @begin_time.to_time.to_i - duration_in_days * 86_400 - 30 * 86_400
|
136
|
-
end_date = @begin_time.to_time.to_i - 30 * 86_400
|
137
|
-
git_ls = git_files(directory_path: @directory_path)
|
138
|
-
file_count = filter_existing_code_files(git_ls.split).count
|
139
|
-
all_files_with_changes = files_with_changes(directory_path: @directory_path, start_date:, end_date:).split.sort
|
140
|
-
code_files_with_changes = filter_existing_code_files(all_files_with_changes)
|
141
|
-
uniq_code_files_with_changes = code_files_with_changes.uniq
|
142
|
-
|
143
183
|
file_team_map = {}
|
144
184
|
uniq_code_files_with_changes.each do |file|
|
145
185
|
filename = File.basename(file)
|
@@ -163,22 +203,35 @@ class GitOwnershipInsights
|
|
163
203
|
|
164
204
|
puts "\n#{filename} [#{commit_count}]:#{teams}\n" if @debug
|
165
205
|
end
|
206
|
+
[all_teams, cross_teams_count, single_ownership_teams_count, files_changed_by_many_teams, total_changes, file_team_map]
|
207
|
+
end
|
166
208
|
|
167
|
-
|
168
|
-
|
169
|
-
contributors = Hash[sorted_occurrences]
|
170
|
-
|
171
|
-
churn_count = file_team_map.values.map { |value| value[1] }.sum
|
172
|
-
hotspot_changes_percentage = (churn_count.to_f / total_changes) * 100
|
173
|
-
|
174
|
-
# Filter files based on extension and size
|
175
|
-
filtered_files = file_team_map.select do |file_path|
|
209
|
+
def filter_files(file_team_map:)
|
210
|
+
file_team_map.select do |file_path|
|
176
211
|
next unless File.exist?(file_path)
|
177
212
|
|
178
213
|
# Check if the file size is more than BIG_FILE_SIZE lines (excluding empty and commented lines)
|
179
214
|
File.foreach(file_path).reject { |line| line.match(%r{^\s*(//|/\*.*\*/|\s*$)}) }.count > BIG_FILE_SIZE.to_i
|
180
215
|
end
|
216
|
+
end
|
181
217
|
|
218
|
+
def contribution_message
|
219
|
+
duration_in_days = @duration_in_days.to_i
|
220
|
+
start_date = @begin_time.to_time.to_i - duration_in_days * 86_400 - 30 * 86_400
|
221
|
+
end_date = @begin_time.to_time.to_i - 30 * 86_400
|
222
|
+
git_ls = git_files(directory_path: @directory_path)
|
223
|
+
file_count = filter_existing_code_files(git_ls.split).count
|
224
|
+
all_files_with_changes = files_with_changes(directory_path: @directory_path, start_date:, end_date:).split.sort
|
225
|
+
code_files_with_changes = filter_existing_code_files(all_files_with_changes)
|
226
|
+
uniq_code_files_with_changes = code_files_with_changes.uniq
|
227
|
+
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:)
|
228
|
+
occurrences = all_teams.flatten.compact.tally
|
229
|
+
sorted_occurrences = occurrences.sort_by { |element, count| [-count, element] }
|
230
|
+
contributors = Hash[sorted_occurrences]
|
231
|
+
churn_count = file_team_map.values.map { |value| value[1] }.sum
|
232
|
+
hotspot_changes_percentage = (churn_count.to_f / total_changes) * 100
|
233
|
+
# Filter files based on extension, existence and size
|
234
|
+
filtered_files = filter_files(file_team_map:)
|
182
235
|
filtered_top_touched_files = filtered_files.sort_by { |element, count| [-count.last, element] }
|
183
236
|
|
184
237
|
puts ''
|
@@ -205,53 +258,15 @@ class GitOwnershipInsights
|
|
205
258
|
puts "\n"
|
206
259
|
puts ' *Hotspot changes:*'
|
207
260
|
filtered_top_touched_files.each do |line|
|
208
|
-
|
261
|
+
file = line.first
|
262
|
+
contributors = line.last.first
|
263
|
+
commits = line.last.last
|
264
|
+
puts " #{file.gsub(@directory_path, '')} Contributors: #{contributors} Commits: #{commits} Owner: #{find_owner(file:)}"
|
209
265
|
end
|
210
266
|
end
|
211
267
|
|
212
|
-
if CODEOWNERS
|
213
|
-
puts "\n"
|
214
|
-
puts '*Code ownership data:*'
|
215
|
-
codeowners = read_codeowners_file
|
216
|
-
|
217
|
-
owners_data = Hash.new do |hash, key|
|
218
|
-
hash[key] = { directories: Hash.new do |h, k|
|
219
|
-
h[k] = { files: [] }
|
220
|
-
end, churn_count: 0 }
|
221
|
-
end
|
222
|
-
|
223
|
-
file_team_map.each do |file, count|
|
224
|
-
owners = find_owners(file, codeowners)
|
225
|
-
owners.each do |owner|
|
226
|
-
owners_data[owner][:churn_count] += count.last
|
227
|
-
|
228
|
-
dir_path = File.dirname(file)
|
229
|
-
owners_data[owner][:directories][dir_path][:files] << { name: File.basename(file), count: }
|
230
|
-
end
|
231
|
-
end
|
232
|
-
|
233
|
-
# Sort owners_data by total count in descending order
|
234
|
-
sorted_owners_data = owners_data.sort_by { |_, data| -data[:churn_count] }
|
235
|
-
|
236
|
-
# Take the last 5 elements
|
237
|
-
top_owners_data = sorted_owners_data.last(TOP_CONTRIBUTED_TEAMS.to_i)
|
238
|
-
|
239
|
-
converted_team_map = file_team_map.transform_keys { |key| File.basename(key) }
|
268
|
+
handle_codeowners(file_team_map:) if CODEOWNERS
|
240
269
|
|
241
|
-
puts ' Codeownership data:'
|
242
|
-
top_owners_data.each do |owner, data|
|
243
|
-
puts " #{owner.split('/').last}:\n Total Count: #{data[:churn_count]}"
|
244
|
-
data[:directories].each do |dir, dir_data|
|
245
|
-
puts " Directory: #{dir}\n Top files:"
|
246
|
-
dir_data[:files].each do |file_data|
|
247
|
-
next if converted_team_map[File.basename(file_data[:name])].nil?
|
248
|
-
|
249
|
-
contributors = converted_team_map[file_data[:name]]&.first&.empty? ? ['Excluded contributor'] : converted_team_map[file_data[:name]].first
|
250
|
-
puts " #{File.basename(file_data[:name])} - #{file_data[:count].last} #{contributors}}"
|
251
|
-
end
|
252
|
-
end
|
253
|
-
end
|
254
|
-
end
|
255
270
|
@steps -= 1
|
256
271
|
|
257
272
|
return unless @steps.positive?
|