Skip to content

Commit 7195411

Browse files
authored
escape globs derived from .codeowner files (#106)
* add a spec for .codeowner in a path containing square bracket chars * add failing spec for .codeowner file inside dir containing brackets * escape globs derived from .codeowner files * bump version 1.36.3 => 1.37.0 * typo * fix example filenames
1 parent a1d74dd commit 7195411

File tree

3 files changed

+50
-3
lines changed

3 files changed

+50
-3
lines changed

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.36.3'
3+
spec.version = '1.37.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

+23-2
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ def globs_to_owner(files)
4848
.map(&:cleanpath)
4949
.each_with_object({}) do |pathname, res|
5050
owner = owner_for_codeowners_file(pathname)
51-
res[pathname.dirname.cleanpath.join('**/**').to_s] = owner
51+
glob = glob_for_codeowners_file(pathname)
52+
res[glob] = owner
5253
end
5354
end
5455

@@ -77,7 +78,7 @@ def owner_for_codeowners_file(codeowners_file)
7778
# Takes a file and finds the relevant `.codeowner` file by walking up the directory
7879
# structure. Example, given `a/b/c.rb`, this looks for `a/b/.codeowner`, `a/.codeowner`,
7980
# and `.codeowner` in that order, stopping at the first file to actually exist.
80-
# If the parovided file is a directory, it will look for `.codeowner` in that directory and then upwards.
81+
# If the provided file is a directory, it will look for `.codeowner` in that directory and then upwards.
8182
# We do additional caching so that we don't have to check for file existence every time.
8283
sig { params(file: String).returns(T.nilable(CodeTeams::Team)) }
8384
def map_file_to_relevant_owner(file)
@@ -123,6 +124,26 @@ def get_team_from_codeowners_file_within_directory(directory)
123124

124125
team
125126
end
127+
128+
sig { params(codeowners_file: Pathname).returns(String) }
129+
def glob_for_codeowners_file(codeowners_file)
130+
unescaped = codeowners_file.dirname.cleanpath.join('**/**').to_s
131+
132+
# Globs can contain certain regex characters, like "[" and "]".
133+
# However, when we are generating a glob from a .codeowner file, we
134+
# need to escape bracket characters and interpret them literally.
135+
# Otherwise the resulting glob will not actually match the directory
136+
# containing the .codeowner file.
137+
#
138+
# Example
139+
# file: "/some/[dir]/.codeowner"
140+
# unescaped: "/some/[dir]/**/**"
141+
# matches: "/some/d/file"
142+
# matches: "/some/i/file"
143+
# matches: "/some/r/file"
144+
# does not match!: "/some/[dir]/file"
145+
unescaped.gsub(/[\[\]]/) { |x| "\\#{x}" }
146+
end
126147
end
127148
end
128149
end

spec/lib/code_ownership_spec.rb

+26
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,19 @@
2525
end
2626
end
2727

28+
context 'directory with [] characters containing a .codeowner file' do
29+
before do
30+
write_file('app/services/[test]/.codeowner', <<~CONTENTS)
31+
Bar
32+
CONTENTS
33+
write_file('app/services/[test]/some_file.rb', '')
34+
end
35+
36+
it 'has no validation errors' do
37+
expect { CodeOwnership.validate!(files: ['app/services/[test]/some_file.rb']) }.to_not raise_error
38+
end
39+
end
40+
2841
context 'file ownership with [] characters' do
2942
before do
3043
write_file('app/services/[test]/some_file.ts', <<~TYPESCRIPT)
@@ -161,6 +174,19 @@
161174
end
162175
end
163176

177+
context '.codeowner in a directory with [] characters' do
178+
before do
179+
write_file('app/javascript/[test]/.codeowner', <<~CONTENTS)
180+
Bar
181+
CONTENTS
182+
write_file('app/javascript/[test]/test.js', '')
183+
end
184+
185+
it 'properly assigns ownership' do
186+
expect(CodeOwnership.for_file('app/javascript/[test]/test.js')).to eq CodeTeams.find('Bar')
187+
end
188+
end
189+
164190
before { create_non_empty_application }
165191
end
166192

0 commit comments

Comments
 (0)