Skip to content

Commit bd75d69

Browse files
att14perryqh
andauthored
feat: check .codeowner within directory if it is provided to for_file (#77)
* feat: check `.codeowner` within directory if it is provided to `for_file` * bump version * move pathnames to constants * add absolute path test --------- Co-authored-by: Perry Hertler <[email protected]>
1 parent 5dea641 commit bd75d69

File tree

4 files changed

+37
-20
lines changed

4 files changed

+37
-20
lines changed

Gemfile.lock

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
PATH
22
remote: .
33
specs:
4-
code_ownership (1.35.0)
4+
code_ownership (1.36.0)
55
code_teams (~> 1.0)
66
packs-specification
77
sorbet-runtime (>= 0.5.10821)

code_ownership.gemspec

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Gem::Specification.new do |spec|
22
spec.name = 'code_ownership'
3-
spec.version = '1.35.0'
3+
spec.version = '1.36.0'
44
spec.authors = ['Gusto Engineers']
55
spec.email = ['[email protected]']
66
spec.summary = 'A gem to help engineering teams declare ownership of code'

lib/code_ownership/private/ownership_mappers/directory_ownership.rb

+30-18
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ class DirectoryOwnership
1010
include Mapper
1111

1212
CODEOWNERS_DIRECTORY_FILE_NAME = '.codeowner'
13+
RELATIVE_ROOT = Pathname('.').freeze
14+
ABSOLUTE_ROOT = Pathname('/').freeze
1315

1416
@@directory_cache = T.let({}, T::Hash[String, T.nilable(CodeTeams::Team)]) # rubocop:disable Style/ClassVars
1517

@@ -74,36 +76,46 @@ def owner_for_codeowners_file(codeowners_file)
7476
)
7577
end
7678

77-
# takes a file and finds the relevant `.codeowner` file by walking up the directory
79+
# Takes a file and finds the relevant `.codeowner` file by walking up the directory
7880
# structure. Example, given `a/b/c.rb`, this looks for `a/b/.codeowner`, `a/.codeowner`,
7981
# and `.codeowner` in that order, stopping at the first file to actually exist.
80-
# We do additional caching so that we don't have to check for file existence every time
82+
# If the parovided file is a directory, it will look for `.codeowner` in that directory and then upwards.
83+
# We do additional caching so that we don't have to check for file existence every time.
8184
sig { params(file: String).returns(T.nilable(CodeTeams::Team)) }
8285
def map_file_to_relevant_owner(file)
8386
file_path = Pathname.new(file)
84-
path_components = file_path.each_filename.to_a.map { |path| Pathname.new(path) }
87+
team = T.let(nil, T.nilable(CodeTeams::Team))
8588

86-
(path_components.length - 1).downto(0).each do |i|
87-
potential_relative_path_name = T.must(path_components[0...i]).reduce(Pathname.new('')) { |built_path, path| built_path.join(path) }
88-
potential_codeowners_file = potential_relative_path_name.join(CODEOWNERS_DIRECTORY_FILE_NAME)
89+
if File.directory?(file)
90+
team = get_team_from_codeowners_file_within_directory(file_path)
91+
end
92+
93+
while team.nil? && file_path != RELATIVE_ROOT && file_path != ABSOLUTE_ROOT
94+
file_path = file_path.parent
95+
team = get_team_from_codeowners_file_within_directory(file_path)
96+
end
97+
98+
team
99+
end
89100

90-
potential_codeowners_file_name = potential_codeowners_file.to_s
101+
sig { params(directory: Pathname).returns(T.nilable(CodeTeams::Team)) }
102+
def get_team_from_codeowners_file_within_directory(directory)
103+
potential_codeowners_file = directory.join(CODEOWNERS_DIRECTORY_FILE_NAME)
91104

92-
team = nil
93-
if @@directory_cache.key?(potential_codeowners_file_name)
94-
team = @@directory_cache[potential_codeowners_file_name]
95-
elsif potential_codeowners_file.exist?
96-
team = owner_for_codeowners_file(potential_codeowners_file)
105+
potential_codeowners_file_name = potential_codeowners_file.to_s
97106

98-
@@directory_cache[potential_codeowners_file_name] = team
99-
else
100-
@@directory_cache[potential_codeowners_file_name] = nil
101-
end
107+
team = nil
108+
if @@directory_cache.key?(potential_codeowners_file_name)
109+
team = @@directory_cache[potential_codeowners_file_name]
110+
elsif potential_codeowners_file.exist?
111+
team = owner_for_codeowners_file(potential_codeowners_file)
102112

103-
return team unless team.nil?
113+
@@directory_cache[potential_codeowners_file_name] = team
114+
else
115+
@@directory_cache[potential_codeowners_file_name] = nil
104116
end
105117

106-
nil
118+
return team
107119
end
108120
end
109121
end

spec/lib/code_ownership/private/ownership_mappers/directory_ownership_spec.rb

+5
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ module CodeOwnership
2121
it 'can find the owner of files in a sub-directory of a team-owned directory' do
2222
expect(CodeOwnership.for_file('a/b/c/c_file.jsx').name).to eq 'Bar'
2323
end
24+
25+
it 'looks for codeowner file within directory' do
26+
expect(CodeOwnership.for_file('a/b').name).to eq 'Bar'
27+
expect(CodeOwnership.for_file(Pathname.pwd.join('a/b').to_s).name).to eq 'Bar'
28+
end
2429
end
2530
end
2631
end

0 commit comments

Comments
 (0)