code_teams 1.0.2 → 1.1.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 +4 -4
- data/README.md +38 -15
- data/lib/code_teams/plugin.rb +16 -3
- data/lib/code_teams/utils.rb +17 -0
- data/lib/code_teams.rb +22 -4
- metadata +5 -63
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 755f17a56df0e3c1f337e0497b4b63887b0681626583bdf3a6f66ec4eb9ce46f
|
4
|
+
data.tar.gz: e060b12412a3a5c0ff02af18490a3ef336b8b83d5236ef5eed0f4a75c12ced2b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 92594da3e4eccae671cd71d1564daf10009d41bd30612a0d5c1b66e3a729466d2c03ccea4c5cad5cf2196bc85be33a06520ec4767e94bb75219f070c8088449f
|
7
|
+
data.tar.gz: 9acbb9c011703218fe398179e723c796452b4564a06817124fd7cd599ea9e93a6d7f01987e0f03f274ddd100074d5d8260a18a85d51da69ae15a458bd992e29b
|
data/README.md
CHANGED
@@ -30,10 +30,7 @@ class MyGithubPlugin < CodeTeams::Plugin
|
|
30
30
|
end
|
31
31
|
|
32
32
|
def member?(user)
|
33
|
-
|
34
|
-
return false unless members
|
35
|
-
|
36
|
-
members.include?(user)
|
33
|
+
github.members.include?(user)
|
37
34
|
end
|
38
35
|
|
39
36
|
sig { override.params(teams: T::Array[CodeTeams::Team]).returns(T::Array[String]) }
|
@@ -62,24 +59,50 @@ github:
|
|
62
59
|
```
|
63
60
|
|
64
61
|
1) You can now use the following API to get GitHub information about that team:
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
62
|
+
|
63
|
+
```ruby
|
64
|
+
team = CodeTeams.find('My Team')
|
65
|
+
members = team.github.members
|
66
|
+
github_name = team.github.team
|
67
|
+
```
|
68
|
+
|
69
|
+
Alternatively, you can assign an accessor method name that differs from the plugin's class name:
|
70
|
+
|
71
|
+
```ruby
|
72
|
+
class MyPlugin < CodeTeams::Plugin
|
73
|
+
data_accessor_name :other_name
|
74
|
+
|
75
|
+
def other_name
|
76
|
+
# ...
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# You can then use:
|
81
|
+
team.other_name
|
82
|
+
# similarly to the Github example above
|
83
|
+
# You can then access data in the following manner:
|
84
|
+
team.other_name.attribute_name
|
85
|
+
```
|
86
|
+
|
87
|
+
However, to avoid confusion, it's recommended to use the naming convention
|
88
|
+
whenever possible so that your accessor name matches your plugin's name
|
89
|
+
|
69
90
|
2) Running team validations (see below) will ensure all teams have a GitHub team specified
|
70
91
|
|
71
|
-
Your plugins can be as simple or as complex as you want. Here are some other things we use plugins for:
|
72
|
-
|
73
|
-
-
|
74
|
-
-
|
75
|
-
-
|
76
|
-
- Specifying
|
92
|
+
Your plugins can be as simple or as complex as you want. Here are some other things we use plugins for:
|
93
|
+
|
94
|
+
- Identifying which teams own which feature flags
|
95
|
+
- Mapping teams to specific portions of the code through `code_ownership`
|
96
|
+
- Allowing teams to protect certain files and require approval on modification of certain files
|
97
|
+
- Specifying owned dependencies (Ruby gems, JavaScript packages, and more)
|
98
|
+
- Specifying how to get in touch with the team via Slack (their channel and handle)
|
77
99
|
|
78
100
|
## Configuration
|
79
101
|
You'll want to ensure that all teams are valid in your CI environment. We recommend running code like this in CI:
|
80
102
|
```ruby
|
81
103
|
require 'code_teams'
|
82
|
-
|
104
|
+
|
105
|
+
errors = CodeTeams.validation_errors(CodeTeams.all)
|
83
106
|
if errors.any?
|
84
107
|
abort <<~ERROR
|
85
108
|
Team validation failed with the following errors:
|
data/lib/code_teams/plugin.rb
CHANGED
@@ -10,11 +10,24 @@ module CodeTeams
|
|
10
10
|
|
11
11
|
abstract!
|
12
12
|
|
13
|
+
@data_accessor_name = T.let(nil, T.nilable(String))
|
14
|
+
|
13
15
|
sig { params(team: Team).void }
|
14
16
|
def initialize(team)
|
15
17
|
@team = team
|
16
18
|
end
|
17
19
|
|
20
|
+
sig { params(key: String).returns(String) }
|
21
|
+
def self.data_accessor_name(key = default_data_accessor_name)
|
22
|
+
@data_accessor_name ||= key
|
23
|
+
end
|
24
|
+
|
25
|
+
sig { returns(String) }
|
26
|
+
def self.default_data_accessor_name
|
27
|
+
# e.g., MyNamespace::MyPlugin -> my_plugin
|
28
|
+
Utils.underscore(Utils.demodulize(name))
|
29
|
+
end
|
30
|
+
|
18
31
|
sig { params(base: T.untyped).void }
|
19
32
|
def self.inherited(base) # rubocop:disable Lint/MissingSuper
|
20
33
|
all_plugins << T.cast(base, T.class_of(Plugin))
|
@@ -28,7 +41,7 @@ module CodeTeams
|
|
28
41
|
end
|
29
42
|
|
30
43
|
sig { params(teams: T::Array[Team]).returns(T::Array[String]) }
|
31
|
-
def self.validation_errors(teams)
|
44
|
+
def self.validation_errors(teams)
|
32
45
|
[]
|
33
46
|
end
|
34
47
|
|
@@ -42,9 +55,9 @@ module CodeTeams
|
|
42
55
|
"#{team.name} is missing required key `#{key}`"
|
43
56
|
end
|
44
57
|
|
45
|
-
sig { returns(T::Hash[T.nilable(String), T::Hash[
|
58
|
+
sig { returns(T::Hash[T.nilable(String), T::Hash[T.class_of(Plugin), Plugin]]) }
|
46
59
|
def self.registry
|
47
|
-
@registry ||= T.let(@registry, T.nilable(T::Hash[String, T::Hash[
|
60
|
+
@registry ||= T.let(@registry, T.nilable(T::Hash[String, T::Hash[T.class_of(Plugin), Plugin]]))
|
48
61
|
@registry ||= {}
|
49
62
|
@registry
|
50
63
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module CodeTeams
|
2
|
+
module Utils
|
3
|
+
module_function
|
4
|
+
|
5
|
+
def underscore(string)
|
6
|
+
string.gsub('::', '/')
|
7
|
+
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
8
|
+
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
9
|
+
.tr('-', '_')
|
10
|
+
.downcase
|
11
|
+
end
|
12
|
+
|
13
|
+
def demodulize(string)
|
14
|
+
string.split('::').last
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/code_teams.rb
CHANGED
@@ -3,11 +3,11 @@
|
|
3
3
|
# typed: strict
|
4
4
|
|
5
5
|
require 'yaml'
|
6
|
-
require 'set'
|
7
6
|
require 'pathname'
|
8
7
|
require 'sorbet-runtime'
|
9
8
|
require 'code_teams/plugin'
|
10
9
|
require 'code_teams/plugins/identity'
|
10
|
+
require 'code_teams/utils'
|
11
11
|
|
12
12
|
module CodeTeams
|
13
13
|
extend T::Sig
|
@@ -15,6 +15,7 @@ module CodeTeams
|
|
15
15
|
class IncorrectPublicApiUsageError < StandardError; end
|
16
16
|
|
17
17
|
UNKNOWN_TEAM_STRING = 'Unknown Team'
|
18
|
+
@plugins_registered = T.let(false, T::Boolean)
|
18
19
|
|
19
20
|
sig { returns(T::Array[Team]) }
|
20
21
|
def self.all
|
@@ -36,6 +37,11 @@ module CodeTeams
|
|
36
37
|
|
37
38
|
sig { params(dir: String).returns(T::Array[Team]) }
|
38
39
|
def self.for_directory(dir)
|
40
|
+
unless @plugins_registered
|
41
|
+
Team.register_plugins
|
42
|
+
@plugins_registered = true
|
43
|
+
end
|
44
|
+
|
39
45
|
Pathname.new(dir).glob('**/*.yml').map do |path|
|
40
46
|
Team.from_yml(path.to_s)
|
41
47
|
rescue Psych::SyntaxError
|
@@ -60,6 +66,7 @@ module CodeTeams
|
|
60
66
|
# The primary reason this is helpful is for clients of CodeTeams who want to test their code, and each test context has different set of teams
|
61
67
|
sig { void }
|
62
68
|
def self.bust_caches!
|
69
|
+
@plugins_registered = false
|
63
70
|
Plugin.bust_caches!
|
64
71
|
@all = nil
|
65
72
|
@index_by_name = nil
|
@@ -86,6 +93,17 @@ module CodeTeams
|
|
86
93
|
)
|
87
94
|
end
|
88
95
|
|
96
|
+
sig { void }
|
97
|
+
def self.register_plugins
|
98
|
+
Plugin.all_plugins.each do |plugin|
|
99
|
+
# e.g., def github (on Team)
|
100
|
+
define_method(plugin.data_accessor_name) do
|
101
|
+
# e.g., MyGithubPlugin.for(team).github
|
102
|
+
plugin.for(T.cast(self, Team)).public_send(plugin.data_accessor_name)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
89
107
|
sig { returns(T::Hash[T.untyped, T.untyped]) }
|
90
108
|
attr_reader :raw_hash
|
91
109
|
|
@@ -116,16 +134,16 @@ module CodeTeams
|
|
116
134
|
sig { params(other: Object).returns(T::Boolean) }
|
117
135
|
def ==(other)
|
118
136
|
if other.is_a?(CodeTeams::Team)
|
119
|
-
|
137
|
+
name == other.name
|
120
138
|
else
|
121
139
|
false
|
122
140
|
end
|
123
141
|
end
|
124
142
|
|
125
|
-
|
143
|
+
alias eql? ==
|
126
144
|
|
127
145
|
sig { returns(Integer) }
|
128
|
-
def hash
|
146
|
+
def hash
|
129
147
|
name.hash
|
130
148
|
end
|
131
149
|
end
|
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: code_teams
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gusto Engineers
|
8
|
-
autorequire:
|
9
8
|
bindir: bin
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: sorbet-runtime
|
@@ -24,62 +23,6 @@ dependencies:
|
|
24
23
|
- - ">="
|
25
24
|
- !ruby/object:Gem::Version
|
26
25
|
version: '0'
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: pry
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - ">="
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '0'
|
34
|
-
type: :development
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - ">="
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '0'
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: rake
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - ">="
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '0'
|
48
|
-
type: :development
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - ">="
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '0'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: rspec
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - "~>"
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '3.0'
|
62
|
-
type: :development
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - "~>"
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: '3.0'
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: sorbet
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - ">="
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: '0'
|
76
|
-
type: :development
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - ">="
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: '0'
|
83
26
|
description: A low-dependency gem for declaring and querying engineering teams
|
84
27
|
email:
|
85
28
|
- dev@gusto.com
|
@@ -91,6 +34,7 @@ files:
|
|
91
34
|
- lib/code_teams.rb
|
92
35
|
- lib/code_teams/plugin.rb
|
93
36
|
- lib/code_teams/plugins/identity.rb
|
37
|
+
- lib/code_teams/utils.rb
|
94
38
|
homepage: https://github.com/rubyatscale/code_teams
|
95
39
|
licenses:
|
96
40
|
- MIT
|
@@ -99,7 +43,6 @@ metadata:
|
|
99
43
|
source_code_uri: https://github.com/rubyatscale/code_teams
|
100
44
|
changelog_uri: https://github.com/rubyatscale/code_teams/releases
|
101
45
|
allowed_push_host: https://rubygems.org
|
102
|
-
post_install_message:
|
103
46
|
rdoc_options: []
|
104
47
|
require_paths:
|
105
48
|
- lib
|
@@ -107,15 +50,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
107
50
|
requirements:
|
108
51
|
- - ">="
|
109
52
|
- !ruby/object:Gem::Version
|
110
|
-
version: '2
|
53
|
+
version: '3.2'
|
111
54
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
112
55
|
requirements:
|
113
56
|
- - ">="
|
114
57
|
- !ruby/object:Gem::Version
|
115
58
|
version: '0'
|
116
59
|
requirements: []
|
117
|
-
rubygems_version: 3.
|
118
|
-
signing_key:
|
60
|
+
rubygems_version: 3.6.7
|
119
61
|
specification_version: 4
|
120
62
|
summary: A low-dependency gem for declaring and querying engineering teams
|
121
63
|
test_files: []
|