code_ownership 1.32.7 → 1.32.9

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: 76d700471a2e895926f184939c856b08ac75d5240a0d924a8e4474ac733a7204
4
- data.tar.gz: 3b62947009df98089b522614022643762c4c51d82af4f90f957606bcedfe60bc
3
+ metadata.gz: 71768f254f9fbb891679c002c16c20bd70a2125460e3c66995821cfe2784fe56
4
+ data.tar.gz: ce5698b1505d306026f807a142f648942ea06d3ab65576d05c623f6bdbdc1dfa
5
5
  SHA512:
6
- metadata.gz: df833467c3721e6b7d67aebd0732d59c46073589210251bd0859cb05979103741876264dd336df10401005360bca7a2937c65e861044b22abd698a2b8b65a20d
7
- data.tar.gz: 1b8f489aa18295cd8c534f3d5bcb3e2ab569553f26ec7e5a9445faff90da3aee3860bd3d7a02d6e91b80815d65b96680b0f54b085770ba85a54efa13aa0109b1
6
+ metadata.gz: 7b2e5da9f4e02b4db10c47be40b1b4bce33f7c44ad18797ff326a6e245f5dc57b2a8534c74fde74a1966970720a9bedf50d59858923e07f64d8e21d9f8411df1
7
+ data.tar.gz: b08f25b9008d86b19207931cb44c13adea814b998af8700b1fa445ad09e48e6549f86d6e4b75db41e5a783bf080ea5e25371d97db5307de8c5451bcd3d4fe3da
@@ -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!(
@@ -40,15 +40,18 @@ module CodeOwnership
40
40
  #
41
41
  sig do
42
42
  abstract.params(files: T::Array[String]).
43
- returns(T::Hash[String, T.nilable(::CodeTeams::Team)])
43
+ returns(T::Hash[String, ::CodeTeams::Team])
44
44
  end
45
- def map_files_to_owners(files)
45
+ def globs_to_owner(files)
46
46
  end
47
47
 
48
+ #
49
+ # This should be fast when run with MANY files
50
+ #
48
51
  sig do
49
- abstract.returns(T::Hash[String, T.nilable(::CodeTeams::Team)])
52
+ abstract.params(cache: GlobsToOwningTeamMap, files: T::Array[String]).returns(GlobsToOwningTeamMap)
50
53
  end
51
- def codeowners_lines_to_owners
54
+ def update_cache(cache, files)
52
55
  end
53
56
 
54
57
  sig { abstract.returns(String) }
@@ -64,7 +67,7 @@ module CodeOwnership
64
67
  glob_to_owner_map_by_mapper_description = {}
65
68
 
66
69
  Mapper.all.each do |mapper|
67
- mapped_files = mapper.codeowners_lines_to_owners
70
+ mapped_files = mapper.globs_to_owner(Private.tracked_files)
68
71
  mapped_files.each do |glob, owner|
69
72
  next if owner.nil?
70
73
  glob_to_owner_map_by_mapper_description[mapper.description] ||= {}
@@ -90,6 +90,58 @@ module CodeOwnership
90
90
  def self.path
91
91
  Pathname.pwd.join('.github/CODEOWNERS')
92
92
  end
93
+
94
+ sig { params(files: T::Array[String]).void }
95
+ def self.update_cache!(files)
96
+ cache = Private.glob_cache
97
+ # Each mapper returns a new copy of the cache subset related to that mapper,
98
+ # which is then stored back into the cache.
99
+ Mapper.all.each do |mapper|
100
+ existing_cache = cache.raw_cache_contents.fetch(mapper.description, {})
101
+ updated_cache = mapper.update_cache(existing_cache, files)
102
+ cache.raw_cache_contents[mapper.description] = updated_cache
103
+ end
104
+ end
105
+
106
+ sig { returns(T::Boolean) }
107
+ def self.use_codeowners_cache?
108
+ CodeownersFile.path.exist? && !Private.configuration.skip_codeowners_validation
109
+ end
110
+
111
+ sig { returns(GlobCache) }
112
+ def self.to_glob_cache
113
+ github_team_to_code_team_map = T.let({}, T::Hash[String, CodeTeams::Team])
114
+ CodeTeams.all.each do |team|
115
+ github_team = TeamPlugins::Github.for(team).github.team
116
+ github_team_to_code_team_map[github_team] = team
117
+ end
118
+ raw_cache_contents = T.let({}, GlobCache::CacheShape)
119
+ current_mapper = T.let(nil, T.nilable(String))
120
+ mapper_descriptions = Set.new(Mapper.all.map(&:description))
121
+
122
+ path.readlines.each do |line|
123
+ line_with_no_comment = line.chomp.gsub("# ", "")
124
+ if mapper_descriptions.include?(line_with_no_comment)
125
+ current_mapper = line_with_no_comment
126
+ else
127
+ next if current_mapper.nil?
128
+ next if line.chomp == ""
129
+ # The codeowners file stores paths relative to the root of directory
130
+ # Since a `/` means root of the file system from the perspective of ruby,
131
+ # we remove that beginning slash so we can correctly glob the files out.
132
+ normalized_line = line.gsub(/^# /, '').gsub(/^\//, '')
133
+ split_line = normalized_line.split
134
+ # Most lines will be in the format: /path/to/file my-github-team
135
+ # This will skip over lines that are not of the correct form
136
+ next if split_line.count > 2
137
+ entry, github_team = split_line
138
+ raw_cache_contents[current_mapper] ||= {}
139
+ raw_cache_contents.fetch(current_mapper)[T.must(entry)] = github_team_to_code_team_map.fetch(T.must(github_team))
140
+ end
141
+ end
142
+
143
+ GlobCache.new(raw_cache_contents)
144
+ end
93
145
  end
94
146
  end
95
147
  end
@@ -7,19 +7,18 @@ module CodeOwnership
7
7
  extend T::Sig
8
8
 
9
9
  MapperDescription = T.type_alias { String }
10
- GlobsByMapper = T.type_alias { T::Hash[String, CodeTeams::Team] }
11
10
 
12
11
  CacheShape = T.type_alias do
13
12
  T::Hash[
14
13
  MapperDescription,
15
- GlobsByMapper
14
+ GlobsToOwningTeamMap
16
15
  ]
17
16
  end
18
17
 
19
18
  FilesByMapper = T.type_alias do
20
19
  T::Hash[
21
20
  String,
22
- T::Array[MapperDescription]
21
+ T::Set[MapperDescription]
23
22
  ]
24
23
  end
25
24
 
@@ -33,6 +32,20 @@ module CodeOwnership
33
32
  @raw_cache_contents
34
33
  end
35
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
+
36
49
  sig { returns(CacheShape) }
37
50
  def expanded_cache
38
51
  @expanded_cache = T.let(@expanded_cache, T.nilable(CacheShape))
@@ -53,20 +66,45 @@ module CodeOwnership
53
66
  end
54
67
 
55
68
  sig { returns(FilesByMapper) }
56
- def files_by_mapper
57
- @files_by_mapper ||= T.let(@files_by_mapper, T.nilable(FilesByMapper))
58
- @files_by_mapper ||= begin
59
- 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)
60
73
  expanded_cache.each do |mapper_description, file_by_owner|
61
74
  file_by_owner.each do |file, owner|
62
- files_by_mapper[file] ||= []
63
- files_by_mapper[file] << mapper_description
75
+ files_by_mappers[file] ||= Set.new([])
76
+ files_by_mappers.fetch(file) << mapper_description
64
77
  end
65
78
  end
66
79
 
67
- files_by_mapper
80
+ files_by_mappers
68
81
  end
69
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
70
108
  end
71
109
  end
72
110
  end
@@ -18,9 +18,10 @@ 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, T.nilable(::CodeTeams::Team)])) # rubocop:disable Style/ClassVars
21
+ @@map_files_to_owners = T.let({}, T.nilable(T::Hash[String, ::CodeTeams::Team])) # rubocop:disable Style/ClassVars
22
22
 
