code_ownership 1.36.3 → 1.38.0

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: eb464ff5e39c0c9f2712a7af6498f30b3b42e3c456e7da52a5209d7f30467346
4
- data.tar.gz: 5fd3f8974a041ff298fbdaceca0b3eccee027f4d9c94bdfaa344ae9b8ade72d5
3
+ metadata.gz: 7883f0d73587e099adf43101b91f0a2f296912cfa3e994e2f1c70472de918e4b
4
+ data.tar.gz: 1e11b8c1ce41986fa40325b3edf10369b00a7ed8a42f47e2ed4fe9694f565642
5
5
  SHA512:
6
- metadata.gz: ef51049577f74ab3faed01b4a37df119eaf577465fa10f342b79c0fabf7bc89b59ed993f8c1d8f687c53a3957745dfe579430e081473b9eb63f9b3cbe1f8eb19
7
- data.tar.gz: ba03e839b81e0dcdfd1c07356b99dbf850b9abddaa83049c2c74e458012c4b81bdfaead12ed6c59fc744848261e078d715f6512df876d1d1fa238362910bfcb9
6
+ metadata.gz: 5a381fc3a9867de019b70ada03aca768c427b05198449c4d6478f245f79fefd8e2bd56ab5df06fbe62577d12ce6f6bf80a6509f8347f65831ab8251075b1047f
7
+ data.tar.gz: 5ea28b28bbf41f5620fbf07d6d4083f8dcfea577648612fd8f05d4e128007ff73d6cec2e1a433ef84cf35a1e4b2c8b4a2d4fca0a8bbaeafedca948be293aaaba
data/README.md CHANGED
@@ -8,6 +8,30 @@ Check out [`code_ownership_spec.rb`](https://github.com/rubyatscale/code_ownersh
8
8
 
9
9
  There is also a [companion VSCode Extension]([url](https://github.com/rubyatscale/code-ownership-vscode)) for this gem. Just search `Gusto.code-ownership-vscode` in the VSCode Extension Marketplace.
10
10
 
11
+ ## Getting started
12
+
13
+ To get started there's a few things you should do.
14
+
15
+ 1) Create a `config/code_ownership.yml` file and declare where your files live. Here's a sample to start with:
16
+ ```yml
17
+ owned_globs:
18
+ - '{app,components,config,frontend,lib,packs,spec}/**/*.{rb,rake,js,jsx,ts,tsx}'
19
+ js_package_paths: []
20
+ unowned_globs:
21
+ - db/**/*
22
+ - app/services/some_file1.rb
23
+ - app/services/some_file2.rb
24
+ - frontend/javascripts/**/__generated__/**/*
25
+ ```
26
+ 2) Declare some teams. Here's an example, that would live at `config/teams/operations.yml`:
27
+ ```yml
28
+ name: Operations
29
+ github:
30
+ team: '@my-org/operations-team'
31
+ ```
32
+ 3) Declare ownership. You can do this at a directory level or at a file level. All of the files within the `owned_globs` you declared in step 1 will need to have an owner assigned (or be opted out via `unowned_globs`). See the next section for more detail.
33
+ 4) Run validations when you commit, and/or in CI. If you run validations in CI, ensure that if your `.github/CODEOWNERS` file gets changed, that gets pushed to the PR.
34
+
11
35
  ## Usage: Declaring Ownership
12
36
 
13
37
  There are three ways to declare code ownership using this gem.
@@ -48,7 +48,8 @@ module CodeOwnership
48
48
  .map(&:cleanpath)
49
49
  .each_with_object({}) do |pathname, res|
50
50
  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
52
53
  end
53
54
  end
54
55
 
@@ -77,7 +78,7 @@ module CodeOwnership
77
78
  # Takes a file and finds the relevant `.codeowner` file by walking up the directory
78
79
  # structure. Example, given `a/b/c.rb`, this looks for `a/b/.codeowner`, `a/.codeowner`,
