code_ownership 1.33.1 → 1.34.2
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 +4 -4
- data/README.md +12 -5
- data/lib/code_ownership/private/ownership_mappers/directory_ownership.rb +111 -0
- data/lib/code_ownership/private.rb +2 -1
- data/lib/code_ownership.rb +2 -2
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1a67c504ef03f9010003b0c3fad3eb09aab223ed033d58a090c3a3efc3509a89
|
4
|
+
data.tar.gz: 30aef026b8487bc1c20aa94890a4eca64fdaa26dddd44f17b77f705317c9b038
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '09d72749e78b49b14284c3360cba9f9f3dda4e20818305b3fe0a819bbaa2720be5107bb85d8612bedfbd5590a039010d42700510ac027b34bc1a6bdaadc7531f'
|
7
|
+
data.tar.gz: 4be38dfd55b95eadf9ee661cb160715cc0ee7f3936f9b9b19d8b7ce65d0ed19b62805f882b8b684863acbc2caa602cb676e58837a060da04fd142f1b89b1e744
|
data/README.md
CHANGED
@@ -12,6 +12,18 @@ There is also a [companion VSCode Extension]([url](https://github.com/rubyatscal
|
|
12
12
|
|
13
13
|
There are three ways to declare code ownership using this gem.
|
14
14
|
|
15
|
+
### Directory-Based Ownership
|
16
|
+
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.
|
17
|
+
```
|
18
|
+
Team
|
19
|
+
```
|
20
|
+
|
21
|
+
### File-Annotation Based Ownership
|
22
|
+
File annotations are a last resort if there is no clear home for your code. File annotations go at the top of your file, and look like this:
|
23
|
+
```ruby
|
24
|
+
# @team MyTeam
|
25
|
+
```
|
26
|
+
|
15
27
|
### Package-Based Ownership
|
16
28
|
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:
|
17
29
|
```yml
|
@@ -38,11 +50,6 @@ owned_globs:
|
|
38
50
|
- app/services/stuff_belonging_to_my_team/**/**
|
39
51
|
- app/controllers/other_stuff_belonging_to_my_team/**/**
|
40
52
|
```
|
41
|
-
### File-Annotation Based Ownership
|
42
|
-
File annotations are a last resort if there is no clear home for your code. File annotations go at the top of your file, and look like this:
|
43
|
-
```ruby
|
44
|
-
# @team MyTeam
|
45
|
-
```
|
46
53
|
|
47
54
|
### Javascript Package Ownership
|
48
55
|
Javascript package based ownership allows you to specify an ownership key in a `package.json`. To use this, configure your `package.json` like this:
|
@@ -0,0 +1,111 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# typed: true
|
4
|
+
|
5
|
+
module CodeOwnership
|
6
|
+
module Private
|
7
|
+
module OwnershipMappers
|
8
|
+
class DirectoryOwnership
|
9
|
+
extend T::Sig
|
10
|
+
include Mapper
|
11
|
+
|
12
|
+
CODEOWNERS_DIRECTORY_FILE_NAME = '.codeowner'
|
13
|
+
|
14
|
+
@@directory_cache = T.let({}, T::Hash[String, T.nilable(CodeTeams::Team)]) # rubocop:disable Style/ClassVars
|
15
|
+
|
16
|
+
sig do
|
17
|
+
override.params(file: String).
|
18
|
+
returns(T.nilable(::CodeTeams::Team))
|
19
|
+
end
|
20
|
+
def map_file_to_owner(file)
|
21
|
+
map_file_to_relevant_owner(file)
|
22
|
+
end
|
23
|
+
|
24
|
+
sig do
|
25
|
+
override.params(cache: GlobsToOwningTeamMap, files: T::Array[String]).returns(GlobsToOwningTeamMap)
|
26
|
+
end
|
27
|
+
def update_cache(cache, files)
|
28
|
+
globs_to_owner(files)
|
29
|
+
end
|
30
|
+
|
31
|
+
#
|
32
|
+
# Directory ownership ignores the passed in files when generating code owners lines.
|
33
|
+
# This is because Directory ownership knows that the fastest way to find code owners for directory based ownership
|
34
|
+
# is to simply iterate over the directories and grab the owner, rather than iterating over each file just to get what directory it is in
|
35
|
+
# In theory this means that we may generate code owners lines that cover files that are not in the passed in argument,
|
36
|
+
# but in practice this is not of consequence because in reality we never really want to generate code owners for only a
|
37
|
+
# subset of files, but rather we want code ownership for all files.
|
38
|
+
#
|
39
|
+
sig do
|
40
|
+
override.params(files: T::Array[String]).
|
41
|
+
returns(T::Hash[String, ::CodeTeams::Team])
|
42
|
+
end
|
43
|
+
def globs_to_owner(files)
|
44
|
+
# The T.unsafe is because the upstream RBI is wrong for Pathname.glob
|
45
|
+
T
|
46
|
+
.unsafe(Pathname)
|
47
|
+
.glob(File.join('**/', CODEOWNERS_DIRECTORY_FILE_NAME))
|
48
|
+
.map(&:cleanpath)
|
49
|
+
.each_with_object({}) do |pathname, res|
|
50
|
+
owner = owner_for_codeowners_file(pathname)
|
51
|
+
res[pathname.dirname.cleanpath.join('**/**').to_s] = owner
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
sig { override.returns(String) }
|
56
|
+
def description
|
57
|
+
'Owner in .codeowner'
|
58
|
+
end
|
59
|
+
|
60
|
+
sig { override.void }
|
61
|
+
def bust_caches!
|
62
|
+
@@directory_cache = {} # rubocop:disable Style/ClassVars
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
sig { params(codeowners_file: Pathname).returns(CodeTeams::Team) }
|
68
|
+
def owner_for_codeowners_file(codeowners_file)
|
69
|
+
raw_owner_value = File.foreach(codeowners_file).first.strip
|
70
|
+
|
71
|
+
Private.find_team!(
|
72
|
+
raw_owner_value,
|
73
|
+
codeowners_file.to_s
|
74
|
+
)
|
75
|
+
end
|
76
|
+
|
77
|
+
# takes a file and finds the relevant `.codeowner` file by walking up the directory
|
78
|
+
# structure. Example, given `a/b/c.rb`, this looks for `a/b/.codeowner`, `a/.codeowner`,
|
79
|
+
# 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
|
81
|
+
sig { params(file: String).returns(T.nilable(CodeTeams::Team)) }
|
82
|
+
def map_file_to_relevant_owner(file)
|
83
|
+
file_path = Pathname.new(file)
|
84
|
+
path_components = file_path.each_filename.to_a.map { |path| Pathname.new(path) }
|
85
|
+
|
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
|
+
|
90
|
+
potential_codeowners_file_name = potential_codeowners_file.to_s
|
91
|
+
|
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)
|
97
|
+
|
98
|
+
@@directory_cache[potential_codeowners_file_name] = team
|
99
|
+
else
|
100
|
+
@@directory_cache[potential_codeowners_file_name] = nil
|
101
|
+
end
|
102
|
+
|
103
|
+
return team unless team.nil?
|
104
|
+
end
|
105
|
+
|
106
|
+
nil
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -13,6 +13,7 @@ require 'code_ownership/private/validations/github_codeowners_up_to_date'
|
|
13
13
|
require 'code_ownership/private/validations/files_have_unique_owners'
|
14
14
|
require 'code_ownership/private/ownership_mappers/file_annotations'
|
15
15
|
require 'code_ownership/private/ownership_mappers/team_globs'
|
16
|
+
require 'code_ownership/private/ownership_mappers/directory_ownership'
|
16
17
|
require 'code_ownership/private/ownership_mappers/package_ownership'
|
17
18
|
require 'code_ownership/private/ownership_mappers/js_package_ownership'
|
18
19
|
require 'code_ownership/private/ownership_mappers/team_yml_ownership'
|
@@ -62,7 +63,7 @@ module CodeOwnership
|
|
62
63
|
|
63
64
|
# Returns a string version of the relative path to a Rails constant,
|
64
65
|
# or nil if it can't find something
|
65
|
-
sig { params(klass: T.nilable(T.any(Class, Module))).returns(T.nilable(String)) }
|
66
|
+
sig { params(klass: T.nilable(T.any(T::Class[T.anything], Module))).returns(T.nilable(String)) }
|
66
67
|
def self.path_from_klass(klass)
|
67
68
|
if klass
|
68
69
|
path = Object.const_source_location(klass.to_s)&.first
|
data/lib/code_ownership.rb
CHANGED
@@ -6,7 +6,7 @@ require 'set'
|
|
6
6
|
require 'code_teams'
|
7
7
|
require 'sorbet-runtime'
|
8
8
|
require 'json'
|
9
|
-
require 'packs'
|
9
|
+
require 'packs-specification'
|
10
10
|
require 'code_ownership/mapper'
|
11
11
|
require 'code_ownership/validator'
|
12
12
|
require 'code_ownership/private'
|
@@ -156,7 +156,7 @@ module CodeOwnership
|
|
156
156
|
end
|
157
157
|
private_class_method(:backtrace_with_ownership)
|
158
158
|
|
159
|
-
sig { params(klass: T.nilable(T.any(Class, Module))).returns(T.nilable(::CodeTeams::Team)) }
|
159
|
+
sig { params(klass: T.nilable(T.any(T::Class[T.anything], Module))).returns(T.nilable(::CodeTeams::Team)) }
|
160
160
|
def for_class(klass)
|
161
161
|
@memoized_values ||= T.let(@memoized_values, T.nilable(T::Hash[String, T.nilable(::CodeTeams::Team)]))
|
162
162
|
@memoized_values ||= {}
|
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.
|
4
|
+
version: 1.34.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gusto Engineers
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-08-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: code_teams
|
@@ -25,7 +25,7 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '1.0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name: packs
|
28
|
+
name: packs-specification
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
@@ -168,6 +168,7 @@ files:
|
|
168
168
|
- lib/code_ownership/private/codeowners_file.rb
|
169
169
|
- lib/code_ownership/private/extension_loader.rb
|
170
170
|
- lib/code_ownership/private/glob_cache.rb
|
171
|
+
- lib/code_ownership/private/ownership_mappers/directory_ownership.rb
|
171
172
|
- lib/code_ownership/private/ownership_mappers/file_annotations.rb
|
172
173
|
- lib/code_ownership/private/ownership_mappers/js_package_ownership.rb
|
173
174
|
- lib/code_ownership/private/ownership_mappers/package_ownership.rb
|