23
23
  TEAM_PATTERN = T.let(/\A(?:#|\/\/) @team (?<team>.*)\Z/.freeze, Regexp)
24
+ DESCRIPTION = 'Annotations at the top of file'
24
25
 
25
26
  sig do
26
27
  override.params(file: String).
@@ -33,9 +34,9 @@ module CodeOwnership
33
34
  sig do
34
35
  override.
35
36
  params(files: T::Array[String]).
36
- returns(T::Hash[String, T.nilable(::CodeTeams::Team)])
37
+ returns(T::Hash[String, ::CodeTeams::Team])
37
38
  end
38
- def map_files_to_owners(files)
39
+ def globs_to_owner(files)
39
40
  return @@map_files_to_owners if @@map_files_to_owners&.keys && @@map_files_to_owners.keys.count > 0
40
41
 
41
42
  @@map_files_to_owners = files.each_with_object({}) do |filename_relative_to_root, mapping| # rubocop:disable Style/ClassVars
@@ -46,6 +47,21 @@ module CodeOwnership
46
47
  end
47
48
  end
48
49
 
50
+ sig do
51
+ override.params(cache: GlobsToOwningTeamMap, files: T::Array[String]).returns(GlobsToOwningTeamMap)
52
+ end
53
+ def update_cache(cache, files)
54
+ cache.merge!(globs_to_owner(files))
55
+ invalid_files = cache.keys.select do |file|
56
+ !Private.file_tracked?(file)
57
+ end
58
+ invalid_files.each do |invalid_file|
59
+ cache.delete(invalid_file)
60
+ end
61
+
62
+ cache
63
+ end
64
+
49
65
  sig { params(filename: String).returns(T.nilable(CodeTeams::Team)) }
50
66
  def file_annotation_based_owner(filename)
51
67
  # If for a directory is named with an ownable extension, we need to skip
@@ -96,17 +112,9 @@ module CodeOwnership
96
112
  end
97
113
  end
98
114
 
99
- sig do
100
- override.returns(T::Hash[String, T.nilable(::CodeTeams::Team)])
101
- end
102
- def codeowners_lines_to_owners
103
- @@map_files_to_owners = nil # rubocop:disable Style/ClassVars
104
- map_files_to_owners(Private.tracked_files)
105
- end
106
-
107
115
  sig { override.returns(String) }
108
116
  def description
109
- 'Annotations at the top of file'
117
+ DESCRIPTION
110
118
  end
111
119
 
112
120
  sig { override.void }
@@ -24,20 +24,10 @@ module CodeOwnership
24
24
  end
25
25
 
26
26
  sig do
27
- override.
28
- params(files: T::Array[String]).
29
- returns(T::Hash[String, T.nilable(::CodeTeams::Team)])
27
+ override.params(cache: GlobsToOwningTeamMap, files: T::Array[String]).returns(GlobsToOwningTeamMap)
30
28
  end
31
- def map_files_to_owners(files) # rubocop:disable Lint/UnusedMethodArgument
32
- ParseJsPackages.all.each_with_object({}) do |package, res|
33
- owner = owner_for_package(package)
34
- next if owner.nil?
35
-
36
- glob = package.directory.join('**/**').to_s
37
- Dir.glob(glob).each do |path|
38
- res[path] = owner
39
- end
40
- end
29
+ def update_cache(cache, files)
30
+ globs_to_owner(files)
41
31
  end
42
32
 
43
33
  #
@@ -49,9 +39,10 @@ module CodeOwnership
49
39
  # subset of files, but rather we want code ownership for all files.
50
40
  #
51
41
  sig do
52
- override.returns(T::Hash[String, T.nilable(::CodeTeams::Team)])
42
+ override.params(files: T::Array[String]).
43
+ returns(T::Hash[String, ::CodeTeams::Team])
53
44
  end
54
- def codeowners_lines_to_owners
45
+ def globs_to_owner(files)
55
46
  ParseJsPackages.all.each_with_object({}) do |package, res|
56
47
  owner = owner_for_package(package)
57
48
  next if owner.nil?
@@ -23,23 +23,6 @@ module CodeOwnership
23
23
  owner_for_package(package)
24
24
  end
25
25
 
26
- sig do
27
- override.
28
- params(files: T::Array[String]).
29
- returns(T::Hash[String, T.nilable(::CodeTeams::Team)])
30
- end
31
- def map_files_to_owners(files) # rubocop:disable Lint/UnusedMethodArgument
32
- Packs.all.each_with_object({}) do |package, res|
33
- owner = owner_for_package(package)
34
- next if owner.nil?
35
-
36
- glob = package.relative_path.join('**/**').to_s
37
- Dir.glob(glob).each do |path|
38
- res[path] = owner
39
- end
40
- end
41
- end
42
-
43
26
  #
44
27
  # Package ownership ignores the passed in files when generating code owners lines.
45
28
  # This is because Package ownership knows that the fastest way to find code owners for package based ownership
@@ -49,9 +32,10 @@ module CodeOwnership
49
32
  # subset of files, but rather we want code ownership for all files.
50
33
  #
51
34
  sig do
52
- override.returns(T::Hash[String, T.nilable(::CodeTeams::Team)])
35
+ override.params(files: T::Array[String]).
36
+ returns(T::Hash[String, ::CodeTeams::Team])
53
37
  end
54
- def codeowners_lines_to_owners
38
+ def globs_to_owner(files)
55
39
  Packs.all.each_with_object({}) do |package, res|
56
40
  owner = owner_for_package(package)
57
41
  next if owner.nil?
@@ -65,6 +49,13 @@ module CodeOwnership
65
49
  'Owner metadata key in package.yml'
66
50
  end
67
51
 
52
+ sig do
53
+ override.params(cache: GlobsToOwningTeamMap, files: T::Array[String]).returns(GlobsToOwningTeamMap)
54
+ end
55
+ def update_cache(cache, files)
56
+ globs_to_owner(files)
57
+ end
58
+
68
59
  sig { params(package: Packs::Pack).returns(T.nilable(CodeTeams::Team)) }
69
60
  def owner_for_package(package)
70
61
  raw_owner_value = package.metadata['owner']
@@ -10,15 +10,14 @@ module CodeOwnership
10
10
  include Mapper
11
11
  include Validator
12
12
 
13
- @@map_files_to_owners = T.let(@map_files_to_owners, T.nilable(T::Hash[String, T.nilable(::CodeTeams::Team)])) # rubocop:disable Style/ClassVars
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, T.nilable(::CodeTeams::Team)])) # 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
16
  @@codeowners_lines_to_owners = {} # rubocop:disable Style/ClassVars
