code_ownership 1.31.1 → 1.32.1
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 +11 -0
- data/lib/code_ownership/configuration.rb +44 -0
- data/lib/code_ownership/mapper.rb +62 -0
- data/lib/code_ownership/private/extension_loader.rb +24 -0
- data/lib/code_ownership/private/ownership_mappers/file_annotations.rb +1 -1
- data/lib/code_ownership/private/ownership_mappers/js_package_ownership.rb +1 -1
- data/lib/code_ownership/private/ownership_mappers/package_ownership.rb +1 -1
- data/lib/code_ownership/private/ownership_mappers/team_globs.rb +19 -1
- data/lib/code_ownership/private/ownership_mappers/team_yml_ownership.rb +1 -1
- data/lib/code_ownership/private/validations/files_have_owners.rb +1 -1
- data/lib/code_ownership/private/validations/files_have_unique_owners.rb +1 -1
- data/lib/code_ownership/private/validations/github_codeowners_up_to_date.rb +2 -2
- data/lib/code_ownership/private.rb +14 -33
- data/lib/code_ownership/validator.rb +30 -0
- data/lib/code_ownership.rb +18 -5
- metadata +6 -6
- data/lib/code_ownership/private/configuration.rb +0 -37
- data/lib/code_ownership/private/ownership_mappers/interface.rb +0 -50
- data/lib/code_ownership/private/validations/interface.rb +0 -18
- data/lib/code_ownership/private/validations/no_overlapping_globs.rb +0 -30
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ebb581f1f5784bcac8ce0772d2350227c522f1d5096f2b9546a514b1d79fba1d
|
4
|
+
data.tar.gz: 4f2d525089ce8a67f1c0248bfa2bdb06db432dac677ae995e125c5997cdd97f1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2ccb639acea58cf8a882a06a1075146991781a8ee213251ec0dd8a5fdde19583df739a439246bd00548bf4b540e2904bd7c679f57ca470a3aecacfe1ee022e26
|
7
|
+
data.tar.gz: af0f3b0ee49dbdd0ce6f5a8b98aa6cf15cf290620d080ae1d791b273377b723ae5a31a4d48a667ece4072a24748dff2672d15cb38752ad8574b0e6bdd65fffd4
|
data/README.md
CHANGED
@@ -57,6 +57,17 @@ js_package_paths:
|
|
57
57
|
|
58
58
|
This defaults `**/`, which makes it look for `package.json` files across your application.
|
59
59
|
|
60
|
+
### Custom Ownership
|
61
|
+
To enable custom ownership, you can inject your own custom classes into `code_ownership`.
|
62
|
+
To do this, first create a class that adheres to the `CodeOwnership::Mapper` and/or `CodeOwnership::Validator` interface.
|
63
|
+
Then, in `config/code_ownership.yml`, you can require that file:
|
64
|
+
```yml
|
65
|
+
require:
|
66
|
+
- ./lib/my_extension.rb
|
67
|
+
```
|
68
|
+
|
69
|
+
Now, `bin/codeownership validate` will automatically include your new mapper and/or validator. See [`spec/lib/code_ownership/private/extension_loader_spec.rb](spec/lib/code_ownership/private/extension_loader_spec.rb) for an example of what this looks like.
|
70
|
+
|
60
71
|
## Usage: Reading CodeOwnership
|
61
72
|
### `for_file`
|
62
73
|
`CodeOwnership.for_file`, given a relative path to a file returns a `CodeTeams::Team` if there is a team that owns the file, `nil` otherwise.
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# typed: strict
|
2
|
+
|
3
|
+
module CodeOwnership
|
4
|
+
class Configuration < T::Struct
|
5
|
+
extend T::Sig
|
6
|
+
DEFAULT_JS_PACKAGE_PATHS = T.let(['**/'], T::Array[String])
|
7
|
+
|
8
|
+
const :owned_globs, T::Array[String]
|
9
|
+
const :unowned_globs, T::Array[String]
|
10
|
+
const :js_package_paths, T::Array[String]
|
11
|
+
const :unbuilt_gems_path, T.nilable(String)
|
12
|
+
const :skip_codeowners_validation, T::Boolean
|
13
|
+
const :raw_hash, T::Hash[T.untyped, T.untyped]
|
14
|
+
|
15
|
+
sig { returns(Configuration) }
|
16
|
+
def self.fetch
|
17
|
+
config_hash = YAML.load_file('config/code_ownership.yml')
|
18
|
+
|
19
|
+
if config_hash.key?("require")
|
20
|
+
config_hash["require"].each do |require_directive|
|
21
|
+
Private::ExtensionLoader.load(require_directive)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
new(
|
26
|
+
owned_globs: config_hash.fetch('owned_globs', []),
|
27
|
+
unowned_globs: config_hash.fetch('unowned_globs', []),
|
28
|
+
js_package_paths: js_package_paths(config_hash),
|
29
|
+
skip_codeowners_validation: config_hash.fetch('skip_codeowners_validation', false),
|
30
|
+
raw_hash: config_hash
|
31
|
+
)
|
32
|
+
end
|
33
|
+
|
34
|
+
sig { params(config_hash: T::Hash[T.untyped, T.untyped]).returns(T::Array[String]) }
|
35
|
+
def self.js_package_paths(config_hash)
|
36
|
+
specified_package_paths = config_hash['js_package_paths']
|
37
|
+
if specified_package_paths.nil?
|
38
|
+
DEFAULT_JS_PACKAGE_PATHS.dup
|
39
|
+
else
|
40
|
+
Array(specified_package_paths)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# typed: strict
|
4
|
+
|
5
|
+
module CodeOwnership
|
6
|
+
module Mapper
|
7
|
+
extend T::Sig
|
8
|
+
extend T::Helpers
|
9
|
+
|
10
|
+
interface!
|
11
|
+
|
12
|
+
class << self
|
13
|
+
extend T::Sig
|
14
|
+
|
15
|
+
sig { params(base: Class).void }
|
16
|
+
def included(base)
|
17
|
+
@mappers ||= T.let(@mappers, T.nilable(T::Array[Class]))
|
18
|
+
@mappers ||= []
|
19
|
+
@mappers << base
|
20
|
+
end
|
21
|
+
|
22
|
+
sig { returns(T::Array[Mapper]) }
|
23
|
+
def all
|
24
|
+
T.unsafe(@mappers).map(&:new)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
#
|
29
|
+
# This should be fast when run with ONE file
|
30
|
+
#
|
31
|
+
sig do
|
32
|
+
abstract.params(file: String).
|
33
|
+
returns(T.nilable(::CodeTeams::Team))
|
34
|
+
end
|
35
|
+
def map_file_to_owner(file)
|
36
|
+
end
|
37
|
+
|
38
|
+
#
|
39
|
+
# This should be fast when run with MANY files
|
40
|
+
#
|
41
|
+
sig do
|
42
|
+
abstract.params(files: T::Array[String]).
|
43
|
+
returns(T::Hash[String, T.nilable(::CodeTeams::Team)])
|
44
|
+
end
|
45
|
+
def map_files_to_owners(files)
|
46
|
+
end
|
47
|
+
|
48
|
+
sig do
|
49
|
+
abstract.returns(T::Hash[String, T.nilable(::CodeTeams::Team)])
|
50
|
+
end
|
51
|
+
def codeowners_lines_to_owners
|
52
|
+
end
|
53
|
+
|
54
|
+
sig { abstract.returns(String) }
|
55
|
+
def description
|
56
|
+
end
|
57
|
+
|
58
|
+
sig { abstract.void }
|
59
|
+
def bust_caches!
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module CodeOwnership
|
5
|
+
module Private
|
6
|
+
# This class handles loading extensions to code_ownership using the `require` directive
|
7
|
+
# in the `code_ownership.yml` configuration.
|
8
|
+
module ExtensionLoader
|
9
|
+
class << self
|
10
|
+
extend T::Sig
|
11
|
+
sig { params(require_directive: String).void }
|
12
|
+
def load(require_directive)
|
13
|
+
# We want to transform the require directive to behave differently
|
14
|
+
# if it's a specific local file being required versus a gem
|
15
|
+
if require_directive.start_with?(".")
|
16
|
+
require File.join(Pathname.pwd, require_directive)
|
17
|
+
else
|
18
|
+
require require_directive
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -7,7 +7,8 @@ module CodeOwnership
|
|
7
7
|
module OwnershipMappers
|
8
8
|
class TeamGlobs
|
9
9
|
extend T::Sig
|
10
|
-
include
|
10
|
+
include Mapper
|
11
|
+
include Validator
|
11
12
|
|
12
13
|
@@map_files_to_owners = T.let(@map_files_to_owners, T.nilable(T::Hash[String, T.nilable(::CodeTeams::Team)])) # rubocop:disable Style/ClassVars
|
13
14
|
@@map_files_to_owners = {} # rubocop:disable Style/ClassVars
|
@@ -114,6 +115,23 @@ module CodeOwnership
|
|
114
115
|
def description
|
115
116
|
'Team-specific owned globs'
|
116
117
|
end
|
118
|
+
|
119
|
+
sig { override.params(files: T::Array[String], autocorrect: T::Boolean, stage_changes: T::Boolean).returns(T::Array[String]) }
|
120
|
+
def validation_errors(files:, autocorrect: true, stage_changes: true)
|
121
|
+
overlapping_globs = OwnershipMappers::TeamGlobs.new.find_overlapping_globs
|
122
|
+
|
123
|
+
errors = T.let([], T::Array[String])
|
124
|
+
|
125
|
+
if overlapping_globs.any?
|
126
|
+
errors << <<~MSG
|
127
|
+
`owned_globs` cannot overlap between teams. The following globs overlap:
|
128
|
+
|
129
|
+
#{overlapping_globs.map { |overlap| "- #{overlap.description}"}.join("\n")}
|
130
|
+
MSG
|
131
|
+
end
|
132
|
+
|
133
|
+
errors
|
134
|
+
end
|
117
135
|
end
|
118
136
|
end
|
119
137
|
end
|
@@ -7,7 +7,7 @@ module CodeOwnership
|
|
7
7
|
module OwnershipMappers
|
8
8
|
class TeamYmlOwnership
|
9
9
|
extend T::Sig
|
10
|
-
include
|
10
|
+
include Mapper
|
11
11
|
|
12
12
|
@@map_files_to_owners = T.let(@map_files_to_owners, T.nilable(T::Hash[String, T.nilable(::CodeTeams::Team)])) # rubocop:disable Style/ClassVars
|
13
13
|
@@map_files_to_owners = {} # rubocop:disable Style/ClassVars
|
@@ -6,7 +6,7 @@ module CodeOwnership
|
|
6
6
|
class FilesHaveOwners
|
7
7
|
extend T::Sig
|
8
8
|
extend T::Helpers
|
9
|
-
include
|
9
|
+
include Validator
|
10
10
|
|
11
11
|
sig { override.params(files: T::Array[String], autocorrect: T::Boolean, stage_changes: T::Boolean).returns(T::Array[String]) }
|
12
12
|
def validation_errors(files:, autocorrect: true, stage_changes: true)
|
@@ -6,7 +6,7 @@ module CodeOwnership
|
|
6
6
|
class FilesHaveUniqueOwners
|
7
7
|
extend T::Sig
|
8
8
|
extend T::Helpers
|
9
|
-
include
|
9
|
+
include Validator
|
10
10
|
|
11
11
|
sig { override.params(files: T::Array[String], autocorrect: T::Boolean, stage_changes: T::Boolean).returns(T::Array[String]) }
|
12
12
|
def validation_errors(files:, autocorrect: true, stage_changes: true)
|
@@ -6,7 +6,7 @@ module CodeOwnership
|
|
6
6
|
class GithubCodeownersUpToDate
|
7
7
|
extend T::Sig
|
8
8
|
extend T::Helpers
|
9
|
-
include
|
9
|
+
include Validator
|
10
10
|
|
11
11
|
sig { override.params(files: T::Array[String], autocorrect: T::Boolean, stage_changes: T::Boolean).returns(T::Array[String]) }
|
12
12
|
def validation_errors(files:, autocorrect: true, stage_changes: true)
|
@@ -105,7 +105,7 @@ module CodeOwnership
|
|
105
105
|
map[team.name] = team_github.team
|
106
106
|
end
|
107
107
|
|
108
|
-
|
108
|
+
Mapper.all.flat_map do |mapper|
|
109
109
|
codeowners_lines = mapper.codeowners_lines_to_owners.filter_map do |line, team|
|
110
110
|
team_mapping = github_team_map[team&.name]
|
111
111
|
next unless team_mapping
|
@@ -2,16 +2,13 @@
|
|
2
2
|
|
3
3
|
# typed: strict
|
4
4
|
|
5
|
-
require 'code_ownership/private/
|
5
|
+
require 'code_ownership/private/extension_loader'
|
6
6
|
require 'code_ownership/private/team_plugins/ownership'
|
7
7
|
require 'code_ownership/private/team_plugins/github'
|
8
8
|
require 'code_ownership/private/parse_js_packages'
|
9
|
-
require 'code_ownership/private/validations/interface'
|
10
9
|
require 'code_ownership/private/validations/files_have_owners'
|
11
10
|
require 'code_ownership/private/validations/github_codeowners_up_to_date'
|
12
11
|
require 'code_ownership/private/validations/files_have_unique_owners'
|
13
|
-
require 'code_ownership/private/validations/no_overlapping_globs'
|
14
|
-
require 'code_ownership/private/ownership_mappers/interface'
|
15
12
|
require 'code_ownership/private/ownership_mappers/file_annotations'
|
16
13
|
require 'code_ownership/private/ownership_mappers/team_globs'
|
17
14
|
require 'code_ownership/private/ownership_mappers/package_ownership'
|
@@ -22,10 +19,18 @@ module CodeOwnership
|
|
22
19
|
module Private
|
23
20
|
extend T::Sig
|
24
21
|
|
25
|
-
sig { returns(
|
22
|
+
sig { returns(Configuration) }
|
26
23
|
def self.configuration
|
27
|
-
@configuration ||= T.let(@configuration, T.nilable(
|
28
|
-
@configuration ||=
|
24
|
+
@configuration ||= T.let(@configuration, T.nilable(Configuration))
|
25
|
+
@configuration ||= Configuration.fetch
|
26
|
+
end
|
27
|
+
|
28
|
+
# This is just an alias for `configuration` that makes it more explicit what we're doing instead of just calling `configuration`.
|
29
|
+
# This is necessary because configuration may contain extensions of code ownership, so those extensions should be loaded prior to
|
30
|
+
# calling APIs that provide ownership information.
|
31
|
+
sig { returns(Configuration) }
|
32
|
+
def self.load_configuration!
|
33
|
+
configuration
|
29
34
|
end
|
30
35
|
|
31
36
|
sig { void }
|
@@ -37,14 +42,7 @@ module CodeOwnership
|
|
37
42
|
|
38
43
|
sig { params(files: T::Array[String], autocorrect: T::Boolean, stage_changes: T::Boolean).void }
|
39
44
|
def self.validate!(files:, autocorrect: true, stage_changes: true)
|
40
|
-
|
41
|
-
Validations::FilesHaveOwners.new,
|
42
|
-
Validations::FilesHaveUniqueOwners.new,
|
43
|
-
Validations::GithubCodeownersUpToDate.new,
|
44
|
-
Validations::NoOverlappingGlobs.new,
|
45
|
-
]
|
46
|
-
|
47
|
-
errors = validators.flat_map do |validator|
|
45
|
+
errors = Validator.all.flat_map do |validator|
|
48
46
|
validator.validation_errors(
|
49
47
|
files: files,
|
50
48
|
autocorrect: autocorrect,
|
@@ -58,23 +56,6 @@ module CodeOwnership
|
|
58
56
|
end
|
59
57
|
end
|
60
58
|
|
61
|
-
sig { returns(T::Array[Private::OwnershipMappers::Interface]) }
|
62
|
-
def self.mappers
|
63
|
-
[
|
64
|
-
file_annotations_mapper,
|
65
|
-
Private::OwnershipMappers::TeamGlobs.new,
|
66
|
-
Private::OwnershipMappers::PackageOwnership.new,
|
67
|
-
Private::OwnershipMappers::JsPackageOwnership.new,
|
68
|
-
Private::OwnershipMappers::TeamYmlOwnership.new,
|
69
|
-
]
|
70
|
-
end
|
71
|
-
|
72
|
-
sig { returns(Private::OwnershipMappers::FileAnnotations) }
|
73
|
-
def self.file_annotations_mapper
|
74
|
-
@file_annotations_mapper = T.let(@file_annotations_mapper, T.nilable(Private::OwnershipMappers::FileAnnotations))
|
75
|
-
@file_annotations_mapper ||= Private::OwnershipMappers::FileAnnotations.new
|
76
|
-
end
|
77
|
-
|
78
59
|
# Returns a string version of the relative path to a Rails constant,
|
79
60
|
# or nil if it can't find something
|
80
61
|
sig { params(klass: T.nilable(T.any(Class, Module))).returns(T.nilable(String)) }
|
@@ -112,7 +93,7 @@ module CodeOwnership
|
|
112
93
|
@files_by_mapper ||= begin
|
113
94
|
files_by_mapper = files.map { |file| [file, []] }.to_h
|
114
95
|
|
115
|
-
|
96
|
+
Mapper.all.each do |mapper|
|
116
97
|
mapper.map_files_to_owners(files).each do |file, _team|
|
117
98
|
files_by_mapper[file] ||= []
|
118
99
|
T.must(files_by_mapper[file]) << mapper.description
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# typed: strict
|
2
|
+
|
3
|
+
module CodeOwnership
|
4
|
+
module Validator
|
5
|
+
extend T::Sig
|
6
|
+
extend T::Helpers
|
7
|
+
|
8
|
+
interface!
|
9
|
+
|
10
|
+
sig { abstract.params(files: T::Array[String], autocorrect: T::Boolean, stage_changes: T::Boolean).returns(T::Array[String]) }
|
11
|
+
def validation_errors(files:, autocorrect: true, stage_changes: true)
|
12
|
+
end
|
13
|
+
|
14
|
+
class << self
|
15
|
+
extend T::Sig
|
16
|
+
|
17
|
+
sig { params(base: Class).void }
|
18
|
+
def included(base)
|
19
|
+
@validators ||= T.let(@validators, T.nilable(T::Array[Class]))
|
20
|
+
@validators ||= []
|
21
|
+
@validators << base
|
22
|
+
end
|
23
|
+
|
24
|
+
sig { returns(T::Array[Validator]) }
|
25
|
+
def all
|
26
|
+
T.unsafe(@validators).map(&:new)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/code_ownership.rb
CHANGED
@@ -7,8 +7,11 @@ require 'code_teams'
|
|
7
7
|
require 'sorbet-runtime'
|
8
8
|
require 'json'
|
9
9
|
require 'packs'
|
10
|
-
require 'code_ownership/
|
10
|
+
require 'code_ownership/mapper'
|
11
|
+
require 'code_ownership/validator'
|
11
12
|
require 'code_ownership/private'
|
13
|
+
require 'code_ownership/cli'
|
14
|
+
require 'code_ownership/configuration'
|
12
15
|
|
13
16
|
module CodeOwnership
|
14
17
|
extend self
|
@@ -25,9 +28,11 @@ module CodeOwnership
|
|
25
28
|
return nil if file.start_with?('./')
|
26
29
|
return @for_file[file] if @for_file.key?(file)
|
27
30
|
|
31
|
+
Private.load_configuration!
|
32
|
+
|
28
33
|
owner = T.let(nil, T.nilable(CodeTeams::Team))
|
29
34
|
|
30
|
-
|
35
|
+
Mapper.all.each do |mapper|
|
31
36
|
owner = mapper.map_file_to_owner(file)
|
32
37
|
break if owner
|
33
38
|
end
|
@@ -37,11 +42,13 @@ module CodeOwnership
|
|
37
42
|
|
38
43
|
sig { params(team: T.any(CodeTeams::Team, String)).returns(String) }
|
39
44
|
def for_team(team)
|
45
|
+
Private.load_configuration!
|
46
|
+
|
40
47
|
team = T.must(CodeTeams.find(team)) if team.is_a?(String)
|
41
48
|
ownership_information = T.let([], T::Array[String])
|
42
49
|
|
43
50
|
ownership_information << "# Code Ownership Report for `#{team.name}` Team"
|
44
|
-
|
51
|
+
Mapper.all.each do |mapper|
|
45
52
|
ownership_information << "## #{mapper.description}"
|
46
53
|
codeowners_lines = mapper.codeowners_lines_to_owners
|
47
54
|
ownership_for_mapper = []
|
@@ -69,7 +76,7 @@ module CodeOwnership
|
|
69
76
|
|
70
77
|
sig { params(filename: String).void }
|
71
78
|
def self.remove_file_annotation!(filename)
|
72
|
-
Private.
|
79
|
+
Private::OwnershipMappers::FileAnnotations.new.remove_file_annotation!(filename)
|
73
80
|
end
|
74
81
|
|
75
82
|
sig do
|
@@ -84,6 +91,7 @@ module CodeOwnership
|
|
84
91
|
autocorrect: true,
|
85
92
|
stage_changes: true
|
86
93
|
)
|
94
|
+
Private.load_configuration!
|
87
95
|
tracked_file_subset = Private.tracked_files & files
|
88
96
|
Private.validate!(files: tracked_file_subset, autocorrect: autocorrect, stage_changes: stage_changes)
|
89
97
|
end
|
@@ -172,6 +180,11 @@ module CodeOwnership
|
|
172
180
|
@for_file = nil
|
173
181
|
@memoized_values = nil
|
174
182
|
Private.bust_caches!
|
175
|
-
|
183
|
+
Mapper.all.each(&:bust_caches!)
|
184
|
+
end
|
185
|
+
|
186
|
+
sig { returns(Configuration) }
|
187
|
+
def self.configuration
|
188
|
+
Private.configuration
|
176
189
|
end
|
177
190
|
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: 1.
|
4
|
+
version: 1.32.1
|
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-03-
|
11
|
+
date: 2023-03-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: code_teams
|
@@ -134,10 +134,11 @@ files:
|
|
134
134
|
- bin/codeownership
|
135
135
|
- lib/code_ownership.rb
|
136
136
|
- lib/code_ownership/cli.rb
|
137
|
+
- lib/code_ownership/configuration.rb
|
138
|
+
- lib/code_ownership/mapper.rb
|
137
139
|
- lib/code_ownership/private.rb
|
138
|
-
- lib/code_ownership/private/
|
140
|
+
- lib/code_ownership/private/extension_loader.rb
|
139
141
|
- lib/code_ownership/private/ownership_mappers/file_annotations.rb
|
140
|
-
- lib/code_ownership/private/ownership_mappers/interface.rb
|
141
142
|
- lib/code_ownership/private/ownership_mappers/js_package_ownership.rb
|
142
143
|
- lib/code_ownership/private/ownership_mappers/package_ownership.rb
|
143
144
|
- lib/code_ownership/private/ownership_mappers/team_globs.rb
|
@@ -148,8 +149,7 @@ files:
|
|
148
149
|
- lib/code_ownership/private/validations/files_have_owners.rb
|
149
150
|
- lib/code_ownership/private/validations/files_have_unique_owners.rb
|
150
151
|
- lib/code_ownership/private/validations/github_codeowners_up_to_date.rb
|
151
|
-
- lib/code_ownership/
|
152
|
-
- lib/code_ownership/private/validations/no_overlapping_globs.rb
|
152
|
+
- lib/code_ownership/validator.rb
|
153
153
|
homepage: https://github.com/rubyatscale/code_ownership
|
154
154
|
licenses:
|
155
155
|
- MIT
|
@@ -1,37 +0,0 @@
|
|
1
|
-
# typed: strict
|
2
|
-
|
3
|
-
module CodeOwnership
|
4
|
-
module Private
|
5
|
-
class Configuration < T::Struct
|
6
|
-
extend T::Sig
|
7
|
-
DEFAULT_JS_PACKAGE_PATHS = T.let(['**/'], T::Array[String])
|
8
|
-
|
9
|
-
const :owned_globs, T::Array[String]
|
10
|
-
const :unowned_globs, T::Array[String]
|
11
|
-
const :js_package_paths, T::Array[String]
|
12
|
-
const :skip_codeowners_validation, T::Boolean
|
13
|
-
|
14
|
-
sig { returns(Configuration) }
|
15
|
-
def self.fetch
|
16
|
-
config_hash = YAML.load_file('config/code_ownership.yml')
|
17
|
-
|
18
|
-
new(
|
19
|
-
owned_globs: config_hash.fetch('owned_globs', []),
|
20
|
-
unowned_globs: config_hash.fetch('unowned_globs', []),
|
21
|
-
js_package_paths: js_package_paths(config_hash),
|
22
|
-
skip_codeowners_validation: config_hash.fetch('skip_codeowners_validation', false)
|
23
|
-
)
|
24
|
-
end
|
25
|
-
|
26
|
-
sig { params(config_hash: T::Hash[T.untyped, T.untyped]).returns(T::Array[String]) }
|
27
|
-
def self.js_package_paths(config_hash)
|
28
|
-
specified_package_paths = config_hash['js_package_paths']
|
29
|
-
if specified_package_paths.nil?
|
30
|
-
DEFAULT_JS_PACKAGE_PATHS.dup
|
31
|
-
else
|
32
|
-
Array(specified_package_paths)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
@@ -1,50 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# typed: strict
|
4
|
-
|
5
|
-
module CodeOwnership
|
6
|
-
module Private
|
7
|
-
module OwnershipMappers
|
8
|
-
module Interface
|
9
|
-
extend T::Sig
|
10
|
-
extend T::Helpers
|
11
|
-
|
12
|
-
interface!
|
13
|
-
|
14
|
-
#
|
15
|
-
# This should be fast when run with ONE file
|
16
|
-
#
|
17
|
-
sig do
|
18
|
-
abstract.params(file: String).
|
19
|
-
returns(T.nilable(::CodeTeams::Team))
|
20
|
-
end
|
21
|
-
def map_file_to_owner(file)
|
22
|
-
end
|
23
|
-
|
24
|
-
#
|
25
|
-
# This should be fast when run with MANY files
|
26
|
-
#
|
27
|
-
sig do
|
28
|
-
abstract.params(files: T::Array[String]).
|
29
|
-
returns(T::Hash[String, T.nilable(::CodeTeams::Team)])
|
30
|
-
end
|
31
|
-
def map_files_to_owners(files)
|
32
|
-
end
|
33
|
-
|
34
|
-
sig do
|
35
|
-
abstract.returns(T::Hash[String, T.nilable(::CodeTeams::Team)])
|
36
|
-
end
|
37
|
-
def codeowners_lines_to_owners
|
38
|
-
end
|
39
|
-
|
40
|
-
sig { abstract.returns(String) }
|
41
|
-
def description
|
42
|
-
end
|
43
|
-
|
44
|
-
sig { abstract.void }
|
45
|
-
def bust_caches!
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
@@ -1,18 +0,0 @@
|
|
1
|
-
# typed: strict
|
2
|
-
|
3
|
-
module CodeOwnership
|
4
|
-
module Private
|
5
|
-
module Validations
|
6
|
-
module Interface
|
7
|
-
extend T::Sig
|
8
|
-
extend T::Helpers
|
9
|
-
|
10
|
-
interface!
|
11
|
-
|
12
|
-
sig { abstract.params(files: T::Array[String], autocorrect: T::Boolean, stage_changes: T::Boolean).returns(T::Array[String]) }
|
13
|
-
def validation_errors(files:, autocorrect: true, stage_changes: true)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
@@ -1,30 +0,0 @@
|
|
1
|
-
# typed: strict
|
2
|
-
|
3
|
-
module CodeOwnership
|
4
|
-
module Private
|
5
|
-
module Validations
|
6
|
-
class NoOverlappingGlobs
|
7
|
-
extend T::Sig
|
8
|
-
extend T::Helpers
|
9
|
-
include Interface
|
10
|
-
|
11
|
-
sig { override.params(files: T::Array[String], autocorrect: T::Boolean, stage_changes: T::Boolean).returns(T::Array[String]) }
|
12
|
-
def validation_errors(files:, autocorrect: true, stage_changes: true)
|
13
|
-
overlapping_globs = OwnershipMappers::TeamGlobs.new.find_overlapping_globs
|
14
|
-
|
15
|
-
errors = T.let([], T::Array[String])
|
16
|
-
|
17
|
-
if overlapping_globs.any?
|
18
|
-
errors << <<~MSG
|
19
|
-
`owned_globs` cannot overlap between teams. The following globs overlap:
|
20
|
-
|
21
|
-
#{overlapping_globs.map { |overlap| "- #{overlap.description}"}.join("\n")}
|
22
|
-
MSG
|
23
|
-
end
|
24
|
-
|
25
|
-
errors
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|