Skip to content

Commit 9e04ed0

Browse files
author
Alex Evanczuk
authored
Use GlobCache in various validations (#47)
1 parent f2731e5 commit 9e04ed0

File tree

7 files changed

+115
-35
lines changed

7 files changed

+115
-35
lines changed

lib/code_ownership/mapper.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,5 +58,21 @@ def description
5858
sig { abstract.void }
5959
def bust_caches!
6060
end
61+
62+
sig { returns(Private::GlobCache) }
63+
def self.to_glob_cache
64+
glob_to_owner_map_by_mapper_description = {}
65+
66+
Mapper.all.each do |mapper|
67+
mapped_files = mapper.codeowners_lines_to_owners
68+
mapped_files.each do |glob, owner|
69+
next if owner.nil?
70+
glob_to_owner_map_by_mapper_description[mapper.description] ||= {}
71+
glob_to_owner_map_by_mapper_description.fetch(mapper.description)[glob] = owner
72+
end
73+
end
74+
75+
Private::GlobCache.new(glob_to_owner_map_by_mapper_description)
76+
end
6177
end
6278
end

lib/code_ownership/private.rb

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
require 'code_ownership/private/team_plugins/github'
88
require 'code_ownership/private/codeowners_file'
99
require 'code_ownership/private/parse_js_packages'
10+
require 'code_ownership/private/glob_cache'
1011
require 'code_ownership/private/validations/files_have_owners'
1112
require 'code_ownership/private/validations/github_codeowners_up_to_date'
1213
require 'code_ownership/private/validations/files_have_unique_owners'
@@ -38,7 +39,7 @@ def self.load_configuration!
3839
def self.bust_caches!
3940
@configuration = nil
4041
@tracked_files = nil
41-
@files_by_mapper = nil
42+
@glob_cache = nil
4243
end
4344

4445
sig { params(files: T::Array[String], autocorrect: T::Boolean, stage_changes: T::Boolean).void }
@@ -88,21 +89,10 @@ def self.find_team!(team_name, location_of_reference)
8889
end
8990
end
9091

91-
sig { params(files: T::Array[String]).returns(T::Hash[String, T::Array[String]]) }
92-
def self.files_by_mapper(files)
93-
@files_by_mapper ||= T.let(@files_by_mapper, T.nilable(T::Hash[String, T::Array[String]]))
94-
@files_by_mapper ||= begin
95-
files_by_mapper = files.map { |file| [file, []] }.to_h
96-
97-
Mapper.all.each do |mapper|
98-
mapper.map_files_to_owners(files).each do |file, _team|
99-
files_by_mapper[file] ||= []
100-
T.must(files_by_mapper[file]) << mapper.description
101-
end
102-
end
103-
104-
files_by_mapper
105-
end
92+
sig { returns(GlobCache) }
93+
def self.glob_cache
94+
@glob_cache ||= T.let(@glob_cache, T.nilable(GlobCache))
95+
@glob_cache ||= Mapper.to_glob_cache
10696
end
10797
end
10898

lib/code_ownership/private/codeowners_file.rb

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,7 @@ def self.actual_contents_lines
1818

1919
sig { returns(T::Array[T.nilable(String)]) }
2020
def self.expected_contents_lines
21-
# Eventually, we'll get this from the `GlobCache`
22-
glob_to_owner_map_by_mapper_description = {}
23-
24-
Mapper.all.each do |mapper|
25-
mapped_files = mapper.codeowners_lines_to_owners
26-
mapped_files.each do |glob, owner|
27-
next if owner.nil?
28-
glob_to_owner_map_by_mapper_description[mapper.description] ||= {}
29-
glob_to_owner_map_by_mapper_description.fetch(mapper.description)[glob] = owner
30-
end
31-
end
21+
cache = Private.glob_cache.raw_cache_contents
3222

3323
header = <<~HEADER
3424
# STOP! - DO NOT EDIT THIS FILE MANUALLY
@@ -52,7 +42,7 @@ def self.expected_contents_lines
5242

5343
codeowners_file_lines = T.let([], T::Array[String])
5444

55-
glob_to_owner_map_by_mapper_description.each do |mapper_description, ownership_map_cache|
45+
cache.each do |mapper_description, ownership_map_cache|
5646
ownership_entries = []
5747
ownership_map_cache.each do |path, code_team|
5848
team_mapping = github_team_map[code_team.name]
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# typed: strict
2+
# frozen_string_literal: true
3+
4+
module CodeOwnership
5+
module Private
6+
class GlobCache
7+
extend T::Sig
8+
9+
MapperDescription = T.type_alias { String }
10+
GlobsByMapper = T.type_alias { T::Hash[String, CodeTeams::Team] }
11+
12+
CacheShape = T.type_alias do
13+
T::Hash[
14+
MapperDescription,
15+
GlobsByMapper
16+
]
17+
end
18+
19+
FilesByMapper = T.type_alias do
20+
T::Hash[
21+
String,
22+
T::Array[MapperDescription]
23+
]
24+
end
25+
26+
sig { params(raw_cache_contents: CacheShape).void }
27+
def initialize(raw_cache_contents)
28+
@raw_cache_contents = raw_cache_contents
29+
end
30+
31+
sig { returns(CacheShape) }
32+
def raw_cache_contents
33+
@raw_cache_contents
34+
end
35+
36+
sig { returns(CacheShape) }
37+
def expanded_cache
38+
@expanded_cache = T.let(@expanded_cache, T.nilable(CacheShape))
39+
40+
@expanded_cache ||= begin
41+
expanded_cache = {}
42+
@raw_cache_contents.each do |mapper_description, globs_by_owner|
43+
expanded_cache[mapper_description] = {}
44+
globs_by_owner.each do |glob, owner|
45+
Dir.glob(glob).each do |file, owner|
46+
expanded_cache[mapper_description][file] = owner
47+
end
48+
end
49+
end
50+
51+
expanded_cache
52+
end
53+
end
54+
55+
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 = {}
60+
expanded_cache.each do |mapper_description, file_by_owner|
61+
file_by_owner.each do |file, owner|
62+
files_by_mapper[file] ||= []
63+
files_by_mapper[file] << mapper_description
64+
end
65+
end
66+
67+
files_by_mapper
68+
end
69+
end
70+
end
71+
end
72+
end

lib/code_ownership/private/validations/files_have_owners.rb

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,11 @@ class FilesHaveOwners
1111
sig { override.params(files: T::Array[String], autocorrect: T::Boolean, stage_changes: T::Boolean).returns(T::Array[String]) }
1212
def validation_errors(files:, autocorrect: true, stage_changes: true)
1313
allow_list = Dir.glob(Private.configuration.unowned_globs)
14-
files_by_mapper = Private.files_by_mapper(files)
15-
files_not_mapped_at_all = files_by_mapper.select { |_file, mapper_descriptions| mapper_descriptions.count == 0 }.keys
14+
files_by_mapper = Private.glob_cache.files_by_mapper
15+
16+
files_not_mapped_at_all = files.select do |file|
17+
files_by_mapper.fetch(file, []).count == 0
18+
end
1619

1720
files_without_owners = files_not_mapped_at_all - allow_list
1821

lib/code_ownership/private/validations/files_have_unique_owners.rb

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,15 @@ class FilesHaveUniqueOwners
1010

1111
sig { override.params(files: T::Array[String], autocorrect: T::Boolean, stage_changes: T::Boolean).returns(T::Array[String]) }
1212
def validation_errors(files:, autocorrect: true, stage_changes: true)
13-
files_by_mapper = Private.files_by_mapper(files)
13+
files_by_mapper = Private.glob_cache.files_by_mapper
1414

15-
files_mapped_by_multiple_mappers = files_by_mapper.select { |_file, mapper_descriptions| mapper_descriptions.count > 1 }.to_h
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
21+
end
1622

1723
errors = T.let([], T::Array[String])
1824

spec/lib/code_ownership/private/validations/files_have_unique_owners_spec.rb

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@ module CodeOwnership
33
describe 'CodeOwnership.validate!' do
44
context 'a file in owned_globs has ownership defined in multiple ways' do
55
before do
6-
create_configuration
6+
write_file('config/code_ownership.yml', <<~YML)
7+
owned_globs:
8+
- '{app,components,frontend,lib,packs,spec}/**/*.{rb,rake,js,jsx,ts,tsx,json,yml}'
9+
YML
10+
711
write_file('app/services/some_other_file.rb', <<~YML)
812
# @team Bar
913
YML
@@ -47,17 +51,16 @@ module CodeOwnership
4751

4852
it 'lets the user know that each file can only have ownership defined in one way' do
4953
expect(CodeOwnership.for_file('app/missing_ownership.rb')).to eq nil
50-
5154
expect { CodeOwnership.validate! }.to raise_error do |e|
5255
expect(e).to be_a CodeOwnership::InvalidCodeOwnershipConfigurationError
5356
puts e.message
5457
expect(e.message).to eq <<~EXPECTED.chomp
5558
Code ownership should only be defined for each file in one way. The following files have declared ownership in multiple ways.
5659
5760
- frontend/javascripts/packages/my_package/owned_file.jsx (Annotations at the top of file, Team-specific owned globs, Owner metadata key in package.json)
61+
- frontend/javascripts/packages/my_package/package.json (Team-specific owned globs, Owner metadata key in package.json)
5862
- packs/my_pack/owned_file.rb (Annotations at the top of file, Team-specific owned globs, Owner metadata key in package.yml)
5963
- packs/my_pack/package.yml (Team-specific owned globs, Owner metadata key in package.yml)
60-
- frontend/javascripts/packages/my_package/package.json (Team-specific owned globs, Owner metadata key in package.json)
6164
6265
See https://github.com/rubyatscale/code_ownership#README.md for more details
6366
EXPECTED

0 commit comments

Comments
 (0)