17
17
 
18
18
  sig do
19
- override.
20
- params(files: T::Array[String]).
21
- returns(T::Hash[String, T.nilable(::CodeTeams::Team)])
19
+ params(files: T::Array[String]).
20
+ returns(T::Hash[String, ::CodeTeams::Team])
22
21
  end
23
22
  def map_files_to_owners(files) # rubocop:disable Lint/UnusedMethodArgument
24
23
  return @@map_files_to_owners if @@map_files_to_owners&.keys && @@map_files_to_owners.keys.count > 0
@@ -93,9 +92,17 @@ module CodeOwnership
93
92
  end
94
93
 
95
94
  sig do
96
- override.returns(T::Hash[String, T.nilable(::CodeTeams::Team)])
95
+ override.params(cache: GlobsToOwningTeamMap, files: T::Array[String]).returns(GlobsToOwningTeamMap)
97
96
  end
98
- def codeowners_lines_to_owners
97
+ def update_cache(cache, files)
98
+ globs_to_owner(files)
99
+ end
100
+
101
+ sig do
102
+ override.params(files: T::Array[String]).
103
+ returns(T::Hash[String, ::CodeTeams::Team])
104
+ end
105
+ def globs_to_owner(files)
99
106
  return @@codeowners_lines_to_owners if @@codeowners_lines_to_owners&.keys && @@codeowners_lines_to_owners.keys.count > 0
