code_ownership 1.32.8 → 1.32.10

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: 18e702e6635a7d3d264eaae41f61fb6f83e423e8021ca76e92a6972fe5444848
4
- data.tar.gz: 90f34b5a02b21a1f78ff781f7ef3e8220a45549e77c4b93b829381a57f37ba7c
3
+ metadata.gz: e0713332aa7dacd9ec6f097bba250555cb25208f594fd08bfba4a4ecf951d06b
4
+ data.tar.gz: aa039e732cfa8aad7a16439bc2608f51c043eef2f9b642513a7616b95b319c01
5
5
  SHA512:
6
- metadata.gz: f1deba0493c9ebbff4e39b81c0a282ffd634cef6db756f3f4be2b3b91f8c43e0d0f6d9362e03f2779ac5fe302101ae19fa8bf90022c55f24afbd9cd7c3b1fb13
7
- data.tar.gz: 0a1a3da4c7c758b0b64b7d9f817a92924c7a5e54fb4918655af2208a5ba8c65f12d41b37f0f568f443a817dd5893d16e351de745be641b3a56d3479e257add45
6
+ metadata.gz: 1c7b7a855b051aa6c856f39144e17484180fa6ff530ec7af3b7870ebf4cbf61b5b33c1a17d2402a319d784a7a17d7b226b64542c65a002707bb87419e1a660dd
7
+ data.tar.gz: b91f701455620e4fe27f2a99b20b58f6b03d78fa04d66548d3f85b1e4202cdf9c09ec2b53a454a3bca419d33b65150c5c24977e9eca93efc102c25e36df21303
@@ -61,7 +61,7 @@ module CodeOwnership
61
61
  File.exist?(file)
62
62
  end
63
63
  else
64
- Private.tracked_files
64
+ nil
65
65
  end
66
66
 
