gitlab-triage 1.53.0 → 1.54.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/Gemfile +3 -0
  3. data/Gemfile.lock +10 -9
  4. data/README.md +159 -1
  5. data/gitlab-triage.gemspec +1 -2
  6. data/lib/gitlab/triage/action/base.rb +62 -0
  7. data/lib/gitlab/triage/action/comment.rb +1 -9
  8. data/lib/gitlab/triage/action/comment_on_summary.rb +0 -6
  9. data/lib/gitlab/triage/action/issue.rb +31 -3
  10. data/lib/gitlab/triage/action/summarize.rb +21 -1
  11. data/lib/gitlab/triage/action/update.rb +432 -0
  12. data/lib/gitlab/triage/action/work_item.rb +0 -27
  13. data/lib/gitlab/triage/action.rb +18 -2
  14. data/lib/gitlab/triage/command_builders/cc_command_builder.rb +0 -4
  15. data/lib/gitlab/triage/entity_builders/issue_builder.rb +8 -0
  16. data/lib/gitlab/triage/entity_builders/issue_fields.rb +52 -0
  17. data/lib/gitlab/triage/entity_builders/summary_builder.rb +8 -0
  18. data/lib/gitlab/triage/graphql_network.rb +12 -2
  19. data/lib/gitlab/triage/network.rb +4 -0
  20. data/lib/gitlab/triage/network_adapters/httparty_adapter.rb +24 -0
  21. data/lib/gitlab/triage/network_adapters/test_adapter.rb +8 -0
  22. data/lib/gitlab/triage/policies/base_policy.rb +10 -2
  23. data/lib/gitlab/triage/policies/rule_policy.rb +18 -8
  24. data/lib/gitlab/triage/policies/summary_policy.rb +9 -0
  25. data/lib/gitlab/triage/rest_api_network.rb +22 -0
  26. data/lib/gitlab/triage/validators/policy_validator.rb +1 -3
  27. data/lib/gitlab/triage/version.rb +1 -1
  28. metadata +14 -10
  29. data/lib/gitlab/triage/command_builders/label_command_builder.rb +0 -42
  30. data/lib/gitlab/triage/command_builders/move_command_builder.rb +0 -21
  31. data/lib/gitlab/triage/command_builders/remove_label_command_builder.rb +0 -17
  32. data/lib/gitlab/triage/command_builders/status_command_builder.rb +0 -25
@@ -86,6 +86,30 @@ module Gitlab
86
86
  }
87
87
  end
88
88
 
89
+ def put(token, url, body)
90
+ response = HTTParty.put(
91
+ url,
92
+ body: body.to_json,
93
+ headers: {
94
+ 'User-Agent' => USER_AGENT,
95
+ 'Content-type' => 'application/json',
96
+ 'PRIVATE-TOKEN' => token
97
+ }
98
+ )
99
+
100
+ raise_on_unauthorized_error!(response)
101
+ raise_on_internal_server_error!(response)
102
+ raise_on_too_many_requests!(response)
103
+
104
+ tracker&.record('PUT', url)
105
+
106
+ {
107
+ results: response.parsed_response,
108
+ ratelimit_remaining: response.headers['ratelimit-remaining'].to_i,
109
+ ratelimit_reset_at: Time.at(response.headers['ratelimit-reset'].to_i)
110
+ }
111
+ end
112
+
89
113
  def delete(token, url)
90
114
  response = HTTParty.delete(
91
115
  url,
@@ -35,6 +35,14 @@ module Gitlab
35
35
  ratelimit_reset_at: Time.now
36
36
  }
37
37
  end
38
+
39
+ def put(_token, _url, _body)
40
+ {
41
+ results: {},
42
+ ratelimit_remaining: 600,
43
+ ratelimit_reset_at: Time.now
44
+ }
45
+ end
38
46
  end
39
47
  end
40
48
  end
@@ -6,6 +6,11 @@ module Gitlab
6
6
  class BasePolicy
7
7
  InvalidPolicyError = Class.new(StandardError)
8
8
 
9
+ UPDATE_ACTION_KEYS = %i[
10
+ labels remove_labels assignees reviewers remove_assignees remove_reviewers
11
+ milestone due_date weight health_status title status discussion_locked move
12
+ ].freeze
13
+
9
14
  attr_reader :type, :policy_spec, :resources, :network
10
15
  attr_accessor :summary
11
16
 
@@ -55,8 +60,11 @@ module Gitlab
55
60
  end
56
61
 
57
62
  def comment?