100
107
 
101
108
  @@codeowners_lines_to_owners = CodeTeams.all.each_with_object({}) do |team, map| # rubocop:disable Style/ClassVars
@@ -9,15 +9,14 @@ module CodeOwnership
9
9
  extend T::Sig
10
10
  include Mapper
11
11
 
12
- @@map_files_to_owners = T.let(@map_files_to_owners, T.nilable(T::Hash[String, T.nilable(::CodeTeams::Team)])) # rubocop:disable Style/ClassVars
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, T.nilable(::CodeTeams::Team)])) # 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
15
  @@codeowners_lines_to_owners = {} # rubocop:disable Style/ClassVars
16
16
 
17
17
  sig do
18
- override.
19
- params(files: T::Array[String]).
20
- returns(T::Hash[String, T.nilable(::CodeTeams::Team)])
18
+ params(files: T::Array[String]).
19
+ returns(T::Hash[String, ::CodeTeams::Team])
21
20
  end
22
21
  def map_files_to_owners(files) # rubocop:disable Lint/UnusedMethodArgument
23
22
  return @@map_files_to_owners if @@map_files_to_owners&.keys && @@map_files_to_owners.keys.count > 0
@@ -36,9 +35,10 @@ module CodeOwnership
36
35
  end
37
36
 
38
37
  sig do
39
- override.returns(T::Hash[String, T.nilable(::CodeTeams::Team)])
38
+ override.params(files: T::Array[String]).
39
+ returns(T::Hash[String, ::CodeTeams::Team])
40
40
  end
