danger-gitlab_reviewbot 1.1.2 → 1.1.8
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/.gitlab-ci.yml +34 -0
- data/Gemfile.lock +9 -9
- data/danger-gitlab_reviewbot.gemspec +1 -1
- data/lib/danger_gitlab_reviewbot.rb +2 -0
- data/lib/danger_plugin.rb +2 -0
- data/lib/gitlab_reviewbot/gem_version.rb +3 -1
- data/lib/gitlab_reviewbot/gitlab.rb +60 -31
- data/lib/gitlab_reviewbot/plugin.rb +35 -34
- data/lib/gitlab_reviewbot/strategies.rb +5 -4
- data/lib/gitlab_reviewbot/strategies/least_busy.rb +10 -10
- data/lib/gitlab_reviewbot/strategies/random.rb +6 -4
- data/lib/gitlab_reviewbot/strategies/strategy.rb +12 -8
- data/spec/gitlab_ext_spec.rb +51 -0
- data/spec/gitlab_reviewbot_spec.rb +13 -14
- data/spec/least_busy_strategy_spec.rb +31 -13
- data/spec/random_strategy_spec.rb +10 -11
- data/spec/spec_helper.rb +11 -8
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4a7eaa5cd15e0c7c399b577b76461cf7eb3580b1d1350bcc0175c5126784fc1c
|
4
|
+
data.tar.gz: 17c5294182aad19fb6d68dcf6858ab313a1ed140a53acffa6ef815abdb4b5a48
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b66136604fadac6c2d49a53739b5435a40a3634a269683d68af032a0bd5475a8070f3a6f8a49801bc537a167c4f3b5955fd97fa464ea9349c268056612526708
|
7
|
+
data.tar.gz: 776cc841222ceb2f057aa7c9bc4fda90b3dff2fddfa602c4863e036f71ab6af098a1a22b0606dda00a829d98edcd7da77d02bf68b4c3ab9adc299489d0471f4a
|
data/.gitlab-ci.yml
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
image: "ruby:2.6"
|
2
|
+
|
3
|
+
cache:
|
4
|
+
paths:
|
5
|
+
- vendor/bundle/
|
6
|
+
|
7
|
+
stages:
|
8
|
+
- test
|
9
|
+
- deploy
|
10
|
+
|
11
|
+
test:
|
12
|
+
stage: test
|
13
|
+
script:
|
14
|
+
- gem install bundler
|
15
|
+
- bundle --path vendor/bundle
|
16
|
+
- bundle exec rake spec
|
17
|
+
artifacts:
|
18
|
+
paths:
|
19
|
+
- rspec.xml
|
20
|
+
reports:
|
21
|
+
junit: rspec.xml
|
22
|
+
|
23
|
+
deploy:
|
24
|
+
stage: deploy
|
25
|
+
only:
|
26
|
+
- tags
|
27
|
+
before_script:
|
28
|
+
- 'mkdir ~/.gem && echo -e "---\r\n:rubygems_api_key: $RUBYGEMS_API_KEY" > ~/.gem/credentials && chmod 0600 ~/.gem/credentials'
|
29
|
+
script:
|
30
|
+
- gem install bundler
|
31
|
+
- bundle --path vendor/bundle
|
32
|
+
- bundle exec rake build
|
33
|
+
- gem push pkg/danger-gitlab_reviewbot-${CI_COMMIT_TAG}.gem
|
34
|
+
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
danger-gitlab_reviewbot (1.1.
|
4
|
+
danger-gitlab_reviewbot (1.1.7)
|
5
5
|
danger-gitlab
|
6
6
|
danger-plugin-api (~> 1.0)
|
7
7
|
|
@@ -21,26 +21,26 @@ GEM
|
|
21
21
|
colored2 (3.1.2)
|
22
22
|
cork (0.3.0)
|
23
23
|
colored2 (~> 3.1)
|
24
|
-
danger (
|
24
|
+
danger (8.0.1)
|
25
25
|
claide (~> 1.0)
|
26
26
|
claide-plugins (>= 0.9.2)
|
27
27
|
colored2 (~> 3.1)
|
28
28
|
cork (~> 0.1)
|
29
|
-
faraday (
|
29
|
+
faraday (>= 0.9.0, < 2.0)
|
30
30
|
faraday-http-cache (~> 2.0)
|
31
|
-
git (~> 1.
|
31
|
+
git (~> 1.7)
|
32
32
|
kramdown (~> 2.0)
|
33
33
|
kramdown-parser-gfm (~> 1.0)
|
34
34
|
no_proxy_fix
|
35
35
|
octokit (~> 4.7)
|
36
36
|
terminal-table (~> 1)
|
37
|
-
danger-gitlab (
|
38
|
-
danger
|
37
|
+
danger-gitlab (8.0.0)
|
38
|
+
danger
|
39
39
|
gitlab (~> 4.2, >= 4.2.0)
|
40
40
|
danger-plugin-api (1.0.0)
|
41
41
|
danger (> 2.0)
|
42
42
|
diff-lcs (1.3)
|
43
|
-
faraday (0.
|
43
|
+
faraday (1.0.1)
|
44
44
|
multipart-post (>= 1.2, < 3)
|
45
45
|
faraday-http-cache (2.2.0)
|
46
46
|
faraday (>= 0.8)
|
@@ -48,7 +48,7 @@ GEM
|
|
48
48
|
formatador (0.2.5)
|
49
49
|
git (1.7.0)
|
50
50
|
rchardet (~> 1.8)
|
51
|
-
gitlab (4.
|
51
|
+
gitlab (4.15.0)
|
52
52
|
httparty (~> 0.14, >= 0.14.0)
|
53
53
|
terminal-table (~> 1.5, >= 1.5.1)
|
54
54
|
guard (2.16.2)
|
@@ -65,7 +65,7 @@ GEM
|
|
65
65
|
guard (~> 2.1)
|
66
66
|
guard-compat (~> 1.1)
|
67
67
|
rspec (>= 2.99.0, < 4.0)
|
68
|
-
httparty (0.18.
|
68
|
+
httparty (0.18.1)
|
69
69
|
mime-types (~> 3.0)
|
70
70
|
multi_xml (>= 0.5.2)
|
71
71
|
kramdown (2.2.1)
|
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
|
|
10
10
|
spec.email = ['fabio.gallonetto@curve.com']
|
11
11
|
spec.description = %q{A review raffle bot for Gitlab }
|
12
12
|
spec.summary = %q{A review raffle bot for Gitlab.}
|
13
|
-
spec.homepage = 'https://
|
13
|
+
spec.homepage = 'https://git.curve.tools/fabio.gallonetto/danger-gitlab_reviewbot'
|
14
14
|
spec.license = 'MIT'
|
15
15
|
|
16
16
|
spec.files = `git ls-files`.split($/)
|
data/lib/danger_plugin.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "gitlab"
|
2
4
|
|
3
5
|
module Gitlab
|
4
6
|
class User
|
@@ -17,57 +19,62 @@ module Gitlab
|
|
17
19
|
end
|
18
20
|
end
|
19
21
|
|
20
|
-
class
|
22
|
+
class ClientHelper
|
23
|
+
def initialize(client)
|
24
|
+
@client = client
|
25
|
+
end
|
26
|
+
|
27
|
+
def users_with_pending_mr_review(project_id)
|
28
|
+
outstanding_mrs = fetch_mrs_requiring_review(project_id)
|
29
|
+
all_assignees = outstanding_mrs.reduce([]) { |acc, mr| acc + mr.assignees - assignees_with_review(project_id, mr.id, mr.assignees)}
|
30
|
+
assignees_id_map = all_assignees.each_with_object({}) do |a, acc|
|
31
|
+
aid = a["id"]
|
32
|
+
ausername = a["username"]
|
33
|
+
assignee = acc[aid] || User.new(aid, ausername)
|
34
|
+
assignee.review_count += 1
|
35
|
+
acc[aid] = assignee
|
36
|
+
end
|
37
|
+
assignees_id_map.values
|
38
|
+
end
|
39
|
+
|
40
|
+
def fetch_mrs_requiring_review(project_id)
|
41
|
+
@client.merge_requests(project_id, state: "opened", per_page: "100")
|
42
|
+
end
|
43
|
+
|
21
44
|
def fetch_users_for_group(group_name)
|
22
|
-
group_id =
|
45
|
+
group_id = search_group_with_path(group_name)
|
23
46
|
return nil if group_id.nil?
|
24
47
|
|
25
|
-
res = group_members(group_id)
|
48
|
+
res = @client.group_members(group_id, :per_page => 100)
|
26
49
|
|
27
50
|
developer_access_level = 30
|
28
|
-
res.select { |u| u.state ==
|
51
|
+
res.select { |u| u.state == "active" && u.access_level >= developer_access_level }.map { |u| User.new(u.id, u.username) }
|
29
52
|
end
|
30
53
|
|
31
54
|
def assign_mr_to_users(project_id, mr_iid, users)
|
32
55
|
user_ids = users.map(&:id)
|
33
|
-
update_merge_request(project_id, mr_iid,
|
56
|
+
@client.update_merge_request(project_id, mr_iid, "assignee_ids" => user_ids)
|
57
|
+
|
34
58
|
end
|
35
59
|
|
36
60
|
def fetch_author_for_mr(project_id, mr_iid)
|
37
|
-
res = merge_request(project_id, mr_iid)
|
61
|
+
res = @client.merge_request(project_id, mr_iid)
|
38
62
|
User.new(res.author.id, res.author.name)
|
39
63
|
end
|
40
64
|
|
41
|
-
def fetch_mrs_requiring_review(project_id)
|
42
|
-
merge_requests(project_id, :state => 'opened', :per_page => '100').select { |mr| mr.merge_status != 'can_be_merged' }
|
43
|
-
end
|
44
|
-
|
45
65
|
def find_user_with_username(username)
|
46
|
-
users({:username
|
47
|
-
end
|
48
|
-
|
49
|
-
def users_with_pending_mr_review(project_id)
|
50
|
-
outstanding_mrs = fetch_mrs_requiring_review(project_id)
|
51
|
-
all_assignees = outstanding_mrs.reduce([]) { |acc, mr| acc + mr.assignees }
|
52
|
-
assignees_id_map = all_assignees.reduce({}) { |acc, a|
|
53
|
-
aid = a['id']
|
54
|
-
ausername = a['username']
|
55
|
-
assignee = acc[aid] || User.new(aid, ausername)
|
56
|
-
assignee.review_count += 1
|
57
|
-
acc[aid] = assignee
|
58
|
-
acc
|
59
|
-
}
|
60
|
-
assignees_id_map.values
|
66
|
+
@client.users({ username: username }).map { |u| User.new(u.id, u.username) }
|
61
67
|
end
|
62
68
|
|
63
69
|
def fetch_mr_reviewers(project_id, mr_iid)
|
64
|
-
merge_request(project_id, mr_iid).assignees.map { |u| User.new(u[
|
70
|
+
@client.merge_request(project_id, mr_iid).assignees.map { |u| User.new(u["id"], u["username"]) }
|
65
71
|
end
|
66
72
|
|
67
73
|
private
|
68
|
-
|
69
|
-
|
70
|
-
|
74
|
+
|
75
|
+
def search_group_with_path(group_name)
|
76
|
+
short_name = group_name.split("/").last
|
77
|
+
res = @client.group_search(short_name)
|
71
78
|
res = res.find { |i| i.full_path == group_name }
|
72
79
|
|
73
80
|
if res.nil?
|
@@ -76,6 +83,28 @@ module Gitlab
|
|
76
83
|
res.id
|
77
84
|
end
|
78
85
|
end
|
86
|
+
|
87
|
+
def assignees_with_review(project_id, mr_iid, gusers)
|
88
|
+
approvals = @client.merge_request_approval_state(project_id, mr_iid)
|
89
|
+
approved_by = approvals.rules.reduce([]) { |acc, r| acc + r['approved_by'] }
|
90
|
+
gusers.filter { |u| approved_by.index { |au| au['id'] == u['id'] } != nil }
|
91
|
+
end
|
92
|
+
|
79
93
|
end
|
80
|
-
end
|
81
94
|
|
95
|
+
class Client < API
|
96
|
+
|
97
|
+
def client_helper
|
98
|
+
@client_helper ||= ClientHelper.new(self)
|
99
|
+
end
|
100
|
+
|
101
|
+
def method_missing(method, *args)
|
102
|
+
if @client_helper.respond_to?(method)
|
103
|
+
@client_helper.send(method, *args)
|
104
|
+
else
|
105
|
+
super
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
end
|
@@ -1,5 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "gitlab_reviewbot/strategies"
|
3
4
|
|
4
5
|
module Danger
|
5
6
|
# This is your plugin class. Any attributes or methods you expose here will
|
@@ -20,8 +21,7 @@ module Danger
|
|
20
21
|
# @tags monday, weekends, time, rattata
|
21
22
|
#
|
22
23
|
class DangerGitlabReviewbot < Plugin
|
23
|
-
|
24
|
-
# Define the group to take the reviewers from.
|
24
|
+
# Define the group to take the reviewers from.
|
25
25
|
# NOTE: This is the group full path as in 'tech/iOS' instead of just the group name
|
26
26
|
#
|
27
27
|
# @return String
|
@@ -44,31 +44,10 @@ module Danger
|
|
44
44
|
# * Danger::AssignStrategies::LeastBusyStrategy - assign the N users with the least amount of open MRs
|
45
45
|
# to review
|
46
46
|
#
|
47
|
-
def strategy
|
48
|
-
@strategy #|| Danger::AssignStrategies::RandomStrategy.new(client: gitlab.api, project: project_id, mr: mr_iid)
|
49
|
-
end
|
50
|
-
|
51
47
|
def strategy=(klass)
|
52
|
-
@strategy = klass.new(client: gitlab.api, project: project_id,
|
53
|
-
end
|
54
|
-
|
55
|
-
def project_id
|
56
|
-
ENV['CI_PROJECT_ID']
|
57
|
-
end
|
58
|
-
|
59
|
-
def mr_iid
|
60
|
-
ENV['CI_MERGE_REQUEST_IID']
|
48
|
+
@strategy = klass.new(client: gitlab.api, project: project_id, mr_iid: mr_iid)
|
61
49
|
end
|
62
50
|
|
63
|
-
#Once a strategy is in place, adopt the conf methods
|
64
|
-
def method_missing(method, *args)
|
65
|
-
super unless method.to_s.start_with? 'strategy_'
|
66
|
-
if strategy.respond_to? method.to_s.delete_prefix('strategy_')
|
67
|
-
strategy.send(method.to_s.delete_prefix('strategy_'), *args)
|
68
|
-
else
|
69
|
-
super
|
70
|
-
end
|
71
|
-
end
|
72
51
|
|
73
52
|
# Call this method from the Dangerfile to assign reviewers to your merge requests
|
74
53
|
# @return The usernames list of assigned reviewes [Array<String>]
|
@@ -82,14 +61,14 @@ module Danger
|
|
82
61
|
raise "Env variable CI_PROJECT_ID doesn't point to a valid project id"
|
83
62
|
end
|
84
63
|
|
85
|
-
current_assignees = (ENV[
|
86
|
-
already_assigned_count = current_assignees.length
|
87
|
-
required_assignees_count = [assignees_amount - already_assigned_count, 0].max
|
64
|
+
current_assignees = (ENV["CI_MERGE_REQUEST_ASSIGNEES"] || "").split(",") # buggy?
|
65
|
+
# already_assigned_count = current_assignees.length
|
66
|
+
# required_assignees_count = [assignees_amount - already_assigned_count, 0].max
|
88
67
|
|
89
|
-
puts "Project ID: #{project_id}" if verbose
|
90
|
-
puts "MR IID: #{mr_iid}" if verbose
|
91
|
-
puts "Currently assigned: #{current_assignees}" if verbose
|
92
|
-
|
68
|
+
ui.puts "Project ID: #{project_id}" if verbose
|
69
|
+
ui.puts "MR IID: #{mr_iid}" if verbose
|
70
|
+
ui.puts "Currently assigned: #{current_assignees}" if verbose
|
71
|
+
# puts "Required: #{required_assignees_count}" if @verbose
|
93
72
|
|
94
73
|
# if required_assignees_count == 0
|
95
74
|
# puts "Nothing to do" if verbose
|
@@ -100,12 +79,34 @@ module Danger
|
|
100
79
|
|
101
80
|
assignees = @strategy.assign! assignees_amount
|
102
81
|
|
103
|
-
puts "Assigning: #{assignees}" if verbose
|
82
|
+
ui.puts "Assigning: #{assignees}" if verbose
|
104
83
|
return assignees
|
105
84
|
end
|
106
85
|
|
107
86
|
private
|
108
87
|
|
88
|
+
attr_reader :strategy
|
89
|
+
|
90
|
+
def project_id
|
91
|
+
ENV["CI_PROJECT_ID"]
|
92
|
+
end
|
93
|
+
|
94
|
+
def mr_iid
|
95
|
+
ENV["CI_MERGE_REQUEST_IID"]
|
96
|
+
end
|
97
|
+
|
98
|
+
# Once a strategy is in place, adopt the conf methods
|
99
|
+
def method_missing(method, *args)
|
100
|
+
super unless method.to_s.start_with? "strategy_"
|
101
|
+
if strategy.respond_to? method.to_s.delete_prefix("strategy_")
|
102
|
+
strategy.send(method.to_s.delete_prefix("strategy_"), *args)
|
103
|
+
else
|
104
|
+
super
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def respond_to_missing?(method_name, include_private = false)
|
109
|
+
method_name.to_s.start_with?("strategy_") || super
|
110
|
+
end
|
109
111
|
end
|
110
112
|
end
|
111
|
-
|
@@ -1,8 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Danger
|
2
4
|
module AssignStrategies
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
5
|
+
require "gitlab_reviewbot/strategies/strategy"
|
6
|
+
require "gitlab_reviewbot/strategies/random"
|
7
|
+
require "gitlab_reviewbot/strategies/least_busy"
|
6
8
|
end
|
7
9
|
end
|
8
|
-
|
@@ -1,21 +1,21 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "gitlab_reviewbot/gitlab"
|
2
4
|
|
3
5
|
module Danger
|
4
6
|
module AssignStrategies
|
5
7
|
class LeastBusyStrategy < Strategy
|
6
8
|
def assignees(amount)
|
7
|
-
|
8
|
-
|
9
|
-
invalid_assignees = [
|
9
|
+
# This doesn't fetch the review count for the users so we need to fetch it separately later
|
10
|
+
users_in_group = fetch_users_in_group
|
11
|
+
invalid_assignees = [fetch_author] + fetch_assigned_reviewers
|
10
12
|
|
11
|
-
|
12
|
-
|
13
|
+
group_users_with_reviews_pending = client.users_with_pending_mr_review(project_id).filter { |u| users_in_group.include? u }
|
14
|
+
group_users_without_reviews_pending = users_in_group.filter { |u| !group_users_with_reviews_pending.include? u }
|
13
15
|
|
14
|
-
(
|
15
|
-
|
16
|
-
.first(amount)
|
16
|
+
candidates = (group_users_with_reviews_pending + group_users_without_reviews_pending).filter { |u| !invalid_assignees.include? u }
|
17
|
+
candidates.shuffle.sort_by(&:review_count).first(amount)
|
17
18
|
end
|
18
19
|
end
|
19
20
|
end
|
20
21
|
end
|
21
|
-
|
@@ -1,12 +1,14 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "gitlab_reviewbot/gitlab"
|
2
4
|
|
3
5
|
module Danger
|
4
6
|
module AssignStrategies
|
5
7
|
class RandomStrategy < Strategy
|
6
8
|
def assignees(amount)
|
7
|
-
invalid_assignees = [
|
8
|
-
fetch_users_in_group.filter { |u| !
|
9
|
-
|
9
|
+
invalid_assignees = [fetch_author] + fetch_assigned_reviewers
|
10
|
+
fetch_users_in_group.filter { |u| !invalid_assignees.include? u }
|
11
|
+
.sample(amount)
|
10
12
|
end
|
11
13
|
end
|
12
14
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Danger
|
2
4
|
module AssignStrategies
|
3
5
|
class Strategy
|
@@ -7,21 +9,21 @@ module Danger
|
|
7
9
|
attr_accessor :client
|
8
10
|
attr_accessor :excluded_users
|
9
11
|
|
10
|
-
def initialize(client:, project:,
|
12
|
+
def initialize(client:, project:, mr_iid:)
|
11
13
|
@client = client
|
12
14
|
@project_id = project
|
13
|
-
@mr_iid =
|
15
|
+
@mr_iid = mr_iid
|
14
16
|
@excluded_users = []
|
15
17
|
end
|
16
18
|
|
17
19
|
def assign!(amount)
|
18
|
-
currently_assigned = fetch_assigned_reviewers
|
20
|
+
currently_assigned = fetch_assigned_reviewers
|
19
21
|
return [] if (amount - currently_assigned.length) <= 0
|
20
22
|
|
21
23
|
to_be_assigned = assignees(amount - currently_assigned.length)
|
22
24
|
all_assignees = currently_assigned + to_be_assigned
|
23
25
|
|
24
|
-
|
26
|
+
client.assign_mr_to_users(project_id, mr_iid, all_assignees)
|
25
27
|
all_assignees.map(&:username)
|
26
28
|
end
|
27
29
|
|
@@ -38,11 +40,13 @@ module Danger
|
|
38
40
|
end
|
39
41
|
|
40
42
|
def fetch_users_in_group
|
41
|
-
excluded = @excluded_users.map
|
42
|
-
|
43
|
+
excluded = @excluded_users.map do |u|
|
44
|
+
server_user = client.find_user_with_username(u).first
|
45
|
+
raise "ERROR: Invalid username #{u} among excluded_users" if server_user.nil?
|
46
|
+
server_user
|
47
|
+
end
|
48
|
+
client.fetch_users_for_group(@group_name).filter { |u| !excluded.include? u }
|
43
49
|
end
|
44
|
-
|
45
50
|
end
|
46
51
|
end
|
47
52
|
end
|
48
|
-
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require File.expand_path("spec_helper", __dir__)
|
4
|
+
|
5
|
+
|
6
|
+
module Danger
|
7
|
+
describe Gitlab::Client do
|
8
|
+
before do
|
9
|
+
#
|
10
|
+
# @sam = Gitlab::User.new(1, "Sam")
|
11
|
+
# @tom = Gitlab::User.new(2, "Tom")
|
12
|
+
# @nic = Gitlab::User.new(3, "Nic")
|
13
|
+
# @luke = Gitlab::User.new(4, "Luke")
|
14
|
+
@mock_client = double(Gitlab::Client)
|
15
|
+
@client_helper = Gitlab::ClientHelper.new(@mock_client)
|
16
|
+
@sam = Gitlab::User.new(1, "Sam")
|
17
|
+
@tom = Gitlab::User.new(2, "Tom")
|
18
|
+
@nic = Gitlab::User.new(3, "Nic")
|
19
|
+
@assignees = [{'id' => 1, 'username' => "Sam"},
|
20
|
+
{'id' => 2, 'username' => "Tom"},
|
21
|
+
{'id' => 3, 'username' => "Nic"}]
|
22
|
+
end
|
23
|
+
|
24
|
+
it "Correctly calculates users with pending reviews" do
|
25
|
+
already_reviewed = [@assignees[0]]
|
26
|
+
|
27
|
+
allow(@mock_client).to receive(:merge_requests).with(10, {:per_page=>"100", :state=>"opened"}).and_return([Gitlab::ObjectifiedHash.new({'id' => 110, 'assignees' => @assignees})])
|
28
|
+
allow(@mock_client).to receive(:merge_request_approval_state).with(10, 110).and_return(Gitlab::ObjectifiedHash.new({'rules' => [{'approved_by' => already_reviewed}]}))
|
29
|
+
|
30
|
+
expect(@client_helper.users_with_pending_mr_review(10)).to be == [@tom, @nic]
|
31
|
+
end
|
32
|
+
|
33
|
+
it "Correctly calculates users with pending reviews (multi rule)" do
|
34
|
+
allow(@mock_client).to receive(:merge_requests).with(10, {:per_page=>"100", :state=>"opened"}).and_return([Gitlab::ObjectifiedHash.new({'id' => 110, 'assignees' => @assignees})])
|
35
|
+
allow(@mock_client).to receive(:merge_request_approval_state).with(10, 110).and_return(Gitlab::ObjectifiedHash.new({'rules' => [{'approved_by' => [{'id' => 1, 'username' => "Sam"}]}, {'approved_by' => [{'id' => 2, 'username' => "Tom"}]}]}))
|
36
|
+
|
37
|
+
expect(@client_helper.users_with_pending_mr_review(10)).to be == [@nic]
|
38
|
+
end
|
39
|
+
|
40
|
+
it "Fetches only active developer users for group" do
|
41
|
+
active_users = [Gitlab::ObjectifiedHash.new({'id' => 1, 'username' => 'Sam', 'state' => 'active', 'access_level' => 30}),
|
42
|
+
Gitlab::ObjectifiedHash.new({'id' => 2, 'username' => 'Tom', 'state' => 'active', 'access_level' => 50})]
|
43
|
+
inactive_users = [Gitlab::ObjectifiedHash.new({'id' => 3, 'username' => 'OldSam', 'state' => 'inactive', 'access_level' => 30})]
|
44
|
+
non_developer_users = [Gitlab::ObjectifiedHash.new({'id' => 4, 'username' => 'Reporter', 'state' => 'active', 'access_level' => 20})]
|
45
|
+
allow(@mock_client).to receive(:group_search).with("mygroup").and_return([Gitlab::ObjectifiedHash.new({'id' => 10, 'full_path' => "mygroup"})])
|
46
|
+
allow(@mock_client).to receive(:group_members).with(10).and_return(active_users + inactive_users + non_developer_users)
|
47
|
+
expect(@client_helper.fetch_users_for_group("mygroup")).to be == [@sam, @tom]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require File.expand_path("spec_helper", __dir__)
|
2
4
|
|
3
5
|
module Danger
|
4
6
|
describe Danger::DangerGitlabReviewbot do
|
@@ -11,25 +13,24 @@ module Danger
|
|
11
13
|
#
|
12
14
|
describe "with Dangerfile" do
|
13
15
|
before do
|
14
|
-
testing_env.each { |k,v| ENV[k] =
|
16
|
+
testing_env.each { |k, v| ENV[k] = v.to_s }
|
15
17
|
|
16
18
|
@dangerfile = testing_dangerfile
|
17
19
|
@plugin = @dangerfile.gitlab_reviewbot
|
18
20
|
@strategy_mock = instance_double(Danger::AssignStrategies::Strategy)
|
19
21
|
allow(Danger::AssignStrategies::RandomStrategy).to receive(:new).and_return(@strategy_mock)
|
20
|
-
allow(@strategy_mock).to receive(:group_name=).with(
|
22
|
+
allow(@strategy_mock).to receive(:group_name=).with("tech/ios")
|
21
23
|
@plugin.strategy = Danger::AssignStrategies::RandomStrategy
|
22
|
-
@plugin.gitlab_group =
|
23
|
-
|
24
|
+
@plugin.gitlab_group = "tech/ios"
|
24
25
|
end
|
25
26
|
|
26
27
|
it "Assign one reviewer" do
|
27
|
-
expect(@strategy_mock).to receive(:assign!).with(1).and_return([
|
28
|
+
expect(@strategy_mock).to receive(:assign!).with(1).and_return(["Sam"])
|
28
29
|
|
29
30
|
@plugin.assign!
|
30
31
|
end
|
31
32
|
it "Assign one reviewer" do
|
32
|
-
expect(@strategy_mock).to receive(:assign!).with(1).and_return([
|
33
|
+
expect(@strategy_mock).to receive(:assign!).with(1).and_return(["Sam"])
|
33
34
|
|
34
35
|
@plugin.assign!
|
35
36
|
end
|
@@ -37,7 +38,7 @@ module Danger
|
|
37
38
|
it "Assign multiple reviewers" do
|
38
39
|
@plugin.assignees_amount = 2
|
39
40
|
|
40
|
-
expect(@strategy_mock).to receive(:assign!).with(2).and_return([
|
41
|
+
expect(@strategy_mock).to receive(:assign!).with(2).and_return(["Sam, Nic"])
|
41
42
|
|
42
43
|
@plugin.assign!
|
43
44
|
end
|
@@ -46,18 +47,16 @@ module Danger
|
|
46
47
|
expect(@strategy_mock).to receive(:excluded_users=)
|
47
48
|
expect(@strategy_mock).to receive(:excluded_users).and_return([])
|
48
49
|
|
49
|
-
@plugin.strategy_excluded_users = [
|
50
|
-
@plugin.strategy_excluded_users <<
|
51
|
-
|
50
|
+
@plugin.strategy_excluded_users = ["Tom"]
|
51
|
+
@plugin.strategy_excluded_users << "Sam"
|
52
52
|
end
|
53
53
|
|
54
|
-
[
|
54
|
+
["CI_PROJECT_ID", "CI_MERGE_REQUEST_IID"].each do |var|
|
55
55
|
it "Fails when required #{var} variables are not available" do
|
56
56
|
ENV[var] = nil
|
57
|
-
expect{@plugin.assign!}.to raise_error(RuntimeError)
|
57
|
+
expect { @plugin.assign! }.to raise_error(RuntimeError)
|
58
58
|
end
|
59
59
|
end
|
60
60
|
end
|
61
61
|
end
|
62
62
|
end
|
63
|
-
|
@@ -1,15 +1,17 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require File.expand_path("spec_helper", __dir__)
|
2
4
|
|
3
5
|
module Danger
|
4
6
|
describe Danger::AssignStrategies::LeastBusyStrategy do
|
5
7
|
before do
|
6
|
-
testing_env.each { |k,v| ENV[k] =
|
8
|
+
testing_env.each { |k, v| ENV[k] = v.to_s }
|
7
9
|
@dangerfile = testing_dangerfile
|
8
10
|
|
9
|
-
@sam = Gitlab::User.new(1,
|
10
|
-
@tom = Gitlab::User.new(2,
|
11
|
-
@nic = Gitlab::User.new(3,
|
12
|
-
@luke = Gitlab::User.new(4,
|
11
|
+
@sam = Gitlab::User.new(1, "Sam")
|
12
|
+
@tom = Gitlab::User.new(2, "Tom")
|
13
|
+
@nic = Gitlab::User.new(3, "Nic")
|
14
|
+
@luke = Gitlab::User.new(4, "Luke")
|
13
15
|
|
14
16
|
@mock_client = double(Gitlab::Client)
|
15
17
|
@author = @nic
|
@@ -18,9 +20,8 @@ module Danger
|
|
18
20
|
allow(@mock_client).to receive(:fetch_author_for_mr).and_return(@author)
|
19
21
|
allow(@mock_client).to receive(:fetch_users_for_group).with("tech/ios").and_return(@members)
|
20
22
|
|
21
|
-
@strategy = AssignStrategies::LeastBusyStrategy.new(client: @mock_client, project: 10,
|
23
|
+
@strategy = AssignStrategies::LeastBusyStrategy.new(client: @mock_client, project: 10, mr_iid: 110)
|
22
24
|
@strategy.group_name = "tech/ios"
|
23
|
-
|
24
25
|
end
|
25
26
|
|
26
27
|
it "Assign the one least busy" do
|
@@ -92,16 +93,17 @@ module Danger
|
|
92
93
|
@strategy.assign!(1)
|
93
94
|
end
|
94
95
|
|
96
|
+
# TODO: This should go up to the superclass for testing
|
95
97
|
it "honours excluded users" do
|
96
98
|
allow(@mock_client).to receive(:fetch_mr_reviewers).with(10, 110).and_return([])
|
97
|
-
@tom.review_count =
|
99
|
+
@tom.review_count = 0
|
98
100
|
@sam.review_count = 4
|
99
101
|
@luke.review_count = 3
|
100
|
-
users_with_pending_mr_review = [@author, @sam, @
|
102
|
+
users_with_pending_mr_review = [@author, @sam, @luke]
|
101
103
|
expect(@mock_client).to receive(:users_with_pending_mr_review).and_return(users_with_pending_mr_review)
|
102
104
|
|
103
|
-
allow(@mock_client).to receive(:find_user_with_username).with(
|
104
|
-
@strategy.excluded_users = [
|
105
|
+
allow(@mock_client).to receive(:find_user_with_username).with("Tom").and_return([@tom])
|
106
|
+
@strategy.excluded_users = ["Tom"]
|
105
107
|
|
106
108
|
expect(@mock_client).to receive(:assign_mr_to_users) do |project, mr, users|
|
107
109
|
expect(project).to be == 10
|
@@ -112,6 +114,22 @@ module Danger
|
|
112
114
|
@strategy.assign!(1)
|
113
115
|
end
|
114
116
|
|
117
|
+
it "Raises error when excluded_user is invalid" do
|
118
|
+
allow(@mock_client).to receive(:fetch_mr_reviewers).with(10, 110).and_return([])
|
119
|
+
|
120
|
+
allow(@mock_client).to receive(:find_user_with_username).with("WrongTom").and_return([])
|
121
|
+
@strategy.excluded_users = ["WrongTom"]
|
122
|
+
|
123
|
+
expect { @strategy.assign!(1) }.to raise_error("ERROR: Invalid username WrongTom among excluded_users")
|
124
|
+
end
|
125
|
+
|
126
|
+
# it "test" do
|
127
|
+
# strategy = AssignStrategies::LeastBusyStrategy.new(client: @dangerfile.gitlab.api, project: 346, mr_iid: 960)
|
128
|
+
# strategy.excluded_users << 'fabio.gallonetto'
|
129
|
+
# strategy.group_name = 'tech/ios'
|
130
|
+
# require 'pry'
|
131
|
+
# binding.pry
|
132
|
+
# puts strategy.assign!(2)
|
133
|
+
# end
|
115
134
|
end
|
116
135
|
end
|
117
|
-
|
@@ -1,17 +1,18 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require File.expand_path("spec_helper", __dir__)
|
2
4
|
|
3
5
|
module Danger
|
4
6
|
describe Danger::AssignStrategies::RandomStrategy do
|
5
7
|
before do
|
6
|
-
testing_env.each { |k,v| ENV[k] =
|
8
|
+
testing_env.each { |k, v| ENV[k] = v.to_s }
|
7
9
|
@dangerfile = testing_dangerfile
|
8
10
|
|
9
|
-
@sam = Gitlab::User.new(1,
|
10
|
-
@tom = Gitlab::User.new(2,
|
11
|
-
@nic = Gitlab::User.new(3,
|
12
|
-
@luke = Gitlab::User.new(4,
|
13
|
-
@lei = Gitlab::User.new(5,
|
14
|
-
|
11
|
+
@sam = Gitlab::User.new(1, "Sam")
|
12
|
+
@tom = Gitlab::User.new(2, "Tom")
|
13
|
+
@nic = Gitlab::User.new(3, "Nic")
|
14
|
+
@luke = Gitlab::User.new(4, "Luke")
|
15
|
+
@lei = Gitlab::User.new(5, "Lei")
|
15
16
|
|
16
17
|
@mock_client = double(Gitlab::Client)
|
17
18
|
@author = @lei
|
@@ -19,7 +20,7 @@ module Danger
|
|
19
20
|
allow(@mock_client).to receive(:fetch_author_for_mr).and_return(@author)
|
20
21
|
allow(@mock_client).to receive(:fetch_users_for_group).with("tech/ios").and_return(@members)
|
21
22
|
|
22
|
-
@strategy = AssignStrategies::RandomStrategy.new(client: @mock_client, project: 10,
|
23
|
+
@strategy = AssignStrategies::RandomStrategy.new(client: @mock_client, project: 10, mr_iid: 110)
|
23
24
|
@strategy.group_name = "tech/ios"
|
24
25
|
end
|
25
26
|
|
@@ -56,7 +57,5 @@ module Danger
|
|
56
57
|
|
57
58
|
@strategy.assign!(2)
|
58
59
|
end
|
59
|
-
|
60
60
|
end
|
61
61
|
end
|
62
|
-
|
data/spec/spec_helper.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "pathname"
|
2
|
-
ROOT = Pathname.new(File.expand_path("
|
4
|
+
ROOT = Pathname.new(File.expand_path("..", __dir__))
|
3
5
|
$:.unshift((ROOT + "lib").to_s)
|
4
6
|
$:.unshift((ROOT + "spec").to_s)
|
5
7
|
|
@@ -8,8 +10,9 @@ require "pry"
|
|
8
10
|
|
9
11
|
require "rspec"
|
10
12
|
require "danger"
|
13
|
+
require File.expand_path("../lib/gitlab_reviewbot/gitlab", __dir__)
|
11
14
|
|
12
|
-
if `git remote -v` ==
|
15
|
+
if `git remote -v` == ""
|
13
16
|
puts "You cannot run tests without setting a local git remote on this repo"
|
14
17
|
puts "It's a weird side-effect of Danger's internals."
|
15
18
|
exit(0)
|
@@ -50,12 +53,12 @@ end
|
|
50
53
|
# running a PR on TravisCI
|
51
54
|
def testing_env
|
52
55
|
{
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
56
|
+
"CI_MERGE_REQUEST_IID" => "549",
|
57
|
+
"CI_MERGE_REQUEST_PROJECT_PATH" => "...",
|
58
|
+
"CI_MERGE_REQUEST_PROJECT_URL" => "...",
|
59
|
+
"DANGER_GITLAB_HOST" => "git.curve.tools", # This needs to be the same as where the repo is stored due to Danger internals :facepalm:
|
60
|
+
"CI_API_V4_URL" => "https://git.curve.tools/api/v4",
|
61
|
+
"CI_PROJECT_ID" => "346",
|
59
62
|
"GITLAB_CI" => true,
|
60
63
|
"DANGER_GITLAB_API_TOKEN" => "token-token-token"
|
61
64
|
}
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: danger-gitlab_reviewbot
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
4
|
+
version: 1.1.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Fabio Gallonetto
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-08-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: danger-plugin-api
|
@@ -200,6 +200,7 @@ extensions: []
|
|
200
200
|
extra_rdoc_files: []
|
201
201
|
files:
|
202
202
|
- ".gitignore"
|
203
|
+
- ".gitlab-ci.yml"
|
203
204
|
- ".rubocop.yml"
|
204
205
|
- Gemfile
|
205
206
|
- Gemfile.lock
|
@@ -217,11 +218,12 @@ files:
|
|
217
218
|
- lib/gitlab_reviewbot/strategies/least_busy.rb
|
218
219
|
- lib/gitlab_reviewbot/strategies/random.rb
|
219
220
|
- lib/gitlab_reviewbot/strategies/strategy.rb
|
221
|
+
- spec/gitlab_ext_spec.rb
|
220
222
|
- spec/gitlab_reviewbot_spec.rb
|
221
223
|
- spec/least_busy_strategy_spec.rb
|
222
224
|
- spec/random_strategy_spec.rb
|
223
225
|
- spec/spec_helper.rb
|
224
|
-
homepage: https://
|
226
|
+
homepage: https://git.curve.tools/fabio.gallonetto/danger-gitlab_reviewbot
|
225
227
|
licenses:
|
226
228
|
- MIT
|
227
229
|
metadata: {}
|
@@ -245,6 +247,7 @@ signing_key:
|
|
245
247
|
specification_version: 4
|
246
248
|
summary: A review raffle bot for Gitlab.
|
247
249
|
test_files:
|
250
|
+
- spec/gitlab_ext_spec.rb
|
248
251
|
- spec/gitlab_reviewbot_spec.rb
|
249
252
|
- spec/least_busy_strategy_spec.rb
|
250
253
|
- spec/random_strategy_spec.rb
|