58
- # The actual keys are strings
59
- (actions.keys.map(&:to_sym) - [:summarize, :comment_on_summary, :delete, :issue, :work_item_status]).any?
63
+ actions.key?(:comment) || actions.key?(:mention)
64
+ end
65
+
66
+ def update?
67
+ (actions.keys.map(&:to_sym) & UPDATE_ACTION_KEYS).any?
60
68
  end
61
69
 
62
70
  def issue?
@@ -9,27 +9,37 @@ module Gitlab
9
9
  module Policies
10
10
  class RulePolicy < BasePolicy
11
11
  # Build a summary from a single rule policy
12
+ #
13
+ # The builder is kept for the life of the policy. Every action that asks
14
+ # for it gets the same one, so the title and description templates are
15
+ # evaluated once for the run rather than once per action.
12
16
  def build_summary
13
- action = actions.fetch(:summarize, {})
14
-
15
- EntityBuilders::SummaryBuilder.new(
17
+ @build_summary ||= EntityBuilders::SummaryBuilder.new(
16
18
  type: type,
17
19
  policy_spec: policy_spec,
18
- action: action,
20
+ action: actions.fetch(:summarize, {}),
19
21
  resources: resources,
20
22
  network: network)
21
23
  end
22
24
 
25
+ # The builder for one resource, kept for the life of the policy the same
26
+ # way build_summary is. Keyed on the resource itself, which the policy
27
+ # holds for the whole run, so two actions handed the same resource share
28
+ # one builder.
23
29
  def build_issue(resource)
24
- action = actions.fetch(:issue, {})
25
-
26
- EntityBuilders::IssueBuilder.new(
30
+ issue_builders[resource] ||= EntityBuilders::IssueBuilder.new(
27
31
  type: type,
28
32
  policy_spec: policy_spec,
29
- action: action,
33
+ action: actions.fetch(:issue, {}),
30
34
  resource: resource,
31
35
  network: network)
32
36
  end
37
+
38
+ private
39
+
40
+ def issue_builders
41
+ @issue_builders ||= {}.compare_by_identity
42
+ end
33
43
  end
34
44
  end
35
45
  end
@@ -8,7 +8,16 @@ module Gitlab
8
8
  module Policies
9
9
  class SummaryPolicy < BasePolicy
10
10
  # Build a summary from several rules policies
11
+ #
12
+ # Kept for the life of the policy, like the single-rule version, so the
13
+ # child summaries are built once for the run.
11
14
  def build_summary
15
+ @build_summary ||= build_combined_summary
16
+ end
17
+
18
+ private
19
+
20
+ def build_combined_summary
12
21
  action = actions[:summarize]
13
22
  child_summaries = resources.map do |inner_policy_spec, inner_resources|
14
23
  Policies::RulePolicy.new(
@@ -112,6 +112,28 @@ module Gitlab
112
112
  end
113
113
  end
114
114
 
115
+ def put_api(url, body)
116
+ response = execute_with_retry(
117
+ exception_types: [Net::ReadTimeout, Errors::Network::InternalServerError],
118
+ backoff_exceptions: Errors::Network::TooManyRequests, debug: options.debug) do
119
+ puts Gitlab::Triage::UI.debug "put_api: #{url}" if options.debug
120
+
121
+ @adapter.put(token, url, body)
122
+ end
123
+
124
+ rate_limit_debug(response) if options.debug
125
+ rate_limit_wait(response)
126
+
127
+ results = response.delete(:results)
128
+
129
+ case results
130
+ when Hash
131
+ results.with_indifferent_access
132
+ else
133
+ raise_unexpected_response(results)
134
+ end
135
+ end
136
+
115
137
  def delete_api(url)
116
138
  response = execute_with_retry(
117
139
  exception_types: [Net::ReadTimeout, Errors::Network::InternalServerError],
@@ -17,9 +17,7 @@ module Gitlab
17
17
  'Use the issues policy type for comment actions.'
18
18
 
19
19
  # Action keys Action.actions_for dispatches for work_items policies.
20
- # Unlike the legacy resource types, work_items have no quick-actions
21
- # comment fallback for unrecognized keys, so anything outside this
22
- # list would silently do nothing - reject it up front instead.
20
+ # Anything outside this list would silently do nothing, so reject it up front.
23
21
  SUPPORTED_WORK_ITEM_ACTIONS = %i[labels assignees summarize comment_on_summary].freeze
24
22
 
25
23
  def initialize(resource_rules)
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Gitlab
4
4
  module Triage
5
- VERSION = '1.53.0'
5
+ VERSION = '1.54.0'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gitlab-triage
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.53.0
4
+ version: 1.54.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - GitLab
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-08-27 00:00:00.000000000 Z
11
+ date: 2026-09-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -76,16 +76,22 @@ dependencies:
76
76
  name: httparty
77
77
  requirement: !ruby/object:Gem::Requirement
78
78
  requirements:
79
- - - "~>"
79
+ - - ">="
80
80
  - !ruby/object:Gem::Version
81
- version: 0.20.0
81
+ version: 0.21.0
82
+ - - "<"
83
+ - !ruby/object:Gem::Version
84
+ version: '1.0'
82
85
  type: :runtime
83
86
  prerelease: false
84
87
  version_requirements: !ruby/object:Gem::Requirement
85
88
  requirements:
86
- - - "~>"
89
+ - - ">="
87
90
  - !ruby/object:Gem::Version
88
- version: 0.20.0
91
+ version: 0.21.0
92
+ - - "<"
93
+ - !ruby/object:Gem::Version
94
+ version: '1.0'
89
95
  - !ruby/object:Gem::Dependency
90
96
  name: gitlab-dangerfiles
91
97
  requirement: !ruby/object:Gem::Requirement
@@ -239,6 +245,7 @@ files:
239
245
  - lib/gitlab/triage/action/delete.rb
240
246
  - lib/gitlab/triage/action/issue.rb
241
247
  - lib/gitlab/triage/action/summarize.rb
248
+ - lib/gitlab/triage/action/update.rb
242
249
  - lib/gitlab/triage/action/work_item.rb
243
250
  - lib/gitlab/triage/action/work_item_status.rb
244
251
  - lib/gitlab/triage/api_observability.rb
@@ -249,13 +256,10 @@ files:
249
256
  - lib/gitlab/triage/command_builders/base_command_builder.rb
250
257
  - lib/gitlab/triage/command_builders/cc_command_builder.rb
251
258
  - lib/gitlab/triage/command_builders/comment_command_builder.rb
252
- - lib/gitlab/triage/command_builders/label_command_builder.rb
253
- - lib/gitlab/triage/command_builders/move_command_builder.rb
254
- - lib/gitlab/triage/command_builders/remove_label_command_builder.rb
255
- - lib/gitlab/triage/command_builders/status_command_builder.rb
256
259
  - lib/gitlab/triage/command_builders/text_content_builder.rb
257
260
  - lib/gitlab/triage/engine.rb
258
261
  - lib/gitlab/triage/entity_builders/issue_builder.rb
262
+ - lib/gitlab/triage/entity_builders/issue_fields.rb
259
263
  - lib/gitlab/triage/entity_builders/summary_builder.rb
260
264
  - lib/gitlab/triage/errors.rb
261
265
  - lib/gitlab/triage/errors/network.rb
@@ -1,42 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'base_command_builder'
4
-
5
- module Gitlab
6
- module Triage
7
- module CommandBuilders
8
- class LabelCommandBuilder < BaseCommandBuilder
9
- def build_command
10
- ensure_labels_exist!
11
-
12
- super
13
- end
14
-
15
- private
16
-
17
- def ensure_labels_exist!
18
- items.each do |label|
19
- source_id_key = resource.key?(:group_id) ? :group_id : :project_id
20
- label_opts = {
21
- source_id_key => resource[source_id_key],
22
- name: label
23
- }
24
-
25
- unless Resource::Label.new(label_opts, network: network).exist?
26
- raise Resource::Label::LabelDoesntExistError,
27
- "Label `#{label}` doesn't exist!"
28
- end
29
- end
30
- end
31
-
32
- def slash_command_string
33
- "/label"
34
- end
35
-
36
- def format_item(item)
37
- "~\"#{item}\""
38
- end
39
- end
40
- end
41
- end
42
- end
@@ -1,21 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'base_command_builder'
4
-
5
- module Gitlab
6
- module Triage
7
- module CommandBuilders
8
- class MoveCommandBuilder < BaseCommandBuilder
9
- private
10
-
11
- def slash_command_string
12
- "/move"
13
- end
14
-
15
- def format_item(item)
16
- item
17
- end
18
- end
19
- end
20
- end
21
- end
@@ -1,17 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'base_command_builder'
4
-
5
- module Gitlab
6
- module Triage
7
- module CommandBuilders
8
- class RemoveLabelCommandBuilder < LabelCommandBuilder
9
- private
10
-
11
- def slash_command_string
12
- '/unlabel'
13
- end
14
- end
15
- end
16
- end
17
- end
@@ -1,25 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'base_command_builder'
4
-
5
- module Gitlab
6
- module Triage
7
- module CommandBuilders
8
- class StatusCommandBuilder < BaseCommandBuilder
9
- private
10
-
11
- def separator
12
- ''
13
- end
14
-
15
- def slash_command_string
16
- "/"
17
- end
18
-
19
- def format_item(item)
20
- item
21
- end
22
- end
23
- end
24
- end
25
- end