gitlab-triage 0.14.1 → 0.15.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 +69 -24
- data/bin/gitlab-triage +2 -74
- data/lib/gitlab/triage/action/base.rb +3 -3
- data/lib/gitlab/triage/action/comment.rb +5 -7
- data/lib/gitlab/triage/action/summarize.rb +4 -5
- data/lib/gitlab/triage/command_builders/base_command_builder.rb +1 -1
- data/lib/gitlab/triage/command_builders/text_content_builder.rb +54 -16
- data/lib/gitlab/triage/engine.rb +27 -34
- data/lib/gitlab/triage/entity_builders/issue_builder.rb +27 -17
- data/lib/gitlab/triage/filters/member_conditions_filter.rb +6 -8
- data/lib/gitlab/triage/filters/ruby_conditions_filter.rb +5 -3
- data/lib/gitlab/triage/network.rb +20 -8
- data/lib/gitlab/triage/network_adapters/base_adapter.rb +1 -1
- data/lib/gitlab/triage/option_parser.rb +72 -0
- data/lib/gitlab/triage/options.rb +21 -0
- data/lib/gitlab/triage/policies/base_policy.rb +21 -2
- data/lib/gitlab/triage/policies/rule_policy.rb +7 -1
- data/lib/gitlab/triage/policies/summary_policy.rb +25 -2
- data/lib/gitlab/triage/resource/base.rb +49 -10
- data/lib/gitlab/triage/resource/context.rb +13 -8
- data/lib/gitlab/triage/resource/instance_version.rb +3 -4
- data/lib/gitlab/triage/resource/issue.rb +14 -0
- data/lib/gitlab/triage/resource/label.rb +41 -0
- data/lib/gitlab/triage/resource/label_event.rb +45 -0
- data/lib/gitlab/triage/resource/merge_request.rb +14 -0
- data/lib/gitlab/triage/resource/milestone.rb +5 -5
- data/lib/gitlab/triage/resource/shared/issuable.rb +54 -0
- data/lib/gitlab/triage/url_builders/url_builder.rb +3 -2
- data/lib/gitlab/triage/version.rb +1 -1
- data/support/.triage-policies.example.yml +4 -4
- metadata +10 -4
@@ -9,7 +9,13 @@ module Gitlab
|
|
9
9
|
class RulePolicy < BasePolicy
|
10
10
|
# Build an issue from a single rule policy
|
11
11
|
def build_issue
|
12
|
-
|
12
|
+
action = actions.fetch(:summarize, {})
|
13
|
+
|
14
|
+
EntityBuilders::IssueBuilder.new(
|
15
|
+
type: type,
|
16
|
+
action: action,
|
17
|
+
resources: resources,
|
18
|
+
network: network)
|
13
19
|
end
|
14
20
|
end
|
15
21
|
end
|
@@ -10,13 +10,36 @@ module Gitlab
|
|
10
10
|
class SummaryPolicy < BasePolicy
|
11
11
|
# Build an issue from several rules policies
|
12
12
|
def build_issue
|
13
|
-
|
13
|
+
action = actions[:summarize]
|
14
|
+
|
15
|
+
EntityBuilders::IssueBuilder.new(
|
16
|
+
type: type,
|
17
|
+
action: action,
|
18
|
+
resources: [],
|
19
|
+
network: network).tap do |issue|
|
14
20
|
issue.items =
|
15
21
|
resources.map do |inner_policy_spec, inner_resources|
|
16
|
-
Policies::RulePolicy.new(
|
22
|
+
Policies::RulePolicy.new(
|
23
|
+
type, inner_policy_spec, inner_resources, network)
|
24
|
+
.build_issue
|
25
|
+
.description
|
17
26
|
end.join("\n\n")
|
18
27
|
end
|
19
28
|
end
|
29
|
+
|
30
|
+
# Due to resources is a different type, this will never work
|
31
|
+
# FIXME: We should try to make sure type is consistent for resources
|
32
|
+
def comment?
|
33
|
+
false
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def attach_resource_type(resources, type)
|
39
|
+
resources.each_with_object({}) do |(rule, rule_resources), result|
|
40
|
+
result[rule] = super(rule_resources, type)
|
41
|
+
end
|
42
|
+
end
|
20
43
|
end
|
21
44
|
end
|
22
45
|
end
|
@@ -1,32 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative '../url_builders/url_builder'
|
2
4
|
|
3
5
|
module Gitlab
|
4
6
|
module Triage
|
5
7
|
module Resource
|
6
8
|
class Base
|
7
|
-
attr_reader :resource, :
|
9
|
+
attr_reader :resource, :parent
|
10
|
+
|
11
|
+
CONFIDENTIAL_TEXT = '(confidential)'
|
8
12
|
|
9
|
-
def
|
10
|
-
|
11
|
-
|
13
|
+
def self.define_field(name, &block)
|
14
|
+
define_method(name) do
|
15
|
+
if redact_confidential_attributes?
|
16
|
+
CONFIDENTIAL_TEXT
|
17
|
+
else
|
18
|
+
instance_eval(&block)
|
19
|
+
end
|
20
|
+
end
|
12
21
|
end
|
13
22
|
|
14
|
-
|
23
|
+
def initialize(
|
24
|
+
resource, parent: nil, network: nil, redact_confidentials: true)
|
25
|
+
@resource = resource
|
26
|
+
@parent = parent
|
27
|
+
@network = network
|
28
|
+
@redact_confidentials = redact_confidentials
|
29
|
+
end
|
30
|
+
|
31
|
+
protected
|
32
|
+
|
33
|
+
def redact_confidential_attributes?
|
34
|
+
parent&.redact_confidential_attributes? ||
|
35
|
+
(@redact_confidentials && resource[:confidential])
|
36
|
+
end
|
15
37
|
|
16
38
|
def network
|
17
|
-
|
39
|
+
parent&.network || @network
|
18
40
|
end
|
19
41
|
|
42
|
+
private
|
43
|
+
|
20
44
|
def url(params = {})
|
45
|
+
build_url(params: params)
|
46
|
+
end
|
47
|
+
|
48
|
+
def resource_url(params: {}, sub_resource_type: nil)
|
49
|
+
build_url(
|
50
|
+
params: params,
|
51
|
+
options: {
|
52
|
+
resource_id: resource[:iid],
|
53
|
+
sub_resource_type: sub_resource_type
|
54
|
+
}
|
55
|
+
)
|
56
|
+
end
|
57
|
+
|
58
|
+
def build_url(params: {}, options: {})
|
21
59
|
UrlBuilders::UrlBuilder.new(
|
22
|
-
|
60
|
+
url_opts
|
61
|
+
.merge(options)
|
62
|
+
.merge(params: { per_page: 100 }.merge(params))
|
23
63
|
).build
|
24
64
|
end
|
25
65
|
|
26
|
-
def
|
66
|
+
def url_opts
|
27
67
|
{
|
28
|
-
|
29
|
-
api_version: net[:api_version],
|
68
|
+
network_options: network.options,
|
30
69
|
resource_type: self.class.name.demodulize.underscore.pluralize,
|
31
70
|
source: source,
|
32
71
|
source_id: resource[:"#{source.singularize}_id"]
|
@@ -1,13 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative 'base'
|
2
|
-
require_relative '
|
4
|
+
require_relative 'issue'
|
5
|
+
require_relative 'merge_request'
|
3
6
|
require_relative 'instance_version'
|
4
7
|
|
5
8
|
module Gitlab
|
6
9
|
module Triage
|
7
10
|
module Resource
|
8
|
-
|
11
|
+
module Context
|
9
12
|
EvaluationError = Class.new(RuntimeError)
|
10
13
|
|
14
|
+
def self.build(resource, **options)
|
15
|
+
const_name = (resource[:type] || 'Base')
|
16
|
+
.to_s.singularize.camelcase
|
17
|
+
|
18
|
+
Resource.const_get(const_name).new(resource, **options).extend(self)
|
19
|
+
end
|
20
|
+
|
11
21
|
def eval(ruby)
|
12
22
|
instance_eval <<~RUBY
|
13
23
|
begin
|
@@ -26,12 +36,7 @@ module Gitlab
|
|
26
36
|
private
|
27
37
|
|
28
38
|
def instance_version
|
29
|
-
@instance_version ||= InstanceVersion.new(
|
30
|
-
end
|
31
|
-
|
32
|
-
def milestone
|
33
|
-
@milestone ||=
|
34
|
-
resource[:milestone] && Milestone.new(resource[:milestone], net)
|
39
|
+
@instance_version ||= InstanceVersion.new(parent: self)
|
35
40
|
end
|
36
41
|
end
|
37
42
|
end
|
@@ -4,8 +4,8 @@ module Gitlab
|
|
4
4
|
module Triage
|
5
5
|
module Resource
|
6
6
|
class InstanceVersion < Base
|
7
|
-
def initialize(
|
8
|
-
super({},
|
7
|
+
def initialize(**options)
|
8
|
+
super({}, options)
|
9
9
|
end
|
10
10
|
|
11
11
|
def version
|
@@ -26,8 +26,7 @@ module Gitlab
|
|
26
26
|
def response
|
27
27
|
@response ||=
|
28
28
|
network.query_api_cached(
|
29
|
-
|
30
|
-
"#{net[:host_url]}/api/#{net[:api_version]}/version")
|
29
|
+
"#{network.options.host_url}/api/#{network.options.api_version}/version")
|
31
30
|
.first
|
32
31
|
end
|
33
32
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'base'
|
4
|
+
require 'date'
|
5
|
+
require 'time'
|
6
|
+
|
7
|
+
module Gitlab
|
8
|
+
module Triage
|
9
|
+
module Resource
|
10
|
+
class Label < Base
|
11
|
+
FIELDS = %i[
|
12
|
+
id
|
13
|
+
project_id
|
14
|
+
group_id
|
15
|
+
name
|
16
|
+
description
|
17
|
+
color
|
18
|
+
priority
|
19
|
+
].freeze
|
20
|
+
|
21
|
+
TIME_FIELDS = %i[
|
22
|
+
added_at
|
23
|
+
].freeze
|
24
|
+
|
25
|
+
FIELDS.each do |field|
|
26
|
+
define_field(field) do
|
27
|
+
resource[field]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
TIME_FIELDS.each do |field|
|
32
|
+
define_field(field) do
|
33
|
+
value = resource[field]
|
34
|
+
|
35
|
+
Time.parse(value) if value
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'base'
|
4
|
+
require_relative 'label'
|
5
|
+
require 'date'
|
6
|
+
require 'time'
|
7
|
+
|
8
|
+
module Gitlab
|
9
|
+
module Triage
|
10
|
+
module Resource
|
11
|
+
class LabelEvent < Base
|
12
|
+
FIELDS = %i[
|
13
|
+
id
|
14
|
+
resource_type
|
15
|
+
resource_id
|
16
|
+
action
|
17
|
+
].freeze
|
18
|
+
|
19
|
+
TIME_FIELDS = %i[
|
20
|
+
created_at
|
21
|
+
].freeze
|
22
|
+
|
23
|
+
FIELDS.each do |field|
|
24
|
+
define_field(field) do
|
25
|
+
resource[field]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
TIME_FIELDS.each do |field|
|
30
|
+
define_field(field) do
|
31
|
+
value = resource[field]
|
32
|
+
|
33
|
+
Time.parse(value) if value
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def label
|
38
|
+
@label ||= Label.new(
|
39
|
+
resource[:label].reverse_merge(added_at: resource[:created_at]),
|
40
|
+
parent: self)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -27,13 +27,13 @@ module Gitlab
|
|
27
27
|
].freeze
|
28
28
|
|
29
29
|
FIELDS.each do |field|
|
30
|
-
|
30
|
+
define_field(field) do
|
31
31
|
resource[field]
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
35
|
DATE_FIELDS.each do |field|
|
36
|
-
|
36
|
+
define_field(field) do
|
37
37
|
value = resource[field]
|
38
38
|
|
39
39
|
Date.parse(value) if value
|
@@ -41,7 +41,7 @@ module Gitlab
|
|
41
41
|
end
|
42
42
|
|
43
43
|
TIME_FIELDS.each do |field|
|
44
|
-
|
44
|
+
define_field(field) do
|
45
45
|
value = resource[field]
|
46
46
|
|
47
47
|
Time.parse(value) if value
|
@@ -73,8 +73,8 @@ module Gitlab
|
|
73
73
|
def all_active
|
74
74
|
@all_active ||=
|
75
75
|
network
|
76
|
-
.query_api_cached(
|
77
|
-
.map { |milestone| self.class.new(milestone,
|
76
|
+
.query_api_cached(url(state: 'active'))
|
77
|
+
.map { |milestone| self.class.new(milestone, parent: self) }
|
78
78
|
end
|
79
79
|
end
|
80
80
|
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../label'
|
4
|
+
require_relative '../label_event'
|
5
|
+
require_relative '../milestone'
|
6
|
+
|
7
|
+
module Gitlab
|
8
|
+
module Triage
|
9
|
+
module Resource
|
10
|
+
module Shared
|
11
|
+
module Issuable
|
12
|
+
def milestone
|
13
|
+
@milestone ||=
|
14
|
+
resource[:milestone] &&
|
15
|
+
Milestone.new(resource[:milestone], parent: self)
|
16
|
+
end
|
17
|
+
|
18
|
+
# This will be more useful when we have:
|
19
|
+
# https://gitlab.com/gitlab-org/gitlab-ce/issues/51011
|
20
|
+
def labels
|
21
|
+
@labels ||= resource[:labels] # an array of label names
|
22
|
+
.map { |label| Label.new({ name: label }, parent: self) }
|
23
|
+
end
|
24
|
+
|
25
|
+
# Make this an alias of `labels` when we have:
|
26
|
+
# https://gitlab.com/gitlab-org/gitlab-ce/issues/51011
|
27
|
+
def labels_with_details
|
28
|
+
@labels_with_details ||= label_events
|
29
|
+
.select { |event| event.action == 'add' }
|
30
|
+
.map(&:label)
|
31
|
+
.uniq(&:name)
|
32
|
+
.select { |label| resource[:labels].include?(label.name) }
|
33
|
+
end
|
34
|
+
|
35
|
+
def label_events
|
36
|
+
@label_events ||= query_label_events
|
37
|
+
.map { |label_event| LabelEvent.new(label_event, parent: self) }
|
38
|
+
end
|
39
|
+
|
40
|
+
def labels_chronologically
|
41
|
+
@labels_chronologically ||= labels_with_details.sort_by(&:added_at)
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def query_label_events
|
47
|
+
network.query_api_cached(
|
48
|
+
resource_url(sub_resource_type: 'resource_label_events'))
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -3,8 +3,9 @@ module Gitlab
|
|
3
3
|
module UrlBuilders
|
4
4
|
class UrlBuilder
|
5
5
|
def initialize(options)
|
6
|
-
@
|
7
|
-
@
|
6
|
+
@network_options = options.fetch(:network_options)
|
7
|
+
@host_url = @network_options.host_url
|
8
|
+
@api_version = @network_options.api_version
|
8
9
|
@source = options.fetch(:source, 'projects')
|
9
10
|
@source_id = options.fetch(:source_id)
|
10
11
|
@resource_type = options.fetch(:resource_type)
|
@@ -18,9 +18,9 @@ resource_rules:
|
|
18
18
|
- name: Newest and oldest issues summary
|
19
19
|
actions:
|
20
20
|
summarize:
|
21
|
-
title: "Newest and oldest
|
21
|
+
title: "Newest and oldest {{type}} summary"
|
22
22
|
summary: |
|
23
|
-
Please triage the following
|
23
|
+
Please triage the following {{type}}:
|
24
24
|
|
25
25
|
{{items}}
|
26
26
|
|
@@ -36,7 +36,7 @@ resource_rules:
|
|
36
36
|
actions:
|
37
37
|
summarize:
|
38
38
|
item: "- [ ] [{{title}}]({{web_url}}) {{labels}}"
|
39
|
-
summary: "Please triage the following new
|
39
|
+
summary: "Please triage the following new {{type}}:\n\n{{items}}"
|
40
40
|
- name: Old issues
|
41
41
|
conditions:
|
42
42
|
state: opened
|
@@ -45,7 +45,7 @@ resource_rules:
|
|
45
45
|
actions:
|
46
46
|
summarize:
|
47
47
|
item: "- [ ] [{{title}}]({{web_url}}) {{labels}}"
|
48
|
-
summary: "Please triage the following old
|
48
|
+
summary: "Please triage the following old {{type}}:\n\n{{items}}"
|
49
49
|
merge_requests:
|
50
50
|
rules:
|
51
51
|
[]
|