41
- def codeowners_lines_to_owners
41
+ def globs_to_owner(files)
42
42
  return @@codeowners_lines_to_owners if @@codeowners_lines_to_owners&.keys && @@codeowners_lines_to_owners.keys.count > 0
43
43
 
44
44
  @@codeowners_lines_to_owners = CodeTeams.all.each_with_object({}) do |team, map| # rubocop:disable Style/ClassVars
@@ -52,6 +52,13 @@ module CodeOwnership
52
52
  @@map_files_to_owners = {} # rubocop:disable Style/ClassVars
53
53
  end
54
54
 
55
+ sig do
56
+ override.params(cache: GlobsToOwningTeamMap, files: T::Array[String]).returns(GlobsToOwningTeamMap)
57
+ end
58
+ def update_cache(cache, files)
59
+ globs_to_owner(files)
60
+ end
61
+
55
62
  sig { override.returns(String) }
56
63
  def description
57
64
  'Team YML ownership'
@@ -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
 
@@ -44,6 +44,8 @@ module CodeOwnership
44
44
 
45
45
  sig { params(files: T::Array[String], autocorrect: T::Boolean, stage_changes: T::Boolean).void }
46
46
  def self.validate!(files:, autocorrect: true, stage_changes: true)
47
+ CodeownersFile.update_cache!(files) if CodeownersFile.use_codeowners_cache?
48
+
47
49
  errors = Validator.all.flat_map do |validator|
48
50
  validator.validation_errors(
49
51
  files: files,
@@ -79,6 +81,24 @@ module CodeOwnership
79
81
  @tracked_files ||= Dir.glob(configuration.owned_globs) - Dir.glob(configuration.unowned_globs)
80
82
  end
81
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
+
82
102
  sig { params(team_name: String, location_of_reference: String).returns(CodeTeams::Team) }
83
103
  def self.find_team!(team_name, location_of_reference)
84
104
  found_team = CodeTeams.find(team_name)
@@ -92,7 +112,13 @@ module CodeOwnership
92
112
  sig { returns(GlobCache) }
93
113
  def self.glob_cache
94
114
  @glob_cache ||= T.let(@glob_cache, T.nilable(GlobCache))
95
- @glob_cache ||= Mapper.to_glob_cache
115
+ @glob_cache ||= begin
116
+ if CodeownersFile.use_codeowners_cache?
117
+ CodeownersFile.to_glob_cache
118
+ else
119
+ Mapper.to_glob_cache
120
+ end
121
+ end
96
122
  end
97
123
  end
98
124
 
@@ -19,6 +19,7 @@ module CodeOwnership
19
19
  extend T::Helpers
20
20
 
21
21
  requires_ancestor { Kernel }
22
+ GlobsToOwningTeamMap = T.type_alias { T::Hash[String, CodeTeams::Team] }
22
23
 
23
24
  sig { params(file: String).returns(T.nilable(CodeTeams::Team)) }
24
25
  def for_file(file)
@@ -50,7 +51,7 @@ module CodeOwnership
50
51
  ownership_information << "# Code Ownership Report for `#{team.name}` Team"
51
52
  Mapper.all.each do |mapper|
52
53
  ownership_information << "## #{mapper.description}"
53
- codeowners_lines = mapper.codeowners_lines_to_owners
54
+ codeowners_lines = mapper.globs_to_owner(Private.tracked_files)
54
55
  ownership_for_mapper = []
55
56
  codeowners_lines.each do |line, team_for_line|
56
57
  next if team_for_line.nil?
@@ -81,18 +82,24 @@ module CodeOwnership
81
82
 
82
83
  sig do
83
84
  params(
84
- files: T::Array[String],
85
85
  autocorrect: T::Boolean,
86
- stage_changes: T::Boolean
86
+ stage_changes: T::Boolean,
87
+ files: T.nilable(T::Array[String]),
87
88
  ).void
88
89
  end
89
90
  def validate!(
90
- files: Private.tracked_files,
91
91
  autocorrect: true,
92
- stage_changes: true
92
+ stage_changes: true,
93
+ files: nil
93
94
  )
94
95
  Private.load_configuration!
95
- 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
+
96
103
  Private.validate!(files: tracked_file_subset, autocorrect: autocorrect, stage_changes: stage_changes)
97
104
  end
98
105
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: code_ownership
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.32.7
4
+ version: 1.32.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gusto Engineers
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-04-11 00:00:00.000000000 Z
11
+ date: 2023-04-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: code_teams