git_ownership_insights 2.0.2 → 2.0.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dd5244ef2598407471bd2b55728b3c86cd48dfce9d4e6e2956b8b0ba2b0cf7ef
4
- data.tar.gz: 9e21ea07a0455fa4792687ee4b0180c8d3dfee0889801f4846ccec8dec04f33c
3
+ metadata.gz: eb151470ef24ed0e040026ced3fb187e7cc02012ba0564dd062be4611b8219a8
4
+ data.tar.gz: 64b844eebef335350f0fa9fa939c0d338910b5a711652a8c9ba926a7caec3456
5
5
  SHA512:
6
- metadata.gz: e07e63cba3b86fdedb333cf5063f559ac36d545fd04482f5339e3f939b4e207d8d99cbeb59e75832132c9501d74b73f251c5de343f76258ef1cbeadc8b06a2f5
7
- data.tar.gz: 9abb98a4c09c8ad308b8fdd2bd591bbd8a291da716090047b26836c9d801780949fde3389348b17739ecd5fe436726bb6efcaab271bf67edf014def7b0c2b992
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 15:39:52 UTC using RuboCop version 1.60.0.
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: 4
16
+ # Offense count: 6
17
17
  # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes.
18
18
  Metrics/AbcSize:
19
- Max: 181
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: 76
25
+ Max: 75
26
26
 
27
27
  # Offense count: 1
28
28
  # Configuration parameters: CountComments, CountAsOne.
29
29
  Metrics/ClassLength:
30
- Max: 200
30
+ Max: 207
31
31
 
32
- # Offense count: 1
32
+ # Offense count: 2
33
33
  # Configuration parameters: AllowedMethods, AllowedPatterns.
34
34
  Metrics/CyclomaticComplexity:
35
- Max: 28
35
+ Max: 12
36
36
 
37
- # Offense count: 4
37
+ # Offense count: 6
38
38
  # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
39
39
  Metrics/MethodLength:
40
- Max: 109
40
+ Max: 49
41
41
 
42
42
  # Offense count: 1
43
43
  # Configuration parameters: AllowedMethods, AllowedPatterns.
44
44
  Metrics/PerceivedComplexity:
45
- Max: 29
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: 11
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
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- git_ownership_insights (2.0.2)
4
+ git_ownership_insights (2.0.4)
5
5
  date
6
6
  pry
7
7
 
@@ -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
@@ -73,7 +122,7 @@ class GitOwnershipInsights
73
122
  count += 1 if lines_count > size
74
123
  end
75
124
 
76
- puts " *Current(\\*) total number of code files longer than #{size} lines:* #{count}"
125
+ puts " *Current total number of code files longer than #{size} lines:* #{count}"
77
126
  end
78
127
 
79
128
  def count_hotspot_lines(files)
@@ -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 contribution_message
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
- occurrences = all_teams.flatten.compact.tally
168
- sorted_occurrences = occurrences.sort_by { |element, count| [-count, element] }
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 ''
@@ -198,61 +251,22 @@ class GitOwnershipInsights
198
251
  puts " *Total amount of commits to #{CODE_EXTENSIONS} files:* #{total_changes}"
199
252
  puts " *Total #{CODE_EXTENSIONS} files changed:* #{uniq_code_files_with_changes.count}"
200
253
  count_big_files(@directory_path)
201
- puts " *Current(\\*) total of #{CODE_EXTENSIONS} files in the folder:* #{file_count}"
254
+ puts " *Current total of #{CODE_EXTENSIONS} files in the folder:* #{file_count}"
202
255
  puts " *Contributors:* #{contributors}"
203
- puts "* means that it the current(instant) repository value, all the other metrics are done over #{duration_in_days} days period"
204
256
 
205
257
  if HOTSPOT
206
258
  puts "\n"
207
259
  puts ' *Hotspot changes:*'
208
260
  filtered_top_touched_files.each do |line|
209
- puts " #{line.first.gsub(@directory_path, '')} Contributors: #{line.last.first} Commits: #{line.last.last}"
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:)}"
210
265
  end
211
266
  end
212
267
 
213
- if CODEOWNERS
214
- puts "\n"
215
- puts '*Code ownership data:*'
216
- codeowners = read_codeowners_file
217
-
218
- owners_data = Hash.new do |hash, key|
219
- hash[key] = { directories: Hash.new do |h, k|
220
- h[k] = { files: [] }
221
- end, churn_count: 0 }
222
- end
223
-
224
- file_team_map.each do |file, count|
225
- owners = find_owners(file, codeowners)
226
- owners.each do |owner|
227
- owners_data[owner][:churn_count] += count.last
228
-
229
- dir_path = File.dirname(file)
230
- owners_data[owner][:directories][dir_path][:files] << { name: File.basename(file), count: }
231
- end
232
- end
233
-
234
- # Sort owners_data by total count in descending order
235
- sorted_owners_data = owners_data.sort_by { |_, data| -data[:churn_count] }
236
-
237
- # Take the last 5 elements
238
- top_owners_data = sorted_owners_data.last(TOP_CONTRIBUTED_TEAMS.to_i)
239
-
240
- converted_team_map = file_team_map.transform_keys { |key| File.basename(key) }
268
+ handle_codeowners(file_team_map:) if CODEOWNERS
241
269
 
242
- puts ' Codeownership data:'
243
- top_owners_data.each do |owner, data|
244
- puts " #{owner.split('/').last}:\n Total Count: #{data[:churn_count]}"
245
- data[:directories].each do |dir, dir_data|
246
- puts " Directory: #{dir}\n Top files:"
247
- dir_data[:files].each do |file_data|
248
- next if converted_team_map[File.basename(file_data[:name])].nil?
249
-
250
- contributors = converted_team_map[file_data[:name]]&.first&.empty? ? ['Excluded contributor'] : converted_team_map[file_data[:name]].first
251
- puts " #{File.basename(file_data[:name])} - #{file_data[:count].last} #{contributors}}"
252
- end
253
- end
254
- end
255
- end
256
270
  @steps -= 1
257
271
 
258
272
  return unless @steps.positive?
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class GitOwnershipInsights
4
- VERSION = '2.0.2'
4
+ VERSION = '2.0.4'
5
5
  end
@@ -52,15 +52,14 @@ RSpec.describe GitOwnershipInsights do
52
52
  *[".swift", ".kt"] files exceeding 250 lines with multiple contributors:* 1
53
53
  *Total amount of commits to [".swift", ".kt"] files:* 7
54
54
  *Total [".swift", ".kt"] files changed:* 3
55
- *Current(\\*) total number of code files longer than 250 lines:* 2
56
- *Current(\\*) total of [".swift", ".kt"] files in the folder:* 4
55
+ *Current total number of code files longer than 250 lines:* 2
56
+ *Current total of [".swift", ".kt"] files in the folder:* 4
57
57
  *Contributors:* {"FIOS"=>5, "FAND"=>2}
58
- * means that it the current(instant) repository value, all the other metrics are done over 7 days period
59
58
 
60
- Hotspot changes:
59
+ *Hotspot changes:*
61
60
  /file1.swift Contributors: ["FIOS", "FAND"] Commits: 3
62
61
 
63
- Code ownership data:
62
+ *Code ownership data:*
64
63
  Codeownership data:
65
64
  FIOS:
66
65
  Total Count: 3
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: git_ownership_insights
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.2
4
+ version: 2.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Serghei Moret