67
67
  CodeOwnership.validate!(
@@ -18,7 +18,7 @@ module CodeOwnership
18
18
  FilesByMapper = T.type_alias do
19
19
  T::Hash[
20
20
  String,
21
- T::Array[MapperDescription]
21
+ T::Set[MapperDescription]
22
22
  ]
23
23
  end
24
24
 
@@ -32,6 +32,20 @@ module CodeOwnership
32
32
  @raw_cache_contents
33
33
  end
34
34
 
35
+ sig { params(files: T::Array[String]).returns(FilesByMapper) }
36
+ def mapper_descriptions_that_map_files(files)
37
+ if files.count > 100
38
+ # When looking at many files, expanding the cache out using Dir.glob and checking for intersections is faster
39
+ files_by_mappers = files.map{ |f| [f, Set.new([]) ]}.to_h
40
+ files_by_mappers.merge(files_by_mappers_via_expanded_cache)
41
+ else
42
+ # When looking at few files, using File.fnmatch is faster
43
+ files_by_mappers_via_file_fnmatch(files)
44
+ end
45
+ end
46
+
47
+ private
48
+
35
49
  sig { returns(CacheShape) }
36
50
  def expanded_cache
37
51
  @expanded_cache = T.let(@expanded_cache, T.nilable(CacheShape))
@@ -52,20 +66,45 @@ module CodeOwnership
52
66
  end
53
67
 
54
68
  sig { returns(FilesByMapper) }
55
- def files_by_mapper
56
- @files_by_mapper ||= T.let(@files_by_mapper, T.nilable(FilesByMapper))
57
- @files_by_mapper ||= begin
58
- files_by_mapper = {}
69
+ def files_by_mappers_via_expanded_cache
70
+ @files_by_mappers ||= T.let(@files_by_mappers, T.nilable(FilesByMapper))
71
+ @files_by_mappers ||= begin
72
+ files_by_mappers = T.let({}, FilesByMapper)
59
73
  expanded_cache.each do |mapper_description, file_by_owner|
60
74
  file_by_owner.each do |file, owner|
61
- files_by_mapper[file] ||= []
62
- files_by_mapper[file] << mapper_description
75
+ files_by_mappers[file] ||= Set.new([])
76
+ files_by_mappers.fetch(file) << mapper_description
63
77
  end
64
78
  end
65
79
 
66
- files_by_mapper
80
+ files_by_mappers
67
81
  end
68
82
  end
83
+
84
+ sig { params(files: T::Array[String]).returns(FilesByMapper) }
85
+ def files_by_mappers_via_file_fnmatch(files)
86
+ files_by_mappers = T.let({}, FilesByMapper)
87
+
88
+ files.each do |file|
89
+ files_by_mappers[file] ||= Set.new([])
90
+ @raw_cache_contents.each do |mapper_description, globs_by_owner|
91
+ # As much as I'd like to *not* special case the file annotations mapper, using File.fnmatch? on the thousands of files mapped by the
92
+ # file annotations mapper is a lot of unnecessary extra work.
93
+ # Therefore we can just check if the file is in the globs directly for file annotations, otherwise use File.fnmatch
94
+ if mapper_description == OwnershipMappers::FileAnnotations::DESCRIPTION
95
+ files_by_mappers.fetch(file) << mapper_description if globs_by_owner[file]
96
+ else
97
+ globs_by_owner.each do |glob, owner|
98
+ if File.fnmatch?(glob, file, File::FNM_PATHNAME | File::FNM_EXTGLOB)
99
+ files_by_mappers.fetch(file) << mapper_description
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
105
+
106
+ files_by_mappers
107
+ end
69
108
  end
70
109
  end
71
110
  end
@@ -18,9 +18,8 @@ module CodeOwnership
18
18
  extend T::Sig
19
19
  include Mapper
20
20
 
21
- @@map_files_to_owners = T.let({}, T.nilable(T::Hash[String, ::CodeTeams::Team])) # rubocop:disable Style/ClassVars
22
-
23
21
  TEAM_PATTERN = T.let(/\A(?:#|\/\/) @team (?<team>.*)\Z/.freeze, Regexp)
22
+ DESCRIPTION = 'Annotations at the top of file'
24
23
 
25
24
  sig do
26
25
  override.params(file: String).
@@ -36,9 +35,7 @@ module CodeOwnership
36
35
  returns(T::Hash[String, ::CodeTeams::Team])
37
36
  end
38
37
  def globs_to_owner(files)
39
- return @@map_files_to_owners if @@map_files_to_owners&.keys && @@map_files_to_owners.keys.count > 0
40
-
41
- @@map_files_to_owners = files.each_with_object({}) do |filename_relative_to_root, mapping| # rubocop:disable Style/ClassVars
38
+ files.each_with_object({}) do |filename_relative_to_root, mapping| # rubocop:disable Style/ClassVars
42
39
  owner = file_annotation_based_owner(filename_relative_to_root)
43
40
  next unless owner
44
41
 
@@ -51,11 +48,8 @@ module CodeOwnership
51
48
  end
52
49
  def update_cache(cache, files)
53
50
  cache.merge!(globs_to_owner(files))
54
-
55
- # TODO: Make `tracked_files` return a set
56
- fileset = Set.new(Private.tracked_files)
57
51
  invalid_files = cache.keys.select do |file|
58
- !fileset.include?(file)
52
+ !Private.file_tracked?(file)
59
53
  end
60
54
  invalid_files.each do |invalid_file|
61
55
  cache.delete(invalid_file)
@@ -116,12 +110,11 @@ module CodeOwnership
116
110
 
117
111
  sig { override.returns(String) }
118
112
  def description
119
- 'Annotations at the top of file'
113
+ DESCRIPTION
120
114
  end
121
115
 
122
116
  sig { override.void }
123
117
  def bust_caches!
124
- @@map_files_to_owners = {} # rubocop:disable Style/ClassVars
125
118
  end
126
119
  end
127
120
  end
@@ -9,8 +9,6 @@ module CodeOwnership
9
9
  extend T::Sig
10
10
  include Mapper
11
11
 
12
- @@package_yml_cache = T.let({}, T::Hash[String, T.nilable(Packs::Pack)]) # rubocop:disable Style/ClassVars
13
-
14
12
  sig do
15
13
  override.params(file: String).
16
14
  returns(T.nilable(::CodeTeams::Team))
@@ -69,7 +67,6 @@ module CodeOwnership
69
67
 
70
68
  sig { override.void }
71
69
  def bust_caches!
72
- @@package_yml_cache = {} # rubocop:disable Style/ClassVars
73
70
  end
74
71
  end
75
72
  end
@@ -12,8 +12,6 @@ module CodeOwnership
12
12
 
13
13
  @@map_files_to_owners = T.let(@map_files_to_owners, T.nilable(T::Hash[String, ::CodeTeams::Team])) # rubocop:disable Style/ClassVars
14
14
  @@map_files_to_owners = {} # rubocop:disable Style/ClassVars
15
- @@codeowners_lines_to_owners = T.let(@codeowners_lines_to_owners, T.nilable(T::Hash[String, ::CodeTeams::Team])) # rubocop:disable Style/ClassVars
16
- @@codeowners_lines_to_owners = {} # rubocop:disable Style/ClassVars
17
15
 
18
16
  sig do
19
17
  params(files: T::Array[String]).
@@ -103,9 +101,7 @@ module CodeOwnership
103
101
  returns(T::Hash[String, ::CodeTeams::Team])
104
102
  end
105
103
  def globs_to_owner(files)
106
- return @@codeowners_lines_to_owners if @@codeowners_lines_to_owners&.keys && @@codeowners_lines_to_owners.keys.count > 0
107
-
108
- @@codeowners_lines_to_owners = CodeTeams.all.each_with_object({}) do |team, map| # rubocop:disable Style/ClassVars
104
+ CodeTeams.all.each_with_object({}) do |team, map| # rubocop:disable Style/ClassVars
109
105
  TeamPlugins::Ownership.for(team).owned_globs.each do |owned_glob|
110
106
  map[owned_glob] = team
111
107
  end
@@ -114,7 +110,6 @@ module CodeOwnership
114
110
 
115
111
  sig { override.void }
116
112
  def bust_caches!
117
- @@codeowners_lines_to_owners = {} # rubocop:disable Style/ClassVars
118
113
  @@map_files_to_owners = {} # rubocop:disable Style/ClassVars
119
114
  end
120
115
 
@@ -11,8 +11,6 @@ module CodeOwnership
11
11
 
12
12
  @@map_files_to_owners = T.let(@map_files_to_owners, T.nilable(T::Hash[String, ::CodeTeams::Team])) # rubocop:disable Style/ClassVars
13
13
  @@map_files_to_owners = {} # rubocop:disable Style/ClassVars
14
- @@codeowners_lines_to_owners = T.let(@codeowners_lines_to_owners, T.nilable(T::Hash[String, ::CodeTeams::Team])) # rubocop:disable Style/ClassVars
15
- @@codeowners_lines_to_owners = {} # rubocop:disable Style/ClassVars
16
14
 
17
15
  sig do
18
16
  params(files: T::Array[String]).
@@ -39,16 +37,13 @@ module CodeOwnership
39
37
  returns(T::Hash[String, ::CodeTeams::Team])
40
38
  end
41
39
  def globs_to_owner(files)
42
- return @@codeowners_lines_to_owners if @@codeowners_lines_to_owners&.keys && @@codeowners_lines_to_owners.keys.count > 0
43
-
44
- @@codeowners_lines_to_owners = CodeTeams.all.each_with_object({}) do |team, map| # rubocop:disable Style/ClassVars
40
+ CodeTeams.all.each_with_object({}) do |team, map| # rubocop:disable Style/ClassVars
45
41
  map[team.config_yml] = team
46
42
  end
47
43
  end
48
44
 
49
45
  sig { override.void }
50
46
  def bust_caches!
51
- @@codeowners_lines_to_owners = {} # rubocop:disable Style/ClassVars
52
47
  @@map_files_to_owners = {} # rubocop:disable Style/ClassVars
53
48
  end
54
49
 
@@ -10,10 +10,10 @@ module CodeOwnership
10
10
 
11
11
  sig { override.params(files: T::Array[String], autocorrect: T::Boolean, stage_changes: T::Boolean).returns(T::Array[String]) }
12
12
  def validation_errors(files:, autocorrect: true, stage_changes: true)
13
- files_by_mapper = Private.glob_cache.files_by_mapper
14
-
15
- files_not_mapped_at_all = files.select do |file|
16
- files_by_mapper.fetch(file, []).count == 0
13
+ cache = Private.glob_cache
14
+ file_mappings = cache.mapper_descriptions_that_map_files(files)
15
+ files_not_mapped_at_all = file_mappings.select do |file, mapper_descriptions|
16
+ mapper_descriptions.count == 0
17
17
  end
18
18
 
19
19
  errors = T.let([], T::Array[String])
@@ -22,7 +22,7 @@ module CodeOwnership
22
22
  errors << <<~MSG
23
23
  Some files are missing ownership:
24
24
 
25
- #{files_not_mapped_at_all.map { |file| "- #{file}" }.join("\n")}
25
+ #{files_not_mapped_at_all.map { |file, mappers| "- #{file}" }.join("\n")}
26
26
  MSG
27
27
  end
28
28
 
@@ -10,14 +10,10 @@ module CodeOwnership
10
10
 
11
11
  sig { override.params(files: T::Array[String], autocorrect: T::Boolean, stage_changes: T::Boolean).returns(T::Array[String]) }
12
12
  def validation_errors(files:, autocorrect: true, stage_changes: true)
13
- files_by_mapper = Private.glob_cache.files_by_mapper
14
-
15
- files_mapped_by_multiple_mappers = {}
16
- files.each do |file|
17
- mappers = files_by_mapper.fetch(file, [])
18
- if mappers.count > 1
19
- files_mapped_by_multiple_mappers[file] = mappers
20
- end
13
+ cache = Private.glob_cache
14
+ file_mappings = cache.mapper_descriptions_that_map_files(files)
15
+ files_mapped_by_multiple_mappers = file_mappings.select do |file, mapper_descriptions|
16
+ mapper_descriptions.count > 1
21
17
  end
22
18
 
23
19
  errors = T.let([], T::Array[String])
@@ -26,7 +22,7 @@ module CodeOwnership
26
22
  errors << <<~MSG
27
23
  Code ownership should only be defined for each file in one way. The following files have declared ownership in multiple ways.
28
24
 
29
- #{files_mapped_by_multiple_mappers.map { |file, descriptions| "- #{file} (#{descriptions.join(', ')})" }.join("\n")}
25
+ #{files_mapped_by_multiple_mappers.map { |file, descriptions| "- #{file} (#{descriptions.to_a.join(', ')})" }.join("\n")}
30
26
  MSG
31
27
  end
32
28
 
@@ -81,6 +81,24 @@ module CodeOwnership
81
81
  @tracked_files ||= Dir.glob(configuration.owned_globs) - Dir.glob(configuration.unowned_globs)
82
82
  end
83
83
 
84
+ sig { params(file: String).returns(T::Boolean) }
85
+ def self.file_tracked?(file)
86
+ # Another way to accomplish this is
87
+ # (Dir.glob(configuration.owned_globs) - Dir.glob(configuration.unowned_globs)).include?(file)
88
+ # However, globbing out can take 5 or more seconds on a large repository, dramatically slowing down
89
+ # invocations to `bin/codeownership validate --diff`.
90
+ # Using `File.fnmatch?` is a lot faster!
91
+ in_owned_globs = configuration.owned_globs.all? do |owned_glob|
92
+ File.fnmatch?(owned_glob, file, File::FNM_PATHNAME | File::FNM_EXTGLOB)
93
+ end
94
+
95
+ in_unowned_globs = configuration.unowned_globs.all? do |unowned_glob|
96
+ File.fnmatch?(unowned_glob, file, File::FNM_PATHNAME | File::FNM_EXTGLOB)
97
+ end
98
+
99
+ in_owned_globs && !in_unowned_globs
100
+ end
101
+
84
102
  sig { params(team_name: String, location_of_reference: String).returns(CodeTeams::Team) }
85
103
  def self.find_team!(team_name, location_of_reference)
86
104
  found_team = CodeTeams.find(team_name)
@@ -82,18 +82,24 @@ module CodeOwnership
82
82
 
83
83
  sig do
84
84
  params(
85
- files: T::Array[String],
86
85
  autocorrect: T::Boolean,
87
- stage_changes: T::Boolean
86
+ stage_changes: T::Boolean,
87
+ files: T.nilable(T::Array[String]),
88
88
  ).void
89
89
  end
90
90
  def validate!(
91
- files: Private.tracked_files,
92
91
  autocorrect: true,
93
- stage_changes: true
92
+ stage_changes: true,
93
+ files: nil
94
94
  )
95
95
  Private.load_configuration!
96
- tracked_file_subset = Private.tracked_files & files
96
+
97
+ tracked_file_subset = if files
98
+ files.select{|f| Private.file_tracked?(f)}
99
+ else
100
+ Private.tracked_files
101
+ end
102
+
97
103
  Private.validate!(files: tracked_file_subset, autocorrect: autocorrect, stage_changes: stage_changes)
98
104
  end
99
105
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: code_ownership
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.32.8
4
+ version: 1.32.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gusto Engineers
@@ -53,7 +53,7 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: rake
56
+ name: pry
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - ">="
@@ -67,7 +67,7 @@ dependencies:
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
- name: pry
70
+ name: rake
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - ">="