79
80
  # 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.
81
82
  # We do additional caching so that we don't have to check for file existence every time.
82
83
  sig { params(file: String).returns(T.nilable(CodeTeams::Team)) }
83
84
  def map_file_to_relevant_owner(file)
@@ -123,6 +124,26 @@ module CodeOwnership
123
124
 
124
125
  team
125
126
  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
126
147
  end
127
148
  end
128
149
  end
@@ -39,7 +39,8 @@ module CodeOwnership
39
39
  owner = file_annotation_based_owner(filename_relative_to_root)
40
40
  next unless owner
41
41
 
42
- mapping[filename_relative_to_root] = owner
42
+ escaped_filename = escaped_path_for_codeowners_file(filename_relative_to_root)
43
+ mapping[escaped_filename] = owner
43
44
  end
44
45
  end
45
46
 
@@ -55,7 +56,8 @@ module CodeOwnership
55
56
 
56
57
  invalid_files = cache.keys.select do |file|
57
58
  # If a file is not tracked, it should be removed from the cache
58
- !Private.file_tracked?(file) ||
59
+ unescaped_file = unescaped_path_for_codeowners_file(file)
60
+ !Private.file_tracked?(unescaped_file) ||
59
61
  # If a file no longer has a file annotation (i.e. `globs_to_owner` doesn't map it)
60
62
  # it should be removed from the cache
61
63
  # We make sure to only apply this to the input files since otherwise `updated_cache_for_files.key?(file)` would always return `false` when files == []
@@ -126,6 +128,32 @@ module CodeOwnership
126
128
 
127
129
  sig { override.void }
128
130
  def bust_caches!; end
131
+
132
+ sig { params(filename: String).returns(String) }
133
+ def escaped_path_for_codeowners_file(filename)
134
+ # Globs can contain certain regex characters, like "[" and "]".
135
+ # However, when we are generating a glob from a file annotation, we
136
+ # need to escape bracket characters and interpret them literally.
137
+ # Otherwise the resulting glob will not actually match the directory
138
+ # containing the file.
139
+ #
140
+ # Example
141
+ # filename: "/some/[xId]/myfile.tsx"
142
+ # matches: "/some/1/file"
143
+ # matches: "/some/2/file"
144
+ # matches: "/some/3/file"
145
+ # does not match!: "/some/[xId]/myfile.tsx"
146
+ filename.gsub(/[\[\]]/) { |x| "\\#{x}" }
147
+ end
148
+
149
+ sig { params(filename: String).returns(String) }
150
+ def unescaped_path_for_codeowners_file(filename)
151
+ # Globs can contain certain regex characters, like "[" and "]".
152
+ # We escape bracket characters and interpret them literally for
153
+ # the CODEOWNERS file. However, we want to compare the unescaped
154
+ # glob to the actual file path when we check if the file was deleted.
155
+ filename.gsub(/\\([\[\]])/, '\1')
156
+ end
129
157
  end
130
158
  end
131
159
  end
@@ -99,6 +99,7 @@ module CodeOwnership
99
99
  in_unowned_globs = configuration.unowned_globs.any? do |unowned_glob|
100
100
  File.fnmatch?(unowned_glob, file, File::FNM_PATHNAME | File::FNM_EXTGLOB)
101
101
  end
102
+
102
103
  in_owned_globs && !in_unowned_globs && File.exist?(file)
103
104
  end
104
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.36.3
4
+ version: 1.38.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gusto Engineers
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-07-30 00:00:00.000000000 Z
11
+ date: 2024-11-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: code_teams
@@ -220,7 +220,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
220
220
  - !ruby/object:Gem::Version
221
221
  version: '0'
222
222
  requirements: []
223
- rubygems_version: 3.5.11
223
+ rubygems_version: 3.5.22
224
224
  signing_key:
225
225
  specification_version: 4
226
226
  summary: A gem to help engineering teams declare ownership of code