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.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +69 -24
  3. data/bin/gitlab-triage +2 -74
  4. data/lib/gitlab/triage/action/base.rb +3 -3
  5. data/lib/gitlab/triage/action/comment.rb +5 -7
  6. data/lib/gitlab/triage/action/summarize.rb +4 -5
  7. data/lib/gitlab/triage/command_builders/base_command_builder.rb +1 -1
  8. data/lib/gitlab/triage/command_builders/text_content_builder.rb +54 -16
  9. data/lib/gitlab/triage/engine.rb +27 -34
  10. data/lib/gitlab/triage/entity_builders/issue_builder.rb +27 -17
  11. data/lib/gitlab/triage/filters/member_conditions_filter.rb +6 -8
  12. data/lib/gitlab/triage/filters/ruby_conditions_filter.rb +5 -3
  13. data/lib/gitlab/triage/network.rb +20 -8
  14. data/lib/gitlab/triage/network_adapters/base_adapter.rb +1 -1
  15. data/lib/gitlab/triage/option_parser.rb +72 -0
  16. data/lib/gitlab/triage/options.rb +21 -0
  17. data/lib/gitlab/triage/policies/base_policy.rb +21 -2
  18. data/lib/gitlab/triage/policies/rule_policy.rb +7 -1
  19. data/lib/gitlab/triage/policies/summary_policy.rb +25 -2
  20. data/lib/gitlab/triage/resource/base.rb +49 -10
  21. data/lib/gitlab/triage/resource/context.rb +13 -8
  22. data/lib/gitlab/triage/resource/instance_version.rb +3 -4
  23. data/lib/gitlab/triage/resource/issue.rb +14 -0
  24. data/lib/gitlab/triage/resource/label.rb +41 -0
  25. data/lib/gitlab/triage/resource/label_event.rb +45 -0
  26. data/lib/gitlab/triage/resource/merge_request.rb +14 -0
  27. data/lib/gitlab/triage/resource/milestone.rb +5 -5
  28. data/lib/gitlab/triage/resource/shared/issuable.rb +54 -0
  29. data/lib/gitlab/triage/url_builders/url_builder.rb +3 -2
  30. data/lib/gitlab/triage/version.rb +1 -1
  31. data/support/.triage-policies.example.yml +4 -4
  32. 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
- EntityBuilders::IssueBuilder.new(actions[:summarize], resources, net)
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
- EntityBuilders::IssueBuilder.new(actions[:summarize].merge(item: "{{description}}\n\n"), [], net).tap do |issue|
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(type, inner_policy_spec, inner_resources, net).build_issue.description
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, :net
9
+ attr_reader :resource, :parent
10
+
11
+ CONFIDENTIAL_TEXT = '(confidential)'
8
12
 
9
- def initialize(new_resource, new_net)
10
- @resource = new_resource
11
- @net = new_net
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
- private
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
- net[:network]
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
- net_opts.merge(params: { per_page: 100 }.merge(params))
60
+ url_opts
61
+ .merge(options)
62
+ .merge(params: { per_page: 100 }.merge(params))
23
63
  ).build
24
64
  end
25
65
 
26
- def net_opts
66
+ def url_opts
27
67
  {
28
- host_url: net[:host_url],
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 'milestone'
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
- class Context < Base
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(net)
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(new_net)
8
- super({}, new_net)
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
- net[:token],
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,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+ require_relative 'shared/issuable'
5
+
6
+ module Gitlab
7
+ module Triage
8
+ module Resource
9
+ class Issue < Base
10
+ include Shared::Issuable
11
+ end
12
+ end
13
+ end
14
+ 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
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+ require_relative 'shared/issuable'
5
+
6
+ module Gitlab
7
+ module Triage
8
+ module Resource
9
+ class MergeRequest < Base
10
+ include Shared::Issuable
11
+ end
12
+ end
13
+ end
14
+ end
@@ -27,13 +27,13 @@ module Gitlab
27
27
  ].freeze
28
28
 
29
29
  FIELDS.each do |field|
30
- define_method(field) do
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
- define_method(field) do
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
- define_method(field) do
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(net[:token], url(state: 'active'))
77
- .map { |milestone| self.class.new(milestone, net) }
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
- @host_url = options.fetch(:host_url)
7
- @api_version = options.fetch(:api_version)
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)
@@ -1,5 +1,5 @@
1
1
  module Gitlab
2
2
  module Triage
3
- VERSION = '0.14.1'.freeze
3
+ VERSION = '0.15.0'.freeze
4
4
  end
5
5
  end
@@ -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 issues summary"
21
+ title: "Newest and oldest {{type}} summary"
22
22
  summary: |
23
- Please triage the following issues:
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 issues:\n\n{{items}}"
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 issues:\n\n{{items}}"
48
+ summary: "Please triage the following old {{type}}:\n\n{{items}}"
49
49
  merge_requests:
50
50
  rules:
51
51
  []