88require 'json'
99require 'packs-specification'
1010require 'code_ownership/version'
11- require 'code_ownership/mapper '
12- require 'code_ownership/validator '
13- require 'code_ownership/private '
11+ require 'code_ownership/file_path_finder '
12+ require 'code_ownership/file_path_team_cache '
13+ require 'code_ownership/team_finder '
1414require 'code_ownership/cli'
15- require 'code_ownership/configuration'
1615
1716begin
1817 RUBY_VERSION =~ /(\d +\. \d +)/
2221end
2322
2423if defined? ( Packwerk )
25- require 'code_ownership/private/ permit_pack_owner_top_level_key'
24+ require 'code_ownership/permit_pack_owner_top_level_key'
2625end
2726
2827module CodeOwnership
@@ -36,50 +35,13 @@ module CodeOwnership
3635
3736 sig { params ( file : String ) . returns ( T . nilable ( CodeTeams ::Team ) ) }
3837 def for_file ( file )
39- @for_file ||= T . let ( @for_file , T . nilable ( T ::Hash [ String , T . nilable ( CodeTeams ::Team ) ] ) )
40- @for_file ||= { }
41-
42- return nil if file . start_with? ( './' )
43- return @for_file [ file ] if @for_file . key? ( file )
44-
45- Private . load_configuration!
46-
47- owner = T . let ( nil , T . nilable ( CodeTeams ::Team ) )
48-
49- Mapper . all . each do |mapper |
50- owner = mapper . map_file_to_owner ( file )
51- break if owner # TODO: what if there are multiple owners? Should we respond with an error instead of the first match?
52- end
53-
54- @for_file [ file ] = owner
38+ TeamFinder . for_file ( file )
5539 end
5640
5741 sig { params ( team : T . any ( CodeTeams ::Team , String ) ) . returns ( String ) }
5842 def for_team ( team )
5943 team = T . must ( CodeTeams . find ( team ) ) if team . is_a? ( String )
60- ownership_information = T . let ( [ ] , T ::Array [ String ] )
61-
62- ownership_information << "# Code Ownership Report for `#{ team . name } ` Team"
63-
64- Private . glob_cache . raw_cache_contents . each do |mapper_description , glob_to_owning_team_map |
65- ownership_information << "## #{ mapper_description } "
66- ownership_for_mapper = [ ]
67- glob_to_owning_team_map . each do |glob , owning_team |
68- next if owning_team != team
69-
70- ownership_for_mapper << "- #{ glob } "
71- end
72-
73- if ownership_for_mapper . empty?
74- ownership_information << 'This team owns nothing in this category.'
75- else
76- ownership_information += ownership_for_mapper . sort
77- end
78-
79- ownership_information << ''
80- end
81-
82- ownership_information . join ( "\n " )
44+ ::RustCodeOwners . for_team ( team . name )
8345 end
8446
8547 class InvalidCodeOwnershipConfigurationError < StandardError
@@ -102,92 +64,35 @@ def validate!(
10264 stage_changes : true ,
10365 files : nil
10466 )
105- Private . validate! ( autocorrect : autocorrect , stage_changes : stage_changes )
67+ if autocorrect
68+ ::RustCodeOwners . generate_and_validate ( !stage_changes )
69+ else
70+ ::RustCodeOwners . validate
71+ end
10672 end
10773
10874 # Given a backtrace from either `Exception#backtrace` or `caller`, find the
10975 # first line that corresponds to a file with assigned ownership
11076 sig { params ( backtrace : T . nilable ( T ::Array [ String ] ) , excluded_teams : T ::Array [ ::CodeTeams ::Team ] ) . returns ( T . nilable ( ::CodeTeams ::Team ) ) }
11177 def for_backtrace ( backtrace , excluded_teams : [ ] )
112- first_owned_file_for_backtrace ( backtrace , excluded_teams : excluded_teams ) &. first
78+ TeamFinder . for_backtrace ( backtrace , excluded_teams : excluded_teams )
11379 end
11480
11581 # Given a backtrace from either `Exception#backtrace` or `caller`, find the
11682 # first owned file in it, useful for figuring out which file is being blamed.
11783 sig { params ( backtrace : T . nilable ( T ::Array [ String ] ) , excluded_teams : T ::Array [ ::CodeTeams ::Team ] ) . returns ( T . nilable ( [ ::CodeTeams ::Team , String ] ) ) }
11884 def first_owned_file_for_backtrace ( backtrace , excluded_teams : [ ] )
119- backtrace_with_ownership ( backtrace ) . each do |( team , file ) |
120- if team && !excluded_teams . include? ( team )
121- return [ team , file ]
122- end
123- end
124-
125- nil
126- end
127-
128- sig { params ( backtrace : T . nilable ( T ::Array [ String ] ) ) . returns ( T ::Enumerable [ [ T . nilable ( ::CodeTeams ::Team ) , String ] ] ) }
129- def backtrace_with_ownership ( backtrace )
130- return [ ] unless backtrace
131-
132- # The pattern for a backtrace hasn't changed in forever and is considered
133- # stable: https://github.com/ruby/ruby/blob/trunk/vm_backtrace.c#L303-L317
134- #
135- # This pattern matches a line like the following:
136- #
137- # ./app/controllers/some_controller.rb:43:in `block (3 levels) in create'
138- #
139- backtrace_line = if RUBY_VERSION >= '3.4.0'
140- %r{\A (#{ Pathname . pwd } /|\. /)?
141- (?<file>.+) # Matches 'app/controllers/some_controller.rb'
142- :
143- (?<line>\d +) # Matches '43'
144- :in\s
145- '(?<function>.*)' # Matches "`block (3 levels) in create'"
146- \z }x
147- else
148- %r{\A (#{ Pathname . pwd } /|\. /)?
149- (?<file>.+) # Matches 'app/controllers/some_controller.rb'
150- :
151- (?<line>\d +) # Matches '43'
152- :in\s
153- `(?<function>.*)' # Matches "`block (3 levels) in create'"
154- \z }x
155- end
156-
157- backtrace . lazy . filter_map do |line |
158- match = line . match ( backtrace_line )
159- next unless match
160-
161- file = T . must ( match [ :file ] )
162-
163- [
164- CodeOwnership . for_file ( file ) ,
165- file
166- ]
167- end
85+ TeamFinder . first_owned_file_for_backtrace ( backtrace , excluded_teams : excluded_teams )
16886 end
169- private_class_method ( :backtrace_with_ownership )
17087
17188 sig { params ( klass : T . nilable ( T . any ( T ::Class [ T . anything ] , Module ) ) ) . returns ( T . nilable ( ::CodeTeams ::Team ) ) }
17289 def for_class ( klass )
173- @memoized_values ||= T . let ( @memoized_values , T . nilable ( T ::Hash [ String , T . nilable ( ::CodeTeams ::Team ) ] ) )
174- @memoized_values ||= { }
175- # We use key because the memoized value could be `nil`
176- if @memoized_values . key? ( klass . to_s )
177- @memoized_values [ klass . to_s ]
178- else
179- path = Private . path_from_klass ( klass )
180- return nil if path . nil?
181-
182- value_to_memoize = for_file ( path )
183- @memoized_values [ klass . to_s ] = value_to_memoize
184- value_to_memoize
185- end
90+ TeamFinder . for_class ( klass )
18691 end
18792
18893 sig { params ( package : Packs ::Pack ) . returns ( T . nilable ( ::CodeTeams ::Team ) ) }
18994 def for_package ( package )
190- Private :: OwnershipMappers :: PackageOwnership . new . owner_for_package ( package )
95+ TeamFinder . for_package ( package )
19196 end
19297
19398 # Generally, you should not ever need to do this, because once your ruby process loads, cached content should not change.
@@ -196,14 +101,6 @@ def for_package(package)
196101 # has different ownership and tracked files.
197102 sig { void }
198103 def self . bust_caches!
199- @for_file = nil
200- @memoized_values = nil
201- Private . bust_caches!
202- Mapper . all . each ( &:bust_caches! )
203- end
204-
205- sig { returns ( Configuration ) }
206- def self . configuration
207- Private . configuration
104+ FilePathTeamCache . bust_cache!
208105 end
209106end
0 commit comments