code_ownership 2.1.0-x86_64-linux → 2.1.2-x86_64-linux

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: 5eefa130f8ea4807d7330296ff516bf19dfba1da29b4a305494825701622287d
4
- data.tar.gz: 2af8901f13bc6bc310efb541cf1ca40592d48e48d62faca7204df2fa16b05df9
3
+ metadata.gz: 11870e7233492c859a41589bd1a85a43825e2fe4ab12279fe681df97ace60fa8
4
+ data.tar.gz: bb5e9e12b04cfa6bc8bc73e557066f04a9a00401dd0f9d462cc6b3b59b9db583
5
5
  SHA512:
6
- metadata.gz: f8c91fb0c2c5950de1b189b32eb5d36b1c4fd9d55c4c5e708a8070ddbd3899e89d1b2b1e19e4d37535a3f9e78538ca0c83f66a22e011e541e20f5f1dd17f2c18
7
- data.tar.gz: fdc5d26e742254450fb022b21d8280ef05d1db863923a2b45dc82cbcc1d910cc4653e793c246a25162f8025e17f740339b6e75edfffd9a0bbca25d5a03b23edc
6
+ metadata.gz: beefff80a9b1d0dd3a618671ebd1fc9e6d74898c06be8dced72d45e852fe8e4c3759771d02b4edd5f86d3c7b5602d73e00f89478847f1400b10df6651e565bdf
7
+ data.tar.gz: d0aeb2d7c0653417cb969da695111a7ce54b8186e62a3c3618d7cd5a9510bbd29f27dfc85cc8eca3a67960d3e9d8ddc9c98f156d9d23e7fab8723104fcf94f43
data/README.md CHANGED
@@ -10,7 +10,7 @@ There is also a [companion VSCode Extension](https://github.com/rubyatscale/code
10
10
 
11
11
  ## Getting started
12
12
 
13
- To get started there's a few things you should do.
13
+ To get started there are a few things you should do.
14
14
 
15
15
  1. Create a `config/code_ownership.yml` file and declare where your files live. Here's a sample to start with:
16
16
 
@@ -42,7 +42,7 @@ There are five ways to declare code ownership using this gem:
42
42
 
43
43
  ### Directory-Based Ownership
44
44
 
45
- Directory based ownership allows for all files in that directory and all its sub-directories to be owned by one team. To define this, add a `.codeowner` file inside that directory with the name of the team as the contents of that file.
45
+ Directory-based ownership allows for all files in that directory and all its sub-directories to be owned by one team. To define this, add a `.codeowner` file inside that directory with the name of the team as the contents of that file.
46
46
 
47
47
  ```
48
48
  Team
@@ -58,7 +58,7 @@ File annotations are a last resort if there is no clear home for your code. File
58
58
 
59
59
  ### Package-Based Ownership
60
60
 
61
- Package based ownership integrates [`packwerk`](https://github.com/Shopify/packwerk) and has ownership defined per package. To define that all files within a package are owned by one team, configure your `package.yml` like this:
61
+ Package-based ownership integrates [`packwerk`](https://github.com/Shopify/packwerk) and has ownership defined per package. To define that all files within a package are owned by one team, configure your `package.yml` like this:
62
62
 
63
63
  ```yml
64
64
  enforce_dependency: true
@@ -92,7 +92,7 @@ unowned_globs:
92
92
 
93
93
  ### Javascript Package Ownership
94
94
 
95
- Javascript package based ownership allows you to specify an ownership key in a `package.json`. To use this, configure your `package.json` like this:
95
+ JavaScript package-based ownership allows you to specify an ownership key in a `package.json`. To use this, configure your `package.json` like this:
96
96
 
97
97
  ```json
98
98
  {
@@ -112,10 +112,10 @@ js_package_paths:
112
112
  - frontend/other_location_for_packages/*
113
113
  ```
114
114
 
115
- This defaults `**/`, which makes it look for `package.json` files across your application.
115
+ This defaults to `**/`, which makes it look for `package.json` files across your application.
116
116
 
117
117
  > [!NOTE]
118
- > Javscript package ownership does not respect `unowned_globs`. If you wish to disable usage of this feature you can set `js_package_paths` to an empty list.
118
+ > JavaScript package ownership does not respect `unowned_globs`. If you wish to disable usage of this feature you can set `js_package_paths` to an empty list.
119
119
 
120
120
  ```yml
121
121
  js_package_paths: []
@@ -184,7 +184,7 @@ If `codeowners_path` is set in `code_ownership.yml` codeowners will use that pat
184
184
  CodeOwnership comes with a validation function to ensure the following things are true:
185
185
 
186
186
  1. Only one mechanism is defining file ownership. That is -- you can't have a file annotation on a file owned via package-based or glob-based ownership. This helps make ownership behavior more clear by avoiding concerns about precedence.
187
- 2. All teams referenced as an owner for any file or package is a valid team (i.e. it's in the list of `CodeTeams.all`).
187
+ 2. All teams referenced as an owner for any file or package are valid teams (i.e. they're in the list of `CodeTeams.all`).
188
188
  3. All files have ownership. You can specify in `unowned_globs` to represent a TODO list of files to add ownership to.
189
189
  4. The `.github/CODEOWNERS` file is up to date. This is automatically corrected and staged unless specified otherwise with `bin/codeownership validate --skip-autocorrect --skip-stage`. You can turn this validation off by setting `skip_codeowners_validation: true` in `config/code_ownership.yml`.
190
190
 
@@ -223,7 +223,7 @@ codeownership validate --diff
223
223
 
224
224
  ## Development
225
225
 
226
- Please add to `CHANGELOG.md` and this `README.md` when you make make changes.
226
+ Please add to `CHANGELOG.md` and this `README.md` when you make changes.
227
227
 
228
228
  ## Running specs
229
229
 
@@ -1,4 +1,5 @@
1
1
  # typed: true
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'optparse'
4
5
  require 'pathname'
@@ -1,29 +1,41 @@
1
1
  # frozen_string_literal: true
2
-
3
2
  # typed: strict
4
3
 
5
4
  module CodeOwnership
6
5
  module Private
7
6
  module FilePathFinder
8
- module_function
9
-
10
7
  extend T::Sig
11
- extend T::Helpers
8
+
9
+ sig { returns(String) }
10
+ def self.pwd_prefix
11
+ @pwd_prefix ||= T.let("#{Dir.pwd}/", T.nilable(String))
12
+ end
13
+
14
+ sig { returns(Pathname) }
15
+ def self.pwd
16
+ @pwd ||= T.let(Pathname.pwd, T.nilable(Pathname))
17
+ end
12
18
 
13
19
  # Returns a string version of the relative path to a Rails constant,
14
20
  # or nil if it can't find anything
15
- sig { params(klass: T.nilable(T.any(T::Class[T.anything], Module))).returns(T.nilable(String)) }
16
- def path_from_klass(klass)
21
+ sig { params(klass: T.nilable(T.any(T::Class[T.anything], T::Module[T.anything]))).returns(T.nilable(String)) }
22
+ def self.path_from_klass(klass)
17
23
  if klass
18
24
  path = Object.const_source_location(klass.to_s)&.first
19
- (path && Pathname.new(path).relative_path_from(Pathname.pwd).to_s) || nil
25
+ return nil unless path
26
+
27
+ if path.start_with?(pwd_prefix)
28
+ path.delete_prefix(pwd_prefix)
29
+ else
30
+ Pathname.new(path).relative_path_from(pwd).to_s
31
+ end
20
32
  end
21
33
  rescue NameError
22
34
  nil
23
35
  end
24
36
 
25
37
  sig { params(backtrace: T.nilable(T::Array[String])).returns(T::Enumerable[String]) }
26
- def from_backtrace(backtrace)
38
+ def self.from_backtrace(backtrace)
27
39
  return [] unless backtrace
28
40
 
29
41
  # The pattern for a backtrace hasn't changed in forever and is considered
@@ -34,7 +46,7 @@ module CodeOwnership
34
46
  # ./app/controllers/some_controller.rb:43:in `block (3 levels) in create'
35
47
  #
36
48
  backtrace_line = if RUBY_VERSION >= '3.4.0'
37
- %r{\A(#{Pathname.pwd}/|\./)?
49
+ %r{\A(#{pwd}/|\./)?
38
50
  (?<file>.+) # Matches 'app/controllers/some_controller.rb'
39
51
  :
40
52
  (?<line>\d+) # Matches '43'
@@ -42,7 +54,7 @@ module CodeOwnership
42
54
  '(?<function>.*)' # Matches "`block (3 levels) in create'"
43
55
  \z}x
44
56
  else
45
- %r{\A(#{Pathname.pwd}/|\./)?
57
+ %r{\A(#{pwd}/|\./)?
46
58
  (?<file>.+) # Matches 'app/controllers/some_controller.rb'
47
59
  :
48
60
  (?<line>\d+) # Matches '43'
@@ -1,37 +1,33 @@
1
1
  # frozen_string_literal: true
2
-
3
2
  # typed: strict
4
3
 
5
4
  module CodeOwnership
6
5
  module Private
7
6
  module FilePathTeamCache
8
- module_function
9
-
10
7
  extend T::Sig
11
- extend T::Helpers
12
8
 
13
9
  sig { params(file_path: String).returns(T.nilable(CodeTeams::Team)) }
14
- def get(file_path)
10
+ def self.get(file_path)
15
11
  cache[file_path]
16
12
  end
17
13
 
18
14
  sig { params(file_path: String, team: T.nilable(CodeTeams::Team)).void }
19
- def set(file_path, team)
15
+ def self.set(file_path, team)
20
16
  cache[file_path] = team
21
17
  end
22
18
 
23
19
  sig { params(file_path: String).returns(T::Boolean) }
24
- def cached?(file_path)
20
+ def self.cached?(file_path)
25
21
  cache.key?(file_path)
26
22
  end
27
23
 
28
24
  sig { void }
29
- def bust_cache!
25
+ def self.bust_cache!
30
26
  @cache = nil
31
27
  end
32
28
 
33
29
  sig { returns(T::Hash[String, T.nilable(CodeTeams::Team)]) }
34
- def cache
30
+ def self.cache
35
31
  @cache ||= T.let(@cache,
36
32
  T.nilable(T::Hash[String, T.nilable(CodeTeams::Team)]))
37
33
  @cache ||= {}
@@ -1,5 +1,4 @@
1
1
  # frozen_string_literal: true
2
-
3
2
  # typed: strict
4
3
 
5
4
  module CodeOwnership
@@ -1,27 +1,20 @@
1
1
  # frozen_string_literal: true
2
-
3
2
  # typed: strict
4
3
 
5
4
  module CodeOwnership
6
5
  module Private
7
6
  module TeamFinder
8
- module_function
9
-
10
7
  extend T::Sig
11
- extend T::Helpers
12
-
13
- requires_ancestor { Kernel }
14
8
 
15
9
  sig { params(file_path: String, allow_raise: T::Boolean).returns(T.nilable(CodeTeams::Team)) }
16
- def for_file(file_path, allow_raise: false)
10
+ def self.for_file(file_path, allow_raise: false)
17
11
  return nil if file_path.start_with?('./')
18
12
 
19
13
  return FilePathTeamCache.get(file_path) if FilePathTeamCache.cached?(file_path)
20
14
 
21
15
  result = T.let(RustCodeOwners.for_file(file_path), T.nilable(T::Hash[Symbol, String]))
22
- return if result.nil?
23
16
 
24
- if result[:team_name].nil?
17
+ if result.nil? || result[:team_name].nil?
25
18
  FilePathTeamCache.set(file_path, nil)
26
19
  else
27
20
  FilePathTeamCache.set(file_path, T.let(find_team!(T.must(result[:team_name]), allow_raise: allow_raise), T.nilable(CodeTeams::Team)))
@@ -31,7 +24,7 @@ module CodeOwnership
31
24
  end
32
25
 
33
26
  sig { params(files: T::Array[String], allow_raise: T::Boolean).returns(T::Hash[String, T.nilable(CodeTeams::Team)]) }
34
- def teams_for_files(files, allow_raise: false)
27
+ def self.teams_for_files(files, allow_raise: false)
35
28
  result = {}
36
29
 
37
30
  # Collect cached results and identify non-cached files
@@ -57,8 +50,8 @@ module CodeOwnership
57
50
  result
58
51
  end
59
52
 
60
- sig { params(klass: T.nilable(T.any(T::Class[T.anything], Module))).returns(T.nilable(::CodeTeams::Team)) }
61
- def for_class(klass)
53
+ sig { params(klass: T.nilable(T.any(T::Class[T.anything], T::Module[T.anything]))).returns(T.nilable(::CodeTeams::Team)) }
54
+ def self.for_class(klass)
62
55
  file_path = FilePathFinder.path_from_klass(klass)
63
56
  return nil if file_path.nil?
64
57
 
@@ -66,7 +59,7 @@ module CodeOwnership
66
59
  end
67
60
 
68
61
  sig { params(package: Packs::Pack).returns(T.nilable(::CodeTeams::Team)) }
69
- def for_package(package)
62
+ def self.for_package(package)
70
63
  owner_name = package.raw_hash['owner'] || package.metadata['owner']
71
64
  return nil if owner_name.nil?
72
65
 
@@ -74,12 +67,12 @@ module CodeOwnership
74
67
  end
75
68
 
76
69
  sig { params(backtrace: T.nilable(T::Array[String]), excluded_teams: T::Array[::CodeTeams::Team]).returns(T.nilable(::CodeTeams::Team)) }
77
- def for_backtrace(backtrace, excluded_teams: [])
70
+ def self.for_backtrace(backtrace, excluded_teams: [])
78
71
  first_owned_file_for_backtrace(backtrace, excluded_teams: excluded_teams)&.first
79
72
  end
80
73
 
81
74
  sig { params(backtrace: T.nilable(T::Array[String]), excluded_teams: T::Array[::CodeTeams::Team]).returns(T.nilable([::CodeTeams::Team, String])) }
82
- def first_owned_file_for_backtrace(backtrace, excluded_teams: [])
75
+ def self.first_owned_file_for_backtrace(backtrace, excluded_teams: [])
83
76
  FilePathFinder.from_backtrace(backtrace).each do |file|
84
77
  team = for_file(file)
85
78
  if team && !excluded_teams.include?(team)
@@ -91,7 +84,7 @@ module CodeOwnership
91
84
  end
92
85
 
93
86
  sig { params(team_name: String, allow_raise: T::Boolean).returns(T.nilable(CodeTeams::Team)) }
94
- def find_team!(team_name, allow_raise: false)
87
+ def self.find_team!(team_name, allow_raise: false)
95
88
  team = CodeTeams.find(team_name)
96
89
  if team.nil? && allow_raise
97
90
  raise(StandardError, "Could not find team with name: `#{team_name}`. Make sure the team is one of `#{CodeTeams.all.map(&:name).sort}`")
@@ -1,6 +1,6 @@
1
- # typed: false
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module CodeOwnership
5
- VERSION = '2.1.0'
5
+ VERSION = '2.1.2'
6
6
  end
@@ -1,5 +1,4 @@
1
1
  # frozen_string_literal: true
2
-
3
2
  # typed: strict
4
3
 
5
4
  require 'code_teams'
@@ -25,17 +24,13 @@ if defined?(Packwerk)
25
24
  end
26
25
 
27
26
  module CodeOwnership
28
- module_function
29
-
30
27
  extend T::Sig
31
- extend T::Helpers
32
28
 
33
- requires_ancestor { Kernel }
34
29
  GlobsToOwningTeamMap = T.type_alias { T::Hash[String, CodeTeams::Team] }
35
30
 
36
31
  # Returns the version of the code_ownership gem and the codeowners-rs gem.
37
32
  sig { returns(T::Array[String]) }
38
- def version
33
+ def self.version
39
34
  ["code_ownership version: #{VERSION}",
40
35
  "codeowners-rs version: #{::RustCodeOwners.version}"]
41
36
  end
@@ -65,7 +60,7 @@ module CodeOwnership
65
60
  # # => raises exception if no owner found
66
61
  #
67
62
  sig { params(file: String, from_codeowners: T::Boolean, allow_raise: T::Boolean).returns(T.nilable(CodeTeams::Team)) }
68
- def for_file(file, from_codeowners: true, allow_raise: false)
63
+ def self.for_file(file, from_codeowners: true, allow_raise: false)
69
64
  if from_codeowners
70
65
  teams_for_files_from_codeowners([file], allow_raise: allow_raise).values.first
71
66
  else
@@ -116,7 +111,7 @@ module CodeOwnership
116
111
  # @see #validate! for ensuring CODEOWNERS file is up-to-date
117
112
  #
118
113
  sig { params(files: T::Array[String], allow_raise: T::Boolean).returns(T::Hash[String, T.nilable(CodeTeams::Team)]) }
119
- def teams_for_files_from_codeowners(files, allow_raise: false)
114
+ def self.teams_for_files_from_codeowners(files, allow_raise: false)
120
115
  Private::TeamFinder.teams_for_files(files, allow_raise: allow_raise)
121
116
  end
122
117
 
@@ -160,12 +155,12 @@ module CodeOwnership
160
155
  # @see CLI#for_file for the command-line interface that uses this method
161
156
  #
162
157
  sig { params(file: String).returns(T.nilable(T::Hash[Symbol, String])) }
163
- def for_file_verbose(file)
158
+ def self.for_file_verbose(file)
164
159
  ::RustCodeOwners.for_file(file)
165
160
  end
166
161
 
167
162
  sig { params(team: T.any(CodeTeams::Team, String)).returns(T::Array[String]) }
168
- def for_team(team)
163
+ def self.for_team(team)
169
164
  team = T.must(CodeTeams.find(team)) if team.is_a?(String)
170
165
  ::RustCodeOwners.for_team(team.name)
171
166
  end
@@ -226,7 +221,7 @@ module CodeOwnership
226
221
  files: T.nilable(T::Array[String])
227
222
  ).void
228
223
  end
229
- def validate!(
224
+ def self.validate!(
230
225
  autocorrect: true,
231
226
  stage_changes: true,
232
227
  files: nil
@@ -269,7 +264,7 @@ module CodeOwnership
269
264
  # @note Leading newlines after the annotation are also removed to maintain clean formatting.
270
265
  #
271
266
  sig { params(filename: String).void }
272
- def remove_file_annotation!(filename)
267
+ def self.remove_file_annotation!(filename)
273
268
  filepath = Pathname.new(filename)
274
269
 
275
270
  begin
@@ -292,24 +287,24 @@ module CodeOwnership
292
287
  # Given a backtrace from either `Exception#backtrace` or `caller`, find the
293
288
  # first line that corresponds to a file with assigned ownership
294
289
  sig { params(backtrace: T.nilable(T::Array[String]), excluded_teams: T::Array[::CodeTeams::Team]).returns(T.nilable(::CodeTeams::Team)) }
295
- def for_backtrace(backtrace, excluded_teams: [])
290
+ def self.for_backtrace(backtrace, excluded_teams: [])
296
291
  Private::TeamFinder.for_backtrace(backtrace, excluded_teams: excluded_teams)
297
292
  end
298
293
 
299
294
  # Given a backtrace from either `Exception#backtrace` or `caller`, find the
300
295
  # first owned file in it, useful for figuring out which file is being blamed.
301
296
  sig { params(backtrace: T.nilable(T::Array[String]), excluded_teams: T::Array[::CodeTeams::Team]).returns(T.nilable([::CodeTeams::Team, String])) }
302
- def first_owned_file_for_backtrace(backtrace, excluded_teams: [])
297
+ def self.first_owned_file_for_backtrace(backtrace, excluded_teams: [])
303
298
  Private::TeamFinder.first_owned_file_for_backtrace(backtrace, excluded_teams: excluded_teams)
304
299
  end
305
300
 
306
- sig { params(klass: T.nilable(T.any(T::Class[T.anything], Module))).returns(T.nilable(::CodeTeams::Team)) }
307
- def for_class(klass)
301
+ sig { params(klass: T.nilable(T.any(T::Class[T.anything], T::Module[T.anything]))).returns(T.nilable(::CodeTeams::Team)) }
302
+ def self.for_class(klass)
308
303
  Private::TeamFinder.for_class(klass)
309
304
  end
310
305
 
311
306
  sig { params(package: Packs::Pack).returns(T.nilable(::CodeTeams::Team)) }
312
- def for_package(package)
307
+ def self.for_package(package)
313
308
  Private::TeamFinder.for_package(package)
314
309
  end
315
310
 
@@ -320,5 +315,7 @@ module CodeOwnership
320
315
  sig { void }
321
316
  def self.bust_caches!
322
317
  Private::FilePathTeamCache.bust_cache!
318
+ Private::FilePathFinder.instance_variable_set(:@pwd, nil)
319
+ Private::FilePathFinder.instance_variable_set(:@pwd_prefix, nil)
323
320
  end
324
321
  end
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: 2.1.0
4
+ version: 2.1.2
5
5
  platform: x86_64-linux
6
6
  authors:
7
7
  - Gusto Engineers
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-11-13 00:00:00.000000000 Z
11
+ date: 2026-04-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: code_teams
@@ -44,14 +44,14 @@ dependencies:
44
44
  requirements:
45
45
  - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: 0.5.11249
47
+ version: 0.6.12763
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: 0.5.11249
54
+ version: 0.6.12763
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: debug
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -176,9 +176,9 @@ files:
176
176
  - README.md
177
177
  - bin/codeownership
178
178
  - lib/code_ownership.rb
179
- - lib/code_ownership/3.2/code_ownership.so
180
179
  - lib/code_ownership/3.3/code_ownership.so
181
180
  - lib/code_ownership/3.4/code_ownership.so
181
+ - lib/code_ownership/4.0/code_ownership.so
182
182
  - lib/code_ownership/cli.rb
183
183
  - lib/code_ownership/private/file_path_finder.rb
184
184
  - lib/code_ownership/private/file_path_team_cache.rb
@@ -203,10 +203,10 @@ required_ruby_version: !ruby/object:Gem::Requirement
203
203
  requirements:
204
204
  - - ">="
205
205
  - !ruby/object:Gem::Version
206
- version: '3.2'
206
+ version: '3.3'
207
207
  - - "<"
208
208
  - !ruby/object:Gem::Version
209
- version: 3.5.dev
209
+ version: 4.1.dev
210
210
  required_rubygems_version: !ruby/object:Gem::Requirement
211
211
  requirements:
212
212
  - - ">="