declarative_policy 1.1.0 → 2.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6dffd68fb3da1c6d7629901c2436d47e87d7a2b275dfa7282371ef97e7e623b9
4
- data.tar.gz: 9d07ae900c5c2de61025ac2ecff512da42a235f2365db2696babc4b41a654ec2
3
+ metadata.gz: 704af6c0500c00a0e6c6797dd5b496c098db86f33ccc1ef2496be37e43b33e03
4
+ data.tar.gz: 2236adb02dbee28b6565fc72541fe2fbee5a087ef15cc2fe1f0d060ce1eece13
5
5
  SHA512:
6
- metadata.gz: e95c536a5b724dc302e192c975b7adf9a3096a7b2fca2ebe63cc1a6fcead19bb37928fecd796b63403902c7885f577859beff3a51237ce37d7a4deff9a51318d
7
- data.tar.gz: acfa272dae2fce1bb4ea9be06c45d10f3da93a8a87ea8c329db1b2e8cd8cf707615104945872bc391e4eb35bc9b2adba0d768ecdd9ddf1bb5ce480ba7dd337c0
6
+ metadata.gz: c3841495e1922ae72f704524e40ca080da4838cfe075d401350212f5b31f116f6003aa3e371d2a0084a11986b4745bb82946ad84b61247a19b59470a26e24755
7
+ data.tar.gz: f3f0d97ba2b9e56079d5307c135ec2b15c73759661f32e6cda2db44ab0fa21002598725cf0c762ca85c0580ae1c5b7c397115f185ec94c9da600cfaf19f6e590
data/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ 2.0.0:
2
+
3
+ - Drop explicit support for Ruby 2.6 and 2.7 by removing those versions from
4
+ the CI matrix. These Ruby versions are now past EOL.
5
+ - Rename default condition scope name to `:user_and_subject`
6
+ - Clarify the use of `User` and `Subject` in README and documentation
7
+ - Update `ruby-git` in Gemfile.lock
8
+
9
+ 1.1.1:
10
+
11
+ - Define development dependencies
12
+
1
13
  1.1.0:
2
14
 
3
15
  - Add cache invalidation API: `DeclarativePolicy.invalidate(cache, keys)`
data/README.md CHANGED
@@ -20,14 +20,72 @@ gem 'declarative_policy'
20
20
 
21
21
  And then execute:
22
22
 
23
- $ bundle install
23
+ ```plain
24
+ $ bundle install
25
+ ```
24
26
 
25
27
  Or install it yourself as:
26
28
 
27
- $ gem install declarative_policy
29
+ ```plain
30
+ $ gem install declarative_policy
31
+ ```
32
+
33
+ ## Example
28
34
 
29
- ## Usage
35
+ ```ruby
36
+ require 'declarative_policy'
37
+
38
+ class User
39
+ attr_reader :name
40
+
41
+ def initialize(name:)
42
+ @name = name
43
+ end
44
+ end
45
+
46
+ class Vehicle
47
+ def initialize(owner:, trusted: [])
48
+ @owner = owner
49
+ @trusted = trusted
50
+ end
51
+
52
+ def owner?(user)
53
+ @owner.name == user.name
54
+ end
55
+
56
+ def trusted?(user)
57
+ @owner.name == user.name || @trusted.detect { |t| t.name == user.name }
58
+ end
59
+ end
30
60
 
61
+ class VehiclePolicy < DeclarativePolicy::Base
62
+ condition(:owns) { @subject.owner?(@user) }
63
+ condition(:trusted) { @subject.trusted?(@user) }
64
+
65
+ rule { owns }.enable :sell_vehicle
66
+ rule { trusted }.enable :drive_vehicle
67
+ end
68
+
69
+ jack = User.new(name: 'jack')
70
+ jill = User.new(name: 'jill')
71
+ jacks_vehicle = Vehicle.new(owner: jack, trusted: [jill])
72
+ jills_vehicle = Vehicle.new(owner: jill, trusted: [jack])
73
+
74
+ puts "Jack can drive Jack's vehicle? -> #{DeclarativePolicy.policy_for(jack, jacks_vehicle).can?(:drive_vehicle)}"
75
+ puts "Jack can drive Jill's vehicle? -> #{DeclarativePolicy.policy_for(jack, jills_vehicle).can?(:drive_vehicle)}"
76
+ puts "Jack can sell Jack's vehicle? -> #{DeclarativePolicy.policy_for(jack, jacks_vehicle).can?(:sell_vehicle)}"
77
+ puts "Jack can sell Jill's vehicle? -> #{DeclarativePolicy.policy_for(jack, jills_vehicle).can?(:sell_vehicle)}"
78
+ ```
79
+
80
+ ```plain
81
+ $ ruby example.rb
82
+ Jack can drive Jack's vehicle? -> true
83
+ Jack can drive Jill's vehicle? -> true
84
+ Jack can sell Jack's vehicle? -> true
85
+ Jack can sell Jill's vehicle? -> false
86
+ ```
87
+
88
+ ## Usage
31
89
 
32
90
  The core abstraction of this library is a `Policy`. Policies combine:
33
91
 
@@ -36,9 +94,12 @@ The core abstraction of this library is a `Policy`. Policies combine:
36
94
 
37
95
  This library exists to determine the truth value of statements of the form:
38
96
 
97
+ ```plain
98
+ User Predicate [Subject]
39
99
  ```
40
- Subject Predicate [Object]
41
- ```
100
+
101
+ Renaming `User` to `Actor` and `Subject` to `Resource` is discussed in
102
+ [this issue](https://gitlab.com/gitlab-org/ruby/gems/declarative-policy/-/issues/6).
42
103
 
43
104
  For example:
44
105
 
@@ -64,14 +125,14 @@ class VehiclePolicy < DeclarativePolicy::Base
64
125
  condition(:owns, score: 0) { @subject.owner == @user }
65
126
  condition(:has_access_to, score: 3) { @subject.owner.trusts?(@user) }
66
127
  condition(:intoxicated, score: 5) { @user.blood_alcohol > laws.max_blood_alcohol }
67
-
128
+
68
129
  # conclusions we can draw:
69
130
  rule { owns }.enable :drive_vehicle
70
131
  rule { has_access_to }.enable :drive_vehicle
71
132
  rule { ~old_enough_to_drive }.prevent :drive_vehicle
72
133
  rule { intoxicated }.prevent :drive_vehicle
73
134
  rule { ~has_driving_license }.prevent :drive_vehicle
74
-
135
+
75
136
  # we can use methods to abstract common logic
76
137
  def laws
77
138
  @subject.registration.country.driving_laws
@@ -128,10 +189,16 @@ interactive prompt that will allow you to experiment.
128
189
 
129
190
  To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
130
191
 
192
+ ## Additional Reading Material
193
+
194
+ More details on policies and custom roles can be found in the following pages:
195
+ - [Development Process for the DeclarativePolicy framework](https://docs.gitlab.com/ee/development/policies.html)
196
+ - [Custom Roles docs](https://docs.gitlab.com/ee/development/permissions/custom_roles.html)
197
+
131
198
  ## Contributing
132
199
 
133
200
  Bug reports and merge requests are welcome on GitLab at
134
- https://gitlab.com/gitlab-org/declarative-policy. This project is intended to be
201
+ https://gitlab.com/gitlab-org/ruby/gems/declarative-policy. This project is intended to be
135
202
  a safe, welcoming space for collaboration, and contributors are expected to
136
203
  adhere to the [GitLab code of conduct](https://about.gitlab.com/community/contribute/code-of-conduct/).
137
204
 
@@ -5,8 +5,8 @@ require_relative 'lib/declarative_policy/version'
5
5
  Gem::Specification.new do |spec|
6
6
  spec.name = 'declarative_policy'
7
7
  spec.version = DeclarativePolicy::VERSION
8
- spec.authors = ['Jeanine Adkisson', 'Alexis Kalderimis']
9
- spec.email = ['akalderimis@gitlab.com']
8
+ spec.authors = ['group::authorization']
9
+ spec.email = ['engineering@gitlab.com']
10
10
 
11
11
  spec.summary = 'An authorization library with a focus on declarative policy definitions.'
12
12
  spec.description = <<~DESC
@@ -17,20 +17,35 @@ Gem::Specification.new do |spec|
17
17
 
18
18
  This library is in production use at GitLab.com
19
19
  DESC
20
- spec.homepage = 'https://gitlab.com/gitlab-org/declarative-policy'
20
+ spec.homepage = 'https://gitlab.com/gitlab-org/ruby/gems/declarative-policy'
21
21
  spec.license = 'MIT'
22
- spec.required_ruby_version = Gem::Requirement.new('>= 2.5.0')
22
+ spec.required_ruby_version = Gem::Requirement.new('>= 3.0.0')
23
23
 
24
24
  spec.metadata['homepage_uri'] = spec.homepage
25
- spec.metadata['source_code_uri'] = 'https://gitlab.com/gitlab-org/declarative-policy'
26
- spec.metadata['changelog_uri'] = 'https://gitlab.com/gitlab-org/declarative-policy/-/blobs/master/CHANGELOG.md'
25
+ spec.metadata['source_code_uri'] = 'https://gitlab.com/gitlab-org/ruby/gems/declarative-policy'
26
+ spec.metadata['changelog_uri'] = 'https://gitlab.com/gitlab-org/ruby/gems/declarative-policy/-/blob/main/CHANGELOG.md'
27
+
28
+ spec.metadata['rubygems_mfa_required'] = 'false'
27
29
 
28
30
  # Specify which files should be added to the gem when it is released.
29
- # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
30
31
  spec.files = Dir.chdir(File.expand_path(__dir__)) do
31
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
32
+ %w[
33
+ *.gemspec
34
+ lib/**/*.rb
35
+ *.{md,txt}
36
+ doc/**/*
37
+ ].flat_map { |pattern| Dir.glob(pattern) }
32
38
  end
33
39
  spec.bindir = 'exe'
34
40
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
35
41
  spec.require_paths = ['lib']
42
+
43
+ # Development dependencies:
44
+ spec.add_development_dependency 'benchmark-ips', '~> 2.12'
45
+ spec.add_development_dependency 'gitlab-dangerfiles', '~> 3.8'
46
+ spec.add_development_dependency 'gitlab-styles', '~> 12.0'
47
+ spec.add_development_dependency 'pry-byebug'
48
+ spec.add_development_dependency 'rake', '~> 12.0'
49
+ spec.add_development_dependency 'rspec', '~> 3.10'
50
+ spec.add_development_dependency 'rspec-parameterized', '~> 1.0'
36
51
  end
data/doc/caching.md CHANGED
@@ -122,8 +122,8 @@ policy itself.
122
122
 
123
123
  The best approach here is to use normal Ruby methods and instance variables for
124
124
  such values. The policy instances themselves are cached, so that any two
125
- invocations of `DeclarativePolicy.policy_for(user, object)` with identical
126
- `user` and `object` arguments will always return the same policy object. This
125
+ invocations of `DeclarativePolicy.policy_for(user, subject)` with identical
126
+ `user` and `subject` arguments will always return the same policy object. This
127
127
  means instance variables stored on the policy will be available for the lifetime
128
128
  of the cache.
129
129
 
@@ -209,18 +209,18 @@ In this case, it doesn't matter who the user is or even where they are going:
209
209
  the condition will be computed once (per cache lifetime) for all combinations.
210
210
 
211
211
  Because of the implications for sharing, the scope determines the
212
- [`#score`](https://gitlab.com/gitlab-org/declarative-policy/blob/2ab9dbdf44fb37beb8d0f7c131742d47ae9ef5d0/lib/declarative_policy/condition.rb#L58-77) of
212
+ [`#score`](https://gitlab.com/gitlab-org/ruby/gems/declarative-policy/blob/2ab9dbdf44fb37beb8d0f7c131742d47ae9ef5d0/lib/declarative_policy/condition.rb#L58-77) of
213
213
  the condition (if not provided explicitly). The intention is to prefer values we
214
214
  are more likely (all other things being equal) to re-use:
215
215
 
216
216
  - Conditions we have already cached get a score of `0`.
217
217
  - Conditions that are in the `:global` scope get a score of `2`.
218
218
  - Conditions that are in the `:user` or `:subject` scopes get a score of `8`.
219
- - Conditions that are in the `:normal` scope get a score of `16`.
219
+ - Conditions that are in the `:user_and_subject` scope get a score of `16`.
220
220
 
221
221
  Bear helper-methods in mind when defining scopes. While the instance level cache
222
222
  for non-boolean values would not be shared, as long as the derived condition is
223
- shared (for example by being in the `:user` scope, rather than the `:normal`
223
+ shared (for example by being in the `:user` scope, rather than the `:user_and_subject`
224
224
  scope), helper-methods will also benefit from improved cache hits.
225
225
 
226
226
  ### Preferred scope
@@ -231,7 +231,7 @@ the `:subject` or the `:user` scope. We can inform the optimizer of this
231
231
  by setting `DeclarativePolicy.preferred_scope`.
232
232
 
233
233
  To do this, check the abilities within a block bounded
234
- by [`DeclarativePolicy.with_preferred_scope`](https://gitlab.com/gitlab-org/declarative-policy/blob/481c322a74f76c325d3ccab7f2f3cc2773e8168b/lib/declarative_policy/preferred_scope.rb#L7-13).
234
+ by [`DeclarativePolicy.with_preferred_scope`](https://gitlab.com/gitlab-org/ruby/gems/declarative-policy/blob/481c322a74f76c325d3ccab7f2f3cc2773e8168b/lib/declarative_policy/preferred_scope.rb#L7-13).
235
235
  For example:
236
236
 
237
237
  ```ruby
@@ -270,7 +270,7 @@ have different `object_id` values, and using `object_id` will not get optimal ca
270
270
  policy subjects should implement `#id` for this reason. `ActiveRecord` models
271
271
  with an `id` primary ID attribute do not need any extra configuration.
272
272
 
273
- Please see: [`DeclarativePolicy::Cache`](https://gitlab.com/gitlab-org/declarative-policy/blob/master/lib/declarative_policy/cache.rb).
273
+ Please see: [`DeclarativePolicy::Cache`](https://gitlab.com/gitlab-org/ruby/gems/declarative-policy/blob/main/lib/declarative_policy/cache.rb).
274
274
 
275
275
  ## Cache invalidation
276
276
 
@@ -22,9 +22,9 @@ and then evaluating them with `DeclarativePolicy::Base#allowed?`.
22
22
  You may wish to define a method to abstract policy evaluation. Something like:
23
23
 
24
24
  ```ruby
25
- def allowed?(user, ability, object)
25
+ def allowed?(user, ability, subject)
26
26
  opts = { cache: Cache.current_cache } # re-using a cache between checks eliminates duplication of work
27
- policy = DeclarativePolicy.policy_for(user, object, opts)
27
+ policy = DeclarativePolicy.policy_for(user, subject, opts)
28
28
  policy.allowed?(ability)
29
29
  end
30
30
  ```
@@ -41,9 +41,9 @@ want to know if a user can drive a vehicle. We need a `VehiclePolicy`:
41
41
  ```ruby
42
42
  class VehiclePolicy < DeclarativePolicy::Base
43
43
  # conditions go here by convention
44
-
44
+
45
45
  # rules go here by convention
46
-
46
+
47
47
  # helper methods go last
48
48
  end
49
49
  ```
@@ -55,14 +55,14 @@ Conditions are facts about the state of the system.
55
55
  They have access to two elements of the proposition:
56
56
 
57
57
  - `@user` - the representation of a user in your system: the *subject* of the proposition.
58
- `user` in `allowed?(user, ability, object)`. `@user` may be `nil`, which means
58
+ `user` in `allowed?(user, ability, subject)`. `@user` may be `nil`, which means
59
59
  that the current user is anonymous (for example this may reflect an
60
60
  unauthenticated request in your system).
61
61
  - `@subject` - any domain object that has an associated policy: the *object* of
62
- the predicate of the proposition. `object` in `allowed?(user, ability, object)`.
62
+ the predicate of the proposition. `subject` in `allowed?(user, ability, subject)`.
63
63
  `@subject` is never `nil`. See [handling `nil` values](./configuration.md#handling-nil-values)
64
64
  for details of how to apply policies to `nil` values.
65
-
65
+
66
66
 
67
67
  They are defined as `condition(name, **options, &block)`, where the block is
68
68
  evaluated in the context of an instance of the policy.
@@ -184,7 +184,7 @@ like:
184
184
  ```ruby
185
185
  class DrivingLicensePolicy < DeclarativePolicy::Base
186
186
  condition(:expired) { @subject.expires_at <= Time.current }
187
-
187
+
188
188
  rule { expired }.prevent :drive_vehicle
189
189
  end
190
190
  ```
@@ -194,7 +194,7 @@ And a registration policy:
194
194
  ```ruby
195
195
  class RegistrationPolicy < DeclarativePolicy::Base
196
196
  condition(:valid) { @subject.valid_for?(@user.current_location) }
197
-
197
+
198
198
  rule { ~valid }.prevent :drive_vehicle
199
199
  end
200
200
  ```
@@ -209,3 +209,41 @@ delegate { @subject.registration }
209
209
 
210
210
  This is a powerful mechanism for inferring rules based on relationships between
211
211
  objects.
212
+
213
+ #### Overrides
214
+
215
+ It can be useful to declare that the given abilities should not be read
216
+ from delegates. This declaration is useful if you have an ability you want
217
+ to define differently in a policy than in a delegated policy, but you still
218
+ want to delegate all other abilities.
219
+
220
+ ```ruby
221
+ delegate { @subject.parent }
222
+
223
+ overrides :drive_car, :watch_tv
224
+ ```
225
+
226
+ NOTE:
227
+ Rules with `prevent_all` present in the delegated policy are properly
228
+ not used during overridden abilities evaluation.
229
+
230
+ #### Delegated conditions
231
+
232
+ When named delegates are defined, their [conditions](#conditions) can be
233
+ referenced in [rules](#rules) using bare words.
234
+
235
+ Given a registration policy:
236
+
237
+ ```ruby
238
+ class RegistrationPolicy < DeclarativePolicy::Base
239
+ condition(:valid) { @subject.valid_for?(@user.current_location) }
240
+ end
241
+ ```
242
+
243
+ The vehicle policy can reference the `:valid` condition as follows:
244
+
245
+ ```ruby
246
+ delegate(:registration) { @subject.registration }
247
+
248
+ rule { registration.valid }.enable :drive_vehicle
249
+ ```
data/doc/optimization.md CHANGED
@@ -97,7 +97,7 @@ They aren't necessarily run in this order, however. Instead, we try to order
97
97
  the list to minimize unnecessary work.
98
98
 
99
99
  The
100
- [logic](https://gitlab.com/gitlab-org/declarative-policy/blob/659ac0525773a76cf8712d47b3c2dadd03b758c9/lib/declarative_policy/runner.rb#L80-112)
100
+ [logic](https://gitlab.com/gitlab-org/ruby/gems/declarative-policy/blob/659ac0525773a76cf8712d47b3c2dadd03b758c9/lib/declarative_policy/runner.rb#L80-112)
101
101
  to process this list is (in pseudo-code):
102
102
 
103
103
  ```pseudo
@@ -144,7 +144,7 @@ module DeclarativePolicy
144
144
  #
145
145
  # example:
146
146
  #
147
- # delegate { @subect.parent }
147
+ # delegate { @subject.parent }
148
148
  #
149
149
  # overrides :drive_car, :watch_tv
150
150
  #
@@ -9,10 +9,14 @@ module DeclarativePolicy
9
9
  class Condition
10
10
  attr_reader :name, :description, :scope, :manual_score, :context_key
11
11
 
12
+ VALID_SCOPES = %i[user_and_subject user subject global].freeze
13
+ ALLOWED_SCOPES = VALID_SCOPES + %i[normal]
14
+
12
15
  def initialize(name, opts = {}, &compute)
13
16
  @name = name
14
17
  @compute = compute
15
- @scope = opts.fetch(:scope, :normal)
18
+ @scope = fetch_scope(opts)
19
+
16
20
  @description = opts.delete(:description)
17
21
  @context_key = opts[:context_key]
18
22
  @manual_score = opts.fetch(:score, nil)
@@ -25,6 +29,20 @@ module DeclarativePolicy
25
29
  def key
26
30
  "#{@context_key}/#{@name}"
27
31
  end
32
+
33
+ private
34
+
35
+ def fetch_scope(options)
36
+ result = options.fetch(:scope, :user_and_subject)
37
+ if result == :normal
38
+ warn "[DEPRECATION] `:normal` is deprecated and will be removed in 2.0. Please use new name `:user_and_subject`"
39
+ result = :user_and_subject
40
+ end
41
+
42
+ raise "Invalid scope #{result}. Allowed values: #{VALID_SCOPES.inspect}" unless ALLOWED_SCOPES.include?(result)
43
+
44
+ result
45
+ end
28
46
  end
29
47
 
30
48
  # In contrast to a Condition, a ManifestCondition contains
@@ -68,7 +86,7 @@ module DeclarativePolicy
68
86
  return 2 if @condition.scope == :global
69
87
 
70
88
  # "Normal" rules can't share caches with any other policies
71
- return 16 if @condition.scope == :normal
89
+ return 16 if @condition.scope == :user_and_subject
72
90
 
73
91
  # otherwise, we're :user or :subject scope, so it's 4 if
74
92
  # the caller has declared a preference
@@ -85,11 +103,10 @@ module DeclarativePolicy
85
103
  def cache_key
86
104
  @cache_key ||=
87
105
  case @condition.scope
88
- when :normal then "/dp/condition/#{@condition.key}/#{user_key},#{subject_key}"
106
+ when :global then "/dp/condition/#{@condition.key}"
89
107
  when :user then "/dp/condition/#{@condition.key}/#{user_key}"
90
108
  when :subject then "/dp/condition/#{@condition.key}/#{subject_key}"
91
- when :global then "/dp/condition/#{@condition.key}"
92
- else raise 'invalid scope'
109
+ else "/dp/condition/#{@condition.key}/#{user_key},#{subject_key}"
93
110
  end
94
111
  end
95
112
 
@@ -35,7 +35,7 @@ module DeclarativePolicy
35
35
  def policy_class(domain_class_name)
36
36
  return unless domain_class_name
37
37
 
38
- @class_for.call((@name_transformation.call(domain_class_name)))
38
+ @class_for.call(@name_transformation.call(domain_class_name))
39
39
  rescue NameError
40
40
  nil
41
41
  end
@@ -15,7 +15,7 @@ module DeclarativePolicy
15
15
  @rule_dsl.delegate(@delegate_name, msg)
16
16
  end
17
17
 
18
- def respond_to_missing?(msg, include_all)
18
+ def respond_to_missing?(_msg, _include_all)
19
19
  true
20
20
  end
21
21
  end
@@ -33,10 +33,10 @@ module DeclarativePolicy
33
33
  @context_class.prevent_all_when(@rule)
34
34
  end
35
35
 
36
- def method_missing(msg, *args, &block)
36
+ def method_missing(msg, ...)
37
37
  return super unless @context_class.respond_to?(msg)
38
38
 
39
- @context_class.__send__(msg, *args, &block) # rubocop: disable GitlabSecurity/PublicSend
39
+ @context_class.__send__(msg, ...) # rubocop: disable GitlabSecurity/PublicSend
40
40
  end
41
41
 
42
42
  def respond_to_missing?(msg)
@@ -2,7 +2,7 @@
2
2
 
3
3
  module DeclarativePolicy
4
4
  module PreferredScope
5
- PREFERRED_SCOPE_KEY = :"DeclarativePolicy.preferred_scope"
5
+ PREFERRED_SCOPE_KEY = :'DeclarativePolicy.preferred_scope'
6
6
 
7
7
  def with_preferred_scope(scope)
8
8
  old_scope = Thread.current[PREFERRED_SCOPE_KEY]
@@ -44,7 +44,7 @@ module DeclarativePolicy
44
44
  end
45
45
  end
46
46
 
47
- def respond_to_missing?(symbol, include_all)
47
+ def respond_to_missing?(_symbol, _include_all)
48
48
  true
49
49
  end
50
50
  end
@@ -93,7 +93,7 @@ module DeclarativePolicy
93
93
 
94
94
  private
95
95
 
96
- def with_state(&block)
96
+ def with_state
97
97
  @state = State.new
98
98
  old_runner_state = Thread.current[:declarative_policy_current_runner_state]
99
99
  Thread.current[:declarative_policy_current_runner_state] = @state
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DeclarativePolicy
4
- VERSION = '1.1.0'
4
+ VERSION = '2.0.1'
5
5
  end
metadata CHANGED
@@ -1,16 +1,113 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: declarative_policy
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 2.0.1
5
5
  platform: ruby
6
6
  authors:
7
- - Jeanine Adkisson
8
- - Alexis Kalderimis
7
+ - group::authorization
9
8
  autorequire:
10
9
  bindir: exe
11
10
  cert_chain: []
12
- date: 2021-11-05 00:00:00.000000000 Z
13
- dependencies: []
11
+ date: 2025-08-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: benchmark-ips
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.12'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.12'
27
+ - !ruby/object:Gem::Dependency
28
+ name: gitlab-dangerfiles
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.8'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.8'
41
+ - !ruby/object:Gem::Dependency
42
+ name: gitlab-styles
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '12.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '12.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry-byebug
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '12.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '12.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.10'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '3.10'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rspec-parameterized
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '1.0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '1.0'
14
111
  description: |
15
112
  This library provides an authorization framework with a declarative DSL
16
113
 
@@ -19,28 +116,17 @@ description: |
19
116
 
20
117
  This library is in production use at GitLab.com
21
118
  email:
22
- - akalderimis@gitlab.com
119
+ - engineering@gitlab.com
23
120
  executables: []
24
121
  extensions: []
25
122
  extra_rdoc_files: []
26
123
  files:
27
- - ".gitignore"
28
- - ".gitlab-ci.yml"
29
- - ".rspec"
30
- - ".rubocop.yml"
31
124
  - CHANGELOG.md
32
125
  - CODE_OF_CONDUCT.md
33
126
  - CONTRIBUTING.md
34
- - Dangerfile
35
- - Gemfile
36
- - Gemfile.lock
37
127
  - LICENSE.txt
38
128
  - README.md
39
- - Rakefile
40
- - benchmarks/repeated_invocation.rb
41
- - danger/plugins/project_helper.rb
42
- - danger/roulette/Dangerfile
43
- - declarative_policy.gemspec
129
+ - declarative-policy.gemspec
44
130
  - doc/caching.md
45
131
  - doc/configuration.md
46
132
  - doc/defining-policies.md
@@ -59,13 +145,14 @@ files:
59
145
  - lib/declarative_policy/runner.rb
60
146
  - lib/declarative_policy/step.rb
61
147
  - lib/declarative_policy/version.rb
62
- homepage: https://gitlab.com/gitlab-org/declarative-policy
148
+ homepage: https://gitlab.com/gitlab-org/ruby/gems/declarative-policy
63
149
  licenses:
64
150
  - MIT
65
151
  metadata:
66
- homepage_uri: https://gitlab.com/gitlab-org/declarative-policy
67
- source_code_uri: https://gitlab.com/gitlab-org/declarative-policy
68
- changelog_uri: https://gitlab.com/gitlab-org/declarative-policy/-/blobs/master/CHANGELOG.md
152
+ homepage_uri: https://gitlab.com/gitlab-org/ruby/gems/declarative-policy
153
+ source_code_uri: https://gitlab.com/gitlab-org/ruby/gems/declarative-policy
154
+ changelog_uri: https://gitlab.com/gitlab-org/ruby/gems/declarative-policy/-/blob/main/CHANGELOG.md
155
+ rubygems_mfa_required: 'false'
69
156
  post_install_message:
70
157
  rdoc_options: []
71
158
  require_paths:
@@ -74,14 +161,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
74
161
  requirements:
75
162
  - - ">="
76
163
  - !ruby/object:Gem::Version
77
- version: 2.5.0
164
+ version: 3.0.0
78
165
  required_rubygems_version: !ruby/object:Gem::Requirement
79
166
  requirements:
80
167
  - - ">="
81
168
  - !ruby/object:Gem::Version
82
169
  version: '0'
83
170
  requirements: []
84
- rubygems_version: 3.2.15
171
+ rubygems_version: 3.5.22
85
172
  signing_key:
86
173
  specification_version: 4
87
174
  summary: An authorization library with a focus on declarative policy definitions.
data/.gitignore DELETED
@@ -1,12 +0,0 @@
1
- /.bundle/
2
- /.yardoc
3
- /_yardoc/
4
- /coverage/
5
- /pkg/
6
- /spec/reports/
7
- /tmp/
8
-
9
- # rspec failure tracking
10
- .rspec_status
11
- declarative_policy-*.gem
12
- .tool-versions
data/.gitlab-ci.yml DELETED
@@ -1,91 +0,0 @@
1
- image: "ruby:2.7"
2
-
3
- include:
4
- - template: 'Workflows/MergeRequest-Pipelines.gitlab-ci.yml'
5
- - template: Security/Dependency-Scanning.gitlab-ci.yml
6
- - template: Security/License-Scanning.gitlab-ci.yml
7
- - template: Security/SAST.gitlab-ci.yml
8
- - template: Security/Secret-Detection.gitlab-ci.yml
9
-
10
- .tests:
11
- stage: test
12
- cache:
13
- paths:
14
- - vendor/ruby
15
- before_script:
16
- - ruby -v # Print out ruby version for debugging
17
- - bundle install -j $(nproc) --path vendor/ruby/$RUBY_VERSION
18
-
19
- rubocop:
20
- extends: .tests
21
- script:
22
- - bundle exec rubocop
23
-
24
- .rspec:
25
- extends: .tests
26
- script:
27
- - bundle exec rspec
28
-
29
- rspec:mri:
30
- extends: .rspec
31
- image: "ruby:$RUBY_VERSION"
32
- parallel:
33
- matrix:
34
- - RUBY_VERSION:
35
- - "2.7"
36
- - "3.0"
37
-
38
- rspec:jruby:
39
- extends: .rspec
40
- image: "bitnami/jruby:latest"
41
- variables:
42
- RUBY_VERSION: jruby
43
-
44
- rspec:truffleruby:
45
- extends: .rspec
46
- image: "flavorjones/truffleruby:latest"
47
- variables:
48
- RUBY_VERSION: truffleruby
49
-
50
- danger-review:
51
- extends: .tests
52
- needs: []
53
- script:
54
- - >
55
- if [ -z "$DANGER_GITLAB_API_TOKEN" ]; then
56
- # Force danger to skip CI source GitLab and fallback to "local only git repo".
57
- unset GITLAB_CI
58
- # We need to base SHA to help danger determine the base commit for this shallow clone.
59
- bundle exec danger dry_run --fail-on-errors=true --verbose --base="$CI_MERGE_REQUEST_DIFF_BASE_SHA"
60
- else
61
- bundle exec danger --fail-on-errors=true --verbose
62
- fi
63
-
64
- # run security jobs on MRs
65
- # see: https://gitlab.com/gitlab-org/gitlab/-/issues/218444#note_478761991
66
-
67
- brakeman-sast:
68
- rules:
69
- - if: $CI_PIPELINE_SOURCE == "merge_request_event"
70
- - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
71
-
72
- gemnasium-dependency_scanning:
73
- rules:
74
- - if: $CI_PIPELINE_SOURCE == "merge_request_event"
75
- - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
76
-
77
- bundler-audit-dependency_scanning:
78
- rules:
79
- - if: $CI_PIPELINE_SOURCE == "merge_request_event"
80
- - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
81
-
82
- license_scanning:
83
- rules:
84
- - if: $CI_PIPELINE_SOURCE == "merge_request_event"
85
- - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
86
-
87
- secret_detection:
88
- rules:
89
- - if: $CI_PIPELINE_SOURCE == "merge_request_event"
90
- - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
91
-
data/.rspec DELETED
@@ -1,4 +0,0 @@
1
- --format documentation
2
- --color
3
- --require spec_helper
4
- --order rand
data/.rubocop.yml DELETED
@@ -1,13 +0,0 @@
1
- inherit_gem:
2
- gitlab-styles:
3
- - rubocop-default.yml
4
-
5
- CodeReuse/ActiveRecord:
6
- Enabled: false
7
-
8
- AllCops:
9
- TargetRubyVersion: 2.5
10
- NewCops: enable
11
-
12
- RSpec/MultipleMemoizedHelpers:
13
- Max: 10
data/Dangerfile DELETED
@@ -1,16 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'gitlab-dangerfiles'
4
-
5
- Gitlab::Dangerfiles.import_plugins(danger)
6
- danger.import_plugin('danger/plugins/*.rb')
7
-
8
- return if helper.release_automation?
9
-
10
- danger.import_dangerfile(path: File.join('danger', 'roulette'))
11
-
12
- anything_to_post = status_report.values.any?(&:any?)
13
-
14
- if helper.ci? && anything_to_post
15
- markdown("**If needed, you can retry the [`danger-review` job](#{ENV['CI_JOB_URL']}) that generated this comment.**")
16
- end
data/Gemfile DELETED
@@ -1,23 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- source 'https://rubygems.org'
4
-
5
- # Specify your gem's dependencies in declarative-policy.gemspec
6
- gemspec
7
-
8
- group :test do
9
- gem 'rspec', '~> 3.10'
10
- gem 'rspec-parameterized', require: false
11
- gem 'pry-byebug', platforms: [:ruby]
12
- end
13
-
14
- group :development, :test do
15
- gem 'gitlab-styles', '~> 6.1.0', require: false, platforms: [:ruby]
16
- gem 'rake', '~> 12.0'
17
- gem 'benchmark', require: false
18
- gem 'rubocop', require: false
19
- end
20
-
21
- group :development, :test, :danger do
22
- gem 'gitlab-dangerfiles', '~> 1.1.0', require: false, platforms: [:ruby]
23
- end
data/Gemfile.lock DELETED
@@ -1,214 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- declarative_policy (1.1.0)
5
-
6
- GEM
7
- remote: https://rubygems.org/
8
- specs:
9
- abstract_type (0.0.7)
10
- activesupport (6.1.3.2)
11
- concurrent-ruby (~> 1.0, >= 1.0.2)
12
- i18n (>= 1.6, < 2)
13
- minitest (>= 5.1)
14
- tzinfo (~> 2.0)
15
- zeitwerk (~> 2.3)
16
- adamantium (0.2.0)
17
- ice_nine (~> 0.11.0)
18
- memoizable (~> 0.4.0)
19
- addressable (2.7.0)
20
- public_suffix (>= 2.0.2, < 5.0)
21
- ast (2.4.2)
22
- benchmark (0.1.1)
23
- binding_ninja (0.2.3)
24
- binding_ninja (0.2.3-java)
25
- byebug (11.1.3)
26
- claide (1.0.3)
27
- claide-plugins (0.9.2)
28
- cork
29
- nap
30
- open4 (~> 1.3)
31
- coderay (1.1.3)
32
- colored2 (3.1.2)
33
- concord (0.1.5)
34
- adamantium (~> 0.2.0)
35
- equalizer (~> 0.0.9)
36
- concurrent-ruby (1.1.8)
37
- cork (0.3.0)
38
- colored2 (~> 3.1)
39
- danger (8.2.3)
40
- claide (~> 1.0)
41
- claide-plugins (>= 0.9.2)
42
- colored2 (~> 3.1)
43
- cork (~> 0.1)
44
- faraday (>= 0.9.0, < 2.0)
45
- faraday-http-cache (~> 2.0)
46
- git (~> 1.7)
47
- kramdown (~> 2.3)
48
- kramdown-parser-gfm (~> 1.0)
49
- no_proxy_fix
50
- octokit (~> 4.7)
51
- terminal-table (>= 1, < 4)
52
- danger-gitlab (8.0.0)
53
- danger
54
- gitlab (~> 4.2, >= 4.2.0)
55
- diff-lcs (1.4.4)
56
- equalizer (0.0.11)
57
- faraday (1.4.1)
58
- faraday-excon (~> 1.1)
59
- faraday-net_http (~> 1.0)
60
- faraday-net_http_persistent (~> 1.1)
61
- multipart-post (>= 1.2, < 3)
62
- ruby2_keywords (>= 0.0.4)
63
- faraday-excon (1.1.0)
64
- faraday-http-cache (2.2.0)
65
- faraday (>= 0.8)
66
- faraday-net_http (1.0.1)
67
- faraday-net_http_persistent (1.1.0)
68
- ffi (1.15.4-java)
69
- git (1.8.1)
70
- rchardet (~> 1.8)
71
- gitlab (4.17.0)
72
- httparty (~> 0.18)
73
- terminal-table (~> 1.5, >= 1.5.1)
74
- gitlab-dangerfiles (1.1.1)
75
- danger-gitlab
76
- gitlab-styles (6.1.0)
77
- rubocop (~> 0.91, >= 0.91.1)
78
- rubocop-gitlab-security (~> 0.1.1)
79
- rubocop-performance (~> 1.9.2)
80
- rubocop-rails (~> 2.9)
81
- rubocop-rspec (~> 1.44)
82
- httparty (0.18.1)
83
- mime-types (~> 3.0)
84
- multi_xml (>= 0.5.2)
85
- i18n (1.8.10)
86
- concurrent-ruby (~> 1.0)
87
- ice_nine (0.11.2)
88
- kramdown (2.3.1)
89
- rexml
90
- kramdown-parser-gfm (1.1.0)
91
- kramdown (~> 2.0)
92
- memoizable (0.4.2)
93
- thread_safe (~> 0.3, >= 0.3.1)
94
- method_source (1.0.0)
95
- mime-types (3.3.1)
96
- mime-types-data (~> 3.2015)
97
- mime-types-data (3.2021.0225)
98
- minitest (5.14.4)
99
- multi_xml (0.6.0)
100
- multipart-post (2.1.1)
101
- nap (1.1.0)
102
- no_proxy_fix (0.1.2)
103
- octokit (4.21.0)
104
- faraday (>= 0.9)
105
- sawyer (~> 0.8.0, >= 0.5.3)
106
- open4 (1.3.4)
107
- parallel (1.20.1)
108
- parser (3.0.0.0)
109
- ast (~> 2.4.1)
110
- proc_to_ast (0.1.0)
111
- coderay
112
- parser
113
- unparser
114
- procto (0.0.3)
115
- pry (0.13.1)
116
- coderay (~> 1.1)
117
- method_source (~> 1.0)
118
- pry (0.13.1-java)
119
- coderay (~> 1.1)
120
- method_source (~> 1.0)
121
- spoon (~> 0.0)
122
- pry-byebug (3.9.0)
123
- byebug (~> 11.0)
124
- pry (~> 0.13.0)
125
- public_suffix (4.0.6)
126
- rack (2.2.3)
127
- rainbow (3.0.0)
128
- rake (12.3.3)
129
- rchardet (1.8.0)
130
- regexp_parser (1.8.2)
131
- rexml (3.2.4)
132
- rspec (3.10.0)
133
- rspec-core (~> 3.10.0)
134
- rspec-expectations (~> 3.10.0)
135
- rspec-mocks (~> 3.10.0)
136
- rspec-core (3.10.1)
137
- rspec-support (~> 3.10.0)
138
- rspec-expectations (3.10.1)
139
- diff-lcs (>= 1.2.0, < 2.0)
140
- rspec-support (~> 3.10.0)
141
- rspec-mocks (3.10.2)
142
- diff-lcs (>= 1.2.0, < 2.0)
143
- rspec-support (~> 3.10.0)
144
- rspec-parameterized (0.4.2)
145
- binding_ninja (>= 0.2.3)
146
- parser
147
- proc_to_ast
148
- rspec (>= 2.13, < 4)
149
- unparser
150
- rspec-support (3.10.2)
151
- rubocop (0.93.1)
152
- parallel (~> 1.10)
153
- parser (>= 2.7.1.5)
154
- rainbow (>= 2.2.2, < 4.0)
155
- regexp_parser (>= 1.8)
156
- rexml
157
- rubocop-ast (>= 0.6.0)
158
- ruby-progressbar (~> 1.7)
159
- unicode-display_width (>= 1.4.0, < 2.0)
160
- rubocop-ast (1.4.1)
161
- parser (>= 2.7.1.5)
162
- rubocop-gitlab-security (0.1.1)
163
- rubocop (>= 0.51)
164
- rubocop-performance (1.9.2)
165
- rubocop (>= 0.90.0, < 2.0)
166
- rubocop-ast (>= 0.4.0)
167
- rubocop-rails (2.9.1)
168
- activesupport (>= 4.2.0)
169
- rack (>= 1.1)
170
- rubocop (>= 0.90.0, < 2.0)
171
- rubocop-rspec (1.44.1)
172
- rubocop (~> 0.87)
173
- rubocop-ast (>= 0.7.1)
174
- ruby-progressbar (1.11.0)
175
- ruby2_keywords (0.0.4)
176
- sawyer (0.8.2)
177
- addressable (>= 2.3.5)
178
- faraday (> 0.8, < 2.0)
179
- spoon (0.0.6)
180
- ffi
181
- terminal-table (1.8.0)
182
- unicode-display_width (~> 1.1, >= 1.1.1)
183
- thread_safe (0.3.6)
184
- thread_safe (0.3.6-java)
185
- tzinfo (2.0.4)
186
- concurrent-ruby (~> 1.0)
187
- unicode-display_width (1.7.0)
188
- unparser (0.4.7)
189
- abstract_type (~> 0.0.7)
190
- adamantium (~> 0.2.0)
191
- concord (~> 0.1.5)
192
- diff-lcs (~> 1.3)
193
- equalizer (~> 0.0.9)
194
- parser (>= 2.6.5)
195
- procto (~> 0.0.2)
196
- zeitwerk (2.4.2)
197
-
198
- PLATFORMS
199
- ruby
200
- universal-java-1.8
201
-
202
- DEPENDENCIES
203
- benchmark
204
- declarative_policy!
205
- gitlab-dangerfiles (~> 1.1.0)
206
- gitlab-styles (~> 6.1.0)
207
- pry-byebug
208
- rake (~> 12.0)
209
- rspec (~> 3.10)
210
- rspec-parameterized
211
- rubocop
212
-
213
- BUNDLED WITH
214
- 2.2.15
data/Rakefile DELETED
@@ -1,8 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bundler/gem_tasks'
4
- require 'rspec/core/rake_task'
5
-
6
- RSpec::Core::RakeTask.new(:spec)
7
-
8
- task default: :spec
@@ -1,37 +0,0 @@
1
- #!/usr/bin/env ruby -w
2
- # frozen_string_literal: true
3
-
4
- require 'declarative_policy'
5
- require 'benchmark'
6
-
7
- Dir["./spec/support/policies/*.rb"].sort.each { |f| require f }
8
- Dir["./spec/support/models/*.rb"].sort.each { |f| require f }
9
-
10
- TIMES = 1_000_000
11
- LABEL = 'allowed?(driver, :drive_vehicle, car)'
12
-
13
- DeclarativePolicy.configure! do
14
- named_policy :global, GlobalPolicy
15
-
16
- name_transformation do |name|
17
- 'ReadmePolicy' if name == 'Vehicle'
18
- end
19
- end
20
-
21
- Benchmark.bm(LABEL.length) do |b|
22
- cache = {}
23
- valid_license = License.valid
24
- country = Country.moderate
25
- registration = Registration.new(number: 'xyz123', country: country)
26
- driver = User.new(name: 'The driver', driving_license: valid_license)
27
- owner = User.new(name: 'The Owner', trusted: [driver.name])
28
- car = Vehicle.new(owner: owner, registration: registration)
29
-
30
- raise 'Expected to drive' unless DeclarativePolicy.policy_for(driver, car).allowed?(:drive_vehicle)
31
-
32
- b.report LABEL do
33
- TIMES.times do
34
- DeclarativePolicy.policy_for(driver, car, cache: cache).allowed?(:drive_vehicle)
35
- end
36
- end
37
- end
@@ -1,58 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Danger
4
- # Project specific configuration
5
- class ProjectHelper < ::Danger::Plugin
6
- LOCAL_RULES ||= %w[
7
- changelog
8
- documentation
9
- ].freeze
10
-
11
- CI_ONLY_RULES ||= %w[
12
- roulette
13
- ].freeze
14
-
15
- MESSAGE_PREFIX = '==>'
16
-
17
- # First-match win, so be sure to put more specific regex at the top...
18
- # rubocop: disable Style/RegexpLiteral
19
- CATEGORIES = {
20
- %r{\A(\.gitlab-ci\.yml\z|\.gitlab/ci)} => :engineering_productivity,
21
- %r{\Alefthook.yml\z} => :engineering_productivity,
22
- %r{\A\.editorconfig\z} => :engineering_productivity,
23
- %r{Dangerfile\z} => :engineering_productivity,
24
- %r{\A(danger/|tooling/danger/)} => :engineering_productivity,
25
- %r{\A?scripts/} => :engineering_productivity,
26
- %r{\Atooling/} => :engineering_productivity,
27
- %r{(CODEOWNERS)} => :engineering_productivity,
28
- %r{\A(Gemfile|Gemfile.lock|Rakefile)\z} => :backend,
29
- %r{\A\.rubocop((_manual)?_todo)?\.yml\z} => :backend,
30
- %r{\.rb\z} => :backend,
31
- %r{(
32
- \.(md|txt)\z |
33
- \.markdownlint\.json
34
- )}x => :docs
35
- }.freeze
36
- # rubocop: enable Style/RegexpLiteral
37
-
38
- def changes_by_category
39
- helper.changes_by_category(CATEGORIES)
40
- end
41
-
42
- def changes
43
- helper.changes(CATEGORIES)
44
- end
45
-
46
- def rule_names
47
- helper.ci? ? LOCAL_RULES | CI_ONLY_RULES : LOCAL_RULES
48
- end
49
-
50
- def project_name
51
- # 'declarative-policy'
52
- # TODO: roulette uses the project name to find reviewers, but the gitlab team
53
- # directory currently does not have any team members assigned to the declarative-policy
54
- # project. We thus are piggybacking on 'gitlab' for now.
55
- 'gitlab'
56
- end
57
- end
58
- end
@@ -1,97 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'digest/md5'
4
-
5
- MESSAGE = <<MARKDOWN
6
- ## Reviewer roulette
7
-
8
- Changes that require review have been detected! A merge request is normally
9
- reviewed by both a reviewer and a maintainer.
10
- MARKDOWN
11
-
12
- CATEGORY_TABLE_HEADER = <<MARKDOWN
13
-
14
- To spread load more evenly across eligible reviewers, Danger has picked a
15
- candidate for each review slot, based on their timezone. Feel free to
16
- [override these selections](https://about.gitlab.com/handbook/engineering/projects/#gitlab) if
17
- you think someone else would be better-suited or use the
18
- [GitLab Review Workload Dashboard](https://gitlab-org.gitlab.io/gitlab-roulette/) to find other
19
- available reviewers.
20
-
21
- To read more on how to use the reviewer roulette, please take a look at the
22
- [Engineering workflow](https://about.gitlab.com/handbook/engineering/workflow/#basics) and
23
- [code review guidelines](https://docs.gitlab.com/ee/development/code_review.html). You may
24
- consider assigning a reviewer or maintainer who is
25
- a [domain expert](https://about.gitlab.com/handbook/engineering/projects/#gitlab) for
26
- the `DeclarativePolicy` library.
27
-
28
- Once you've decided who will review this merge request, assign them as a reviewer!
29
- Danger does not automatically notify them for you.
30
-
31
- | Category | Reviewer | Maintainer |
32
- | -------- | -------- | ---------- |
33
- MARKDOWN
34
-
35
- UNKNOWN_FILES_MESSAGE = <<MARKDOWN
36
-
37
- These files couldn't be categorised, so Danger was unable to suggest a reviewer.
38
- Please consider creating a merge request
39
- to [add support](https://gitlab.com/gitlab-org/gitlab/blob/master/tooling/danger/project_helper.rb)
40
- for them.
41
- MARKDOWN
42
-
43
- OPTIONAL_REVIEW_TEMPLATE = '%{role} review is optional for %{category}'
44
- NOT_AVAILABLE_TEMPLATE = 'No %{role} available'
45
-
46
- def note_for_spins_role(spins, role)
47
- spins.each do |spin|
48
- note = note_for_spin_role(spin, role)
49
-
50
- return note if note
51
- end
52
-
53
- format(NOT_AVAILABLE_TEMPLATE, role: role)
54
- end
55
-
56
- def note_for_spin_role(spin, role)
57
- if spin.optional_role == role
58
- return format(OPTIONAL_REVIEW_TEMPLATE, role: role.capitalize, category: helper.label_for_category(spin.category))
59
- end
60
-
61
- spin.public_send(role)&.markdown_name(author: roulette.team_mr_author) # rubocop:disable GitlabSecurity/PublicSend
62
- end
63
-
64
- def markdown_row_for_spins(category, spins_array)
65
- reviewer_note = note_for_spins_role(spins_array, :reviewer)
66
- maintainer_note = note_for_spins_role(spins_array, :maintainer)
67
-
68
- "| #{helper.label_for_category(category)} | #{reviewer_note} | #{maintainer_note} |"
69
- end
70
-
71
- changes = project_helper.changes_by_category
72
-
73
- # Ignore any files that are known but uncategorized. Prompt for any unknown files
74
- changes.delete(:none)
75
- # To reinstate roulette for documentation, remove this line.
76
- changes.delete(:docs)
77
- # No special review for changelog needed and changelog was never a label.
78
- changes.delete(:changelog)
79
- # No special review for feature flags needed.
80
- changes.delete(:feature_flag)
81
- categories = changes.keys.to_set.delete(:unknown)
82
-
83
- if changes.any?
84
- project = project_helper.project_name
85
-
86
- random_roulette_spins = roulette.spin(project, categories, timezone_experiment: false)
87
-
88
- rows = random_roulette_spins.map do |spin|
89
- markdown_row_for_spins(spin.category, [spin])
90
- end
91
-
92
- markdown(MESSAGE)
93
- markdown(CATEGORY_TABLE_HEADER + rows.join("\n")) unless rows.empty?
94
-
95
- unknown = changes.fetch(:unknown, [])
96
- markdown(UNKNOWN_FILES_MESSAGE + helper.markdown_list(unknown)) unless unknown.empty?
97
- end