gitlab-triage 1.23.1 → 1.24.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 34fcebd4ec7d33597bb45cf4a48a9708143972b9d5c836f374f2cc629dc392a8
4
- data.tar.gz: 48ed9e443369d98b3681d07bd83d81962a82fc5e5449c6c3198935f46a52e764
3
+ metadata.gz: 0d4dafafa454a68a9258f5753ef3df345e0a0f13c104158bc1243ecb561d408a
4
+ data.tar.gz: c0d12641e77a6865e0f5569d405e2548b03884e61280dc262a922ff38b786c84
5
5
  SHA512:
6
- metadata.gz: d32b0e581d1067771b25a1124d591e1e084c10e042a82d20e00ac4d00346e0f386c4c0962477db98d166dab2b0e893d82cbc171f7a5e81c398d2758c6839807d
7
- data.tar.gz: 94b2712733c583bab7bc2a4f0c7d92155f9b454a257cd8461cecb1290b4e3aef77d758fadbba1b8720f95585372d2b1c4366ed38481d013f8c19a7ae52d19f44
6
+ metadata.gz: ea2b0d33abf1eb19403519f264ab25d249a9992e97e08f830505f497a50cc91ae0f9210db270b87d45649ef41c1d1248fcf2ef13b6820991455cfe4d09e8247f
7
+ data.tar.gz: 063b0a7ec5503f55ee4971e84cc1ddeb81b1ce83997a41dbd48a9c730ada4b402693fc1e4f2be6a955cc9de57c283ed4ee1b37e059633bec355a95c2a55efe3b
@@ -9,3 +9,5 @@ If you are experiencing an issue when using GitLab.com, your first port of call
9
9
  If you feel that your issue can be categorized as a reproducible bug or a feature proposal, please use one of the issue templates provided and include as much information as possible.
10
10
 
11
11
  Thank you for helping to make GitLab a better product.
12
+
13
+ <!-- template sourced from https://gitlab.com/gitlab-org/ruby/gems/gitlab-triage/-/blob/master/.gitlab/issue_templates/Default.md -->
@@ -7,3 +7,5 @@ Please keep this description updated with any discussion that takes place so
7
7
  that reviewers can understand your intent. Keeping the description updated is
8
8
  especially important if they didn't participate in the discussion.
9
9
  -->
10
+
11
+ <!-- template sourced from https://gitlab.com/gitlab-org/ruby/gems/gitlab-triage/-/blob/master/.gitlab/merge_request_templates/Default.md -->
data/.gitlab-ci.yml CHANGED
@@ -106,6 +106,13 @@ specs:
106
106
  script:
107
107
  - bundle exec rake spec
108
108
 
109
+ specs ruby3.0:
110
+ image: ruby:3.0
111
+ needs: ["setup-test-env"]
112
+ stage: test
113
+ script:
114
+ - bundle exec rake spec
115
+
109
116
  ##################
110
117
  ## Triage stage ##
111
118
  ##################
data/.rubocop.yml CHANGED
@@ -1,3 +1,5 @@
1
+ inherit_from: .rubocop_todo.yml
2
+
1
3
  inherit_gem:
2
4
  gitlab-styles:
3
5
  - rubocop-default.yml
@@ -14,3 +16,6 @@ Rails/Output:
14
16
 
15
17
  Metrics/LineLength:
16
18
  Max: 152
19
+
20
+ Style/SingleArgumentDig:
21
+ Enabled: false
data/.rubocop_todo.yml ADDED
@@ -0,0 +1,145 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2022-05-26 15:15:19 UTC using RuboCop version 0.93.1.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 43
10
+ CodeReuse/ActiveRecord:
11
+ Exclude:
12
+ - 'lib/gitlab/triage/engine.rb'
13
+ - 'lib/gitlab/triage/graphql_network.rb'
14
+ - 'spec/gitlab/triage/engine_spec.rb'
15
+ - 'spec/gitlab/triage/graphql_network_spec.rb'
16
+ - 'spec/gitlab/triage/network_adapters/graphql_adapter_spec.rb'
17
+ - 'spec/gitlab/triage/rest_api_network_spec.rb'
18
+ - 'spec/gitlab/triage/policies/rule_policy_spec.rb'
19
+ - 'spec/gitlab/triage/resource/label_spec.rb'
20
+ - 'spec/support/expect_next_instance_of.rb'
21
+ - 'spec/support/shared_examples/issuable_shared_examples.rb'
22
+ - 'spec/support/shared_examples/label_command_shared_examples.rb'
23
+ - 'spec/support/stub_api.rb'
24
+
25
+ # Offense count: 1
26
+ # Cop supports --auto-correct.
27
+ Cop/LineBreakAroundConditionalBlock:
28
+ Exclude:
29
+ - 'lib/gitlab/triage/engine.rb'
30
+
31
+ # Offense count: 1
32
+ # Configuration parameters: Include.
33
+ # Include: **/*.gemspec
34
+ Gemspec/RequiredRubyVersion:
35
+ Exclude:
36
+ - 'gitlab-triage.gemspec'
37
+
38
+ # Offense count: 1
39
+ # Cop supports --auto-correct.
40
+ Lint/NonDeterministicRequireOrder:
41
+ Exclude:
42
+ - 'spec/spec_helper.rb'
43
+
44
+ # Offense count: 1
45
+ # Configuration parameters: IgnoredMethods.
46
+ Metrics/AbcSize:
47
+ Max: 57
48
+
49
+ # Offense count: 1
50
+ # Cop supports --auto-correct.
51
+ Performance/ConstantRegexp:
52
+ Exclude:
53
+ - 'lib/gitlab/triage/command_builders/text_content_builder.rb'
54
+
55
+ # Offense count: 2
56
+ # Cop supports --auto-correct.
57
+ # Configuration parameters: SafeMultiline.
58
+ Performance/DeleteSuffix:
59
+ Exclude:
60
+ - 'lib/gitlab/triage/api_query_builders/date_query_param_builder.rb'
61
+ - 'lib/gitlab/triage/graphql_queries/query_param_builders/date_param_builder.rb'
62
+
63
+ # Offense count: 5
64
+ Performance/MethodObjectAsBlock:
65
+ Exclude:
66
+ - 'lib/gitlab/triage/engine.rb'
67
+ - 'lib/gitlab/triage/entity_builders/issue_builder.rb'
68
+ - 'lib/gitlab/triage/expand_condition.rb'
69
+ - 'lib/gitlab/triage/expand_condition/expansion.rb'
70
+ - 'lib/gitlab/triage/limiters/date_field_limiter.rb'
71
+
72
+ # Offense count: 31
73
+ # Cop supports --auto-correct.
74
+ RSpec/EmptyLineAfterLetBlock:
75
+ Enabled: false
76
+
77
+ # Offense count: 93
78
+ # Configuration parameters: AllowSubject.
79
+ RSpec/MultipleMemoizedHelpers:
80
+ Max: 12
81
+
82
+ # Offense count: 12
83
+ # Configuration parameters: IgnoreNameless, IgnoreSymbolicNames.
84
+ RSpec/VerifiedDoubles:
85
+ Exclude:
86
+ - 'spec/gitlab/triage/graphql_network_spec.rb'
87
+ - 'spec/gitlab/triage/network_adapters/graphql_adapter_spec.rb'
88
+ - 'spec/gitlab/triage/rest_api_network_spec.rb'
89
+ - 'spec/support/shared_examples/label_command_shared_examples.rb'
90
+
91
+ # Offense count: 1
92
+ # Cop supports --auto-correct.
93
+ Rails/IndexBy:
94
+ Exclude:
95
+ - 'lib/gitlab/triage/engine.rb'
96
+
97
+ # Offense count: 1
98
+ # Cop supports --auto-correct.
99
+ Rails/NegateInclude:
100
+ Exclude:
101
+ - 'lib/gitlab/triage/filters/member_conditions_filter.rb'
102
+
103
+ # Offense count: 3
104
+ # Cop supports --auto-correct.
105
+ Rails/Pluck:
106
+ Exclude:
107
+ - 'lib/gitlab/triage/filters/base_conditions_filter.rb'
108
+ - 'lib/gitlab/triage/filters/member_conditions_filter.rb'
109
+ - 'lib/gitlab/triage/validators/limiter_validator.rb'
110
+
111
+ # Offense count: 2
112
+ # Cop supports --auto-correct.
113
+ Style/ArrayCoercion:
114
+ Exclude:
115
+ - 'lib/gitlab/triage/graphql_network.rb'
116
+
117
+ # Offense count: 1
118
+ # Cop supports --auto-correct.
119
+ Style/ExplicitBlockArgument:
120
+ Exclude:
121
+ - 'spec/support/expect_next_instance_of.rb'
122
+
123
+ # Offense count: 115
124
+ # Cop supports --auto-correct.
125
+ # Configuration parameters: EnforcedStyle.
126
+ # SupportedStyles: always, always_true, never
127
+ Style/FrozenStringLiteralComment:
128
+ Enabled: false
129
+
130
+ # Offense count: 5
131
+ Style/OpenStructUse:
132
+ Exclude:
133
+ - 'spec/gitlab/triage/network_adapters/httparty_adapter_spec.rb'
134
+
135
+ # Offense count: 1
136
+ # Cop supports --auto-correct.
137
+ Style/RedundantRegexpEscape:
138
+ Exclude:
139
+ - 'lib/gitlab/triage/command_builders/text_content_builder.rb'
140
+
141
+ # Offense count: 1
142
+ # Configuration parameters: AllowModifier.
143
+ Style/SoleNestedConditional:
144
+ Exclude:
145
+ - 'lib/gitlab/triage/validators/params_validator.rb'
data/Gemfile CHANGED
@@ -6,7 +6,7 @@ gemspec
6
6
  gem "yard"
7
7
 
8
8
  group :test do
9
- gem 'gitlab-styles', '~> 3.3.0'
9
+ gem 'gitlab-styles', '~> 7.0'
10
10
  end
11
11
 
12
12
  group :development, :test, :danger do
data/README.md CHANGED
@@ -178,8 +178,9 @@ Accepts a hash of fields.
178
178
  | `condition` | string | `older_than`, `newer_than` | yes |
179
179
  | `interval_type` | string | `days`, `weeks`, `months`, `years` | yes |
180
180
  | `interval` | integer | integer | yes |
181
-
182
- > **Note:** `merged_at` only works on merge requests.
181
+ > **Note:**
182
+ > - `merged_at` only works on merge requests.
183
+ > - `closed_at` is not supported in the GitLab API, but can be used in a [`ruby` condition](#ruby-condition).
183
184
 
184
185
  Example:
185
186
 
@@ -612,6 +613,13 @@ conditions:
612
613
  This will make it only act on resources which have active milestones and
613
614
  there exists next milestone which has already started.
614
615
 
616
+ Since `closed_at` is not a queryable attribute in the GitLab API, we can use a Ruby expression to filter resources like:
617
+
618
+ ```yml
619
+ conditions:
620
+ ruby: resource[:closed_at] > 7.days.ago.strftime('%Y-%m-%dT00:00:00.000Z')
621
+ ```
622
+
615
623
  See [Ruby expression API](#ruby-expression-api) for the list of currently
616
624
  available API.
617
625
 
@@ -984,6 +992,46 @@ resource_rules:
984
992
  /label ~"needs attention"
985
993
  ```
986
994
 
995
+ ##### Create a new issue from each resource
996
+
997
+ Generates one issue for each resource, by default in the same project as the resource.
998
+
999
+ The use case for this is, for example, creating test issues in the same (or different)
1000
+ project for issues labeled "extended-testing"; or automatically splitting one issue with a
1001
+ certain label into multiple ones.
1002
+
1003
+ Accepts a hash of fields.
1004
+
1005
+ | Field | Type | Description | Required | Placeholders | Ruby expression | Default |
1006
+ | ---- | ---- | ---- | ---- | ---- | ---- | ---- |
1007
+ | `title` | string | The title of the generated issue | yes | yes | yes | |
1008
+ | `destination` | integer or string | The project ID or path to create the generated issue in | no | no | no | source project |
1009
+ | `description` | string | The description of the generated issue | no | yes | yes | |
1010
+ | `redact_confidential_resources` | boolean | Whether redact fields for confidential resources | no | no | no | true |
1011
+
1012
+ The placeholders available in `title` and `destination` are the properties of the resource being used to generate the issue.
1013
+
1014
+ Example
1015
+
1016
+ ```yml
1017
+ resource_rules:
1018
+ issues:
1019
+ rules:
1020
+ - name: Issues requiring extra testing
1021
+ labels:
1022
+ - needs-testing
1023
+ actions:
1024
+ issue:
1025
+ title: |
1026
+ Testing: {{ title }}
1027
+ description: |
1028
+ The issue {{ full_reference }} needs testing.
1029
+
1030
+ Please take care of them before the end of #{7.days.from_now.strftime('%Y-%m-%d')}
1031
+
1032
+ /label ~"needs attention"
1033
+ ```
1034
+
987
1035
  ### Summary policies
988
1036
 
989
1037
  Summary policies are special policies that join multiple rule policies together
@@ -1090,13 +1138,20 @@ Here's a list of currently available Ruby expression API:
1090
1138
  | project_path | String | The path with namespace to the issues or merge requests project |
1091
1139
  | full_resource_reference | String | A full reference including project path to the issue or merge request |
1092
1140
 
1093
- ##### Methods for `Issue` (issue context)
1141
+ ##### Methods for `Issue` and `LinkedIssue` (issue context)
1094
1142
 
1095
1143
  | Name | Return type | Description |
1096
1144
  | ---- | ---- | ---- |
1097
1145
  | merge_requests_count | Integer | The number of merge requests related to the issue |
1098
1146
  | related_merge_requests | [MergeRequest] | The list of merge requests related to the issue |
1099
1147
  | closed_by | [MergeRequest] | The list of merge requests that close the issue |
1148
+ | linked_issues | [LinkedIssue] | The list of issues that are linked to the issue |
1149
+
1150
+ ##### Methods for `LinkedIssue`
1151
+
1152
+ | Method | Return type | Description |
1153
+ | ---- | ---- | ---- |
1154
+ | link_type | String | The link type of the linked issue (`blocks`, `is_blocked_by`, or `relates_to`) |
1100
1155
 
1101
1156
  ##### Methods for `MergeRequest` (merge request context)
1102
1157
 
@@ -1227,8 +1282,8 @@ run:triage:triage:
1227
1282
  script:
1228
1283
  - gem install gitlab-triage
1229
1284
  - gitlab-triage --token $GITLAB_API_TOKEN --source-id $CI_PROJECT_PATH
1230
- only:
1231
- - schedules
1285
+ rules:
1286
+ - if: $CI_PIPELINE_SOURCE == "schedule"
1232
1287
  ```
1233
1288
 
1234
1289
  > **Note:** You can use the [`--init-ci`](#usage) option to add an example [`.gitlab-ci.yml` file](support/.gitlab-ci.example.yml) to your project
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Gitlab
6
+ module Triage
7
+ module Action
8
+ class Issue < Base
9
+ class Dry < Issue
10
+ def act
11
+ puts "The following issues would be created for the rule **#{policy.name}**:\n\n"
12
+
13
+ super
14
+ end
15
+
16
+ private
17
+
18
+ def perform(resource, issue)
19
+ puts ">>>"
20
+ puts "* Project: #{issue.destination || resource[policy.source_id_sym]}"
21
+ puts "* Title: #{issue.title}"
22
+ puts "* Description: #{issue.description}"
23
+ puts ">>>"
24
+ end
25
+ end
26
+
27
+ def act
28
+ policy.resources.each do |resource|
29
+ issue = policy.build_issue(resource)
30
+
31
+ perform(resource, issue) if issue.valid?
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ def perform(resource, issue)
38
+ network.post_api(build_post_url(resource, issue), post_issue_body(issue))
39
+ end
40
+
41
+ def build_post_url(resource, issue)
42
+ # POST /projects/:id/issues
43
+ # https://docs.gitlab.com/ee/api/issues.html#new-issue
44
+ post_url = UrlBuilders::UrlBuilder.new(
45
+ network_options: network.options,
46
+ source_id: issue.destination || resource[policy.source_id_sym],
47
+ resource_type: 'issues'
48
+ ).build
49
+
50
+ puts Gitlab::Triage::UI.debug "post_issue_url: #{post_url}" if network.options.debug
51
+
52
+ post_url
53
+ end
54
+
55
+ def post_issue_body(issue)
56
+ {
57
+ title: issue.title,
58
+ description: issue.description
59
+ }
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -45,7 +45,7 @@ module Gitlab
45
45
  end
46
46
 
47
47
  def issue
48
- @issue ||= policy.build_issue
48
+ @issue ||= policy.build_summary
49
49
  end
50
50
 
51
51
  def destination
@@ -1,6 +1,7 @@
1
1
  require_relative 'action/summarize'
2
2
  require_relative 'action/comment'
3
3
  require_relative 'action/comment_on_summary'
4
+ require_relative 'action/issue'
4
5
 
5
6
  module Gitlab
6
7
  module Triage
@@ -11,7 +12,8 @@ module Gitlab
11
12
  [
12
13
  [Summarize, policy.summarize?],
13
14
  [Comment, policy.comment?],
14
- [CommentOnSummary, policy.comment_on_summary?]
15
+ [CommentOnSummary, policy.comment_on_summary?],
16
+ [Issue, policy.issue?]
15
17
  ].each do |action, active|
16
18
  act(action: action, policy: policy, **args) if active
17
19
  end
@@ -21,6 +21,7 @@ require_relative 'api_query_builders/multi_query_param_builder'
21
21
  require_relative 'url_builders/url_builder'
22
22
  require_relative 'network'
23
23
  require_relative 'graphql_network'
24
+ require_relative 'rest_api_network'
24
25
  require_relative 'network_adapters/httparty_adapter'
25
26
  require_relative 'network_adapters/graphql_adapter'
26
27
  require_relative 'graphql_queries/query_builder'
@@ -78,7 +79,11 @@ module Gitlab
78
79
  end
79
80
 
80
81
  def network
81
- @network ||= Network.new(network_adapter)
82
+ @network ||= Network.new(restapi: restapi_network, graphql: graphql_network)
83
+ end
84
+
85
+ def restapi_network
86
+ @restapi_network ||= RestAPINetwork.new(network_adapter)
82
87
  end
83
88
 
84
89
  def graphql_network
@@ -6,72 +6,44 @@ module Gitlab
6
6
  module Triage
7
7
  module EntityBuilders
8
8
  class IssueBuilder
9
+ attr_reader :destination
10
+
9
11
  def initialize(
10
- type:, action:, resources:, network:,
12
+ type:, action:, resource:, network:,
11
13
  policy_spec: {}, separator: "\n")
12
14
  @type = type
13
15
  @policy_spec = policy_spec
14
16
  @item_template = action[:item]
15
17
  @title_template = action[:title]
16
- @summary_template = action[:summary]
17
- @summary_destination = action[:destination]
18
+ @description_template = action[:description]
19
+ @destination = action[:destination]
18
20
  @redact_confidentials =
19
21
  action[:redact_confidential_resources] != false
20
- @resources = resources
22
+ @resource = resource
21
23
  @network = network
22
24
  @separator = separator
23
25
  end
24
26
 
25
27
  def title
26
- @title ||= build_text(title_resource, @title_template)
28
+ @title ||= build_text(@title_template)
27
29
  end
28
30
 
29
31
  def description
30
- @description ||= build_text(description_resource, @summary_template)
31
- end
32
-
33
- def destination
34
- @summary_destination
32
+ @description ||= build_text(@description_template)
35
33
  end
36
34
 
37
35
  def valid?
38
- title =~ /\S+/ && any_resources?
39
- end
40
-
41
- def any_resources?
42
- @resources.any?
36
+ title =~ /\S+/
43
37
  end
44
38
 
45
39
  private
46
40
 
47
- def title_resource
48
- { type: @type }
49
- end
50
-
51
- def description_resource
52
- title_resource.merge(
53
- title: title, items: items, resources: @resources)
54
- end
55
-
56
- def items
57
- @items ||= @resources.map(&method(:build_item)).join(@separator)
58
- end
59
-
60
- def build_item(resource)
61
- case resource
62
- when IssueBuilder
63
- resource.description
64
- else
65
- build_text(resource, @item_template)
66
- end
67
- end
68
-
69
- def build_text(resource, template)
41
+ def build_text(template)
70
42
  return '' unless template
71
43
 
72
44
  CommandBuilders::TextContentBuilder.new(
73
45
  template,
74
- resource: resource,
46
+ resource: @resource,
75
47
  network: @network,
76
48
  redact_confidentials: @redact_confidentials)
77
49
  .build_command.chomp
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../command_builders/text_content_builder'
4
+
5
+ module Gitlab
6
+ module Triage
7
+ module EntityBuilders
8
+ class SummaryBuilder
9
+ def initialize(
10
+ type:, action:, resources:, network:,
11
+ policy_spec: {}, separator: "\n")
12
+ @type = type
13
+ @policy_spec = policy_spec
14
+ @item_template = action[:item]
15
+ @title_template = action[:title]
16
+ @summary_template = action[:summary]
17
+ @summary_destination = action[:destination]
18
+ @redact_confidentials =
19
+ action[:redact_confidential_resources] != false
20
+ @resources = resources
21
+ @network = network
22
+ @separator = separator
23
+ end
24
+
25
+ def title
26
+ @title ||= build_text(title_resource, @title_template)
27
+ end
28
+
29
+ def description
30
+ @description ||= build_text(description_resource, @summary_template)
31
+ end
32
+
33
+ def destination
34
+ @summary_destination
35
+ end
36
+
37
+ def valid?
38
+ title =~ /\S+/ && any_resources?
39
+ end
40
+
41
+ def any_resources?
42
+ @resources.any?
43
+ end
44
+
45
+ private
46
+
47
+ def title_resource
48
+ { type: @type }
49
+ end
50
+
51
+ def description_resource
52
+ title_resource.merge(
53
+ title: title, items: items, resources: @resources)
54
+ end
55
+
56
+ def items
57
+ @items ||= @resources.map { |x| build_item(x) }.join(@separator)
58
+ end
59
+
60
+ def build_item(resource)
61
+ case resource
62
+ when SummaryBuilder
63
+ resource.description
64
+ else
65
+ build_text(resource, @item_template)
66
+ end
67
+ end
68
+
69
+ def build_text(resource, template)
70
+ return '' unless template
71
+
72
+ CommandBuilders::TextContentBuilder.new(
73
+ template,
74
+ resource: resource,
75
+ network: @network,
76
+ redact_confidentials: @redact_confidentials)
77
+ .build_command.chomp
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -103,28 +103,28 @@ module Gitlab
103
103
  when 'forbidden_labels'
104
104
  ['labelName', condition_params, { negated: true }]
105
105
  when 'labels'
106
- ['labelName', condition_params]
106
+ ['labelName', condition_params, {}]
107
107
  else
108
108
  return nil
109
109
  end
110
110
 
111
- QueryParamBuilders::LabelsParamBuilder.new(*args)
111
+ QueryParamBuilders::LabelsParamBuilder.new(*args[0...-1], **args.last)
112
112
  end
113
113
 
114
114
  def merge_requests_resource_query(condition, condition_params)
115
115
  args =
116
116
  case condition.to_s
117
117
  when 'source_branch'
118
- ['sourceBranches', condition_params]
118
+ ['sourceBranches', condition_params, {}]
119
119
  when 'target_branch'
120
- ['targetBranches', condition_params]
120
+ ['targetBranches', condition_params, {}]
121
121
  when 'draft'
122
122
  ['draft', condition_params, { with_quotes: false }]
123
123
  else
124
124
  return nil
125
125
  end
126
126
 
127
- QueryParamBuilders::BaseParamBuilder.new(*args)
127
+ QueryParamBuilders::BaseParamBuilder.new(*args[0...-1], **args.last)
128
128
  end
129
129
 
130
130
  def merge_requests_label_query(condition, condition_params)
@@ -133,12 +133,12 @@ module Gitlab
133
133
  when 'forbidden_labels'
134
134
  ['labels', condition_params, { negated: true }]
135
135
  when 'labels'
136
- ['labels', condition_params]
136
+ ['labels', condition_params, {}]
137
137
  else
138
138
  return nil
139
139
  end
140
140
 
141
- QueryParamBuilders::LabelsParamBuilder.new(*args)
141
+ QueryParamBuilders::LabelsParamBuilder.new(*args[0...-1], **args.last)
142
142
  end
143
143
  end
144
144
  end
@@ -1,110 +1,34 @@
1
- require 'active_support/all'
2
- require 'net/protocol'
3
-
4
- require_relative 'retryable'
5
- require_relative 'ui'
6
- require_relative 'errors'
1
+ require_relative 'graphql_network'
2
+ require_relative 'rest_api_network'
7
3
 
8
4
  module Gitlab
9
5
  module Triage
10
- class Network
11
- include Retryable
12
-
13
- TokenNotFound = Class.new(StandardError)
14
- MINIMUM_RATE_LIMIT = 25
15
-
16
- attr_reader :options, :adapter
17
-
18
- def initialize(adapter)
19
- @adapter = adapter
20
- @options = adapter.options
21
- @cache = {}
22
- end
23
-
24
- def query_api_cached(url)
25
- @cache[url] || @cache[url] = query_api(url)
26
- end
27
-
6
+ Network = Struct.new(:restapi, :graphql, keyword_init: true) do
28
7
  def query_api(url)
29
- response = {}
30
- resources = []
31
-
32
- begin
33
- print '.'
34
- url = response.fetch(:next_page_url) { url }
35
-
36
- response = execute_with_retry(
37
- exception_types: [Net::ReadTimeout, Errors::Network::InternalServerError],
38
- backoff_exceptions: Errors::Network::TooManyRequests) do
39
- puts Gitlab::Triage::UI.debug "query_api: #{url}" if options.debug
40
-
41
- @adapter.get(token, url)
42
- end
43
-
44
- results = response.delete(:results)
45
-
46
- case results
47
- when Array
48
- resources.concat(results)
49
- when Hash
50
- resources << results
51
- else
52
- raise_unexpected_response(results)
53
- end
54
-
55
- rate_limit_debug(response) if options.debug
56
- rate_limit_wait(response)
57
- end while response.delete(:more_pages)
58
-
59
- resources.map!(&:with_indifferent_access)
60
- rescue Net::ReadTimeout
61
- []
8
+ restapi.query_api(url)
62
9
  end
63
10
 
64
- def post_api(url, body)
65
- response = execute_with_retry(
66
- exception_types: Net::ReadTimeout,
67
- backoff_exceptions: Errors::Network::TooManyRequests) do
68
- puts Gitlab::Triage::UI.debug "post_api: #{url}" if options.debug
69
-
70
- @adapter.post(token, url, body)
71
- end
72
-
73
- rate_limit_debug(response) if options.debug
74
- rate_limit_wait(response)
75
-
76
- results = response.delete(:results)
77
-
78
- case results
79
- when Hash
80
- results.with_indifferent_access
81
- else
82
- raise_unexpected_response(results)
83
- end
84
- rescue Net::ReadTimeout
85
- {}
11
+ def query_graphql(...)
12
+ graphql.query(...)
86
13
  end
87
14
 
88
- private
89
-
90
- def token
91
- options.token || raise(TokenNotFound)
15
+ def query_api_cached(url)
16
+ restapi.query_api_cached(url)
92
17
  end
93
18
 
94
- def rate_limit_debug(response)
95
- rate_limit_infos = "Rate limit remaining: #{response[:ratelimit_remaining]} (reset at #{response[:ratelimit_reset_at]})"
96
- puts Gitlab::Triage::UI.debug "rate_limit_infos: #{rate_limit_infos}"
19
+ def restapi_options
20
+ restapi.options
97
21
  end
98
22
 
99
- def rate_limit_wait(response)
100
- return unless response.delete(:ratelimit_remaining) < MINIMUM_RATE_LIMIT
23
+ # FIXME: Remove the alias method
24
+ alias_method :options, :restapi_options
101
25
 
102
- puts Gitlab::Triage::UI.debug "Rate limit almost exceeded, sleeping for #{response[:ratelimit_reset_at] - Time.now} seconds" if options.debug
103
- sleep(1) until Time.now >= response[:ratelimit_reset_at]
26
+ def graphql_options
27
+ graphql.options
104
28
  end
105
29
 
106
- def raise_unexpected_response(results)
107
- raise Errors::Network::UnexpectedResponse, "Unexpected response: #{results.inspect}"
30
+ def post_api(...)
31
+ restapi.post_api(...)
108
32
  end
109
33
  end
110
34
  end
@@ -59,9 +59,17 @@ module Gitlab
59
59
  (actions.keys.map(&:to_sym) - [:summarize, :comment_on_summary]).any?
60
60
  end
61
61
 
62
+ def issue?
63
+ actions.key?(:issue)
64
+ end
65
+
62
66
  def build_issue
63
67
  raise NotImplementedError
64
68
  end
69
+
70
+ def build_summary
71
+ raise NotImplementedError
72
+ end
65
73
  end
66
74
  end
67
75
  end
@@ -2,22 +2,34 @@
2
2
 
3
3
  require_relative 'base_policy'
4
4
  require_relative '../entity_builders/issue_builder'
5
+ require_relative '../entity_builders/summary_builder'
5
6
 
6
7
  module Gitlab
7
8
  module Triage
8
9
  module Policies
9
10
  class RulePolicy < BasePolicy
10
- # Build an issue from a single rule policy
11
- def build_issue
11
+ # Build a summary from a single rule policy
12
+ def build_summary
12
13
  action = actions.fetch(:summarize, {})
13
14
 
14
- EntityBuilders::IssueBuilder.new(
15
+ EntityBuilders::SummaryBuilder.new(
15
16
  type: type,
16
17
  policy_spec: policy_spec,
17
18
  action: action,
18
19
  resources: resources,
19
20
  network: network)
20
21
  end
22
+
23
+ def build_issue(resource)
24
+ action = actions.fetch(:issue, {})
25
+
26
+ EntityBuilders::IssueBuilder.new(
27
+ type: type,
28
+ policy_spec: policy_spec,
29
+ action: action,
30
+ resource: resource,
31
+ network: network)
32
+ end
21
33
  end
22
34
  end
23
35
  end
@@ -1,22 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'base_policy'
4
- require_relative '../entity_builders/issue_builder'
4
+ require_relative '../entity_builders/summary_builder'
5
5
 
6
6
  module Gitlab
7
7
  module Triage
8
8
  module Policies
9
9
  class SummaryPolicy < BasePolicy
10
- # Build an issue from several rules policies
11
- def build_issue
10
+ # Build a summary from several rules policies
11
+ def build_summary
12
12
  action = actions[:summarize]
13
13
  issues = resources.map do |inner_policy_spec, inner_resources|
14
14
  Policies::RulePolicy.new(
15
15
  type, inner_policy_spec, inner_resources, network)
16
- .build_issue
16
+ .build_summary
17
17
  end
18
18
 
19
- EntityBuilders::IssueBuilder.new(
19
+ EntityBuilders::SummaryBuilder.new(
20
20
  type: type,
21
21
  action: action,
22
22
  resources: issues.select(&:any_resources?),
@@ -3,6 +3,7 @@
3
3
  require_relative 'base'
4
4
  require_relative 'epic'
5
5
  require_relative 'issue'
6
+ require_relative 'linked_issue'
6
7
  require_relative 'merge_request'
7
8
  require_relative 'instance_version'
8
9
 
@@ -5,7 +5,7 @@ module Gitlab
5
5
  module Resource
6
6
  class InstanceVersion < Base
7
7
  def initialize(**options)
8
- super({}, options)
8
+ super({}, **options)
9
9
  end
10
10
 
11
11
  def version
@@ -24,6 +24,12 @@ module Gitlab
24
24
  resource_url(sub_resource_type: 'closed_by'))
25
25
  .map { |merge_request| MergeRequest.new(merge_request, parent: self) }
26
26
  end
27
+
28
+ def linked_issues
29
+ @linked_issues ||= network.query_api_cached(
30
+ resource_url(sub_resource_type: 'links'))
31
+ .map { |issue| LinkedIssue.new(issue, parent: self) }
32
+ end
27
33
  end
28
34
  end
29
35
  end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'issue'
4
+
5
+ module Gitlab
6
+ module Triage
7
+ module Resource
8
+ class LinkedIssue < Issue
9
+ def link_type
10
+ @link_type ||= resource.dig(:link_type)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,111 @@
1
+ require 'active_support/all'
2
+ require 'net/protocol'
3
+
4
+ require_relative 'retryable'
5
+ require_relative 'ui'
6
+ require_relative 'errors'
7
+
8
+ module Gitlab
9
+ module Triage
10
+ class RestAPINetwork
11
+ include Retryable
12
+
13
+ TokenNotFound = Class.new(StandardError)
14
+ MINIMUM_RATE_LIMIT = 25
15
+
16
+ attr_reader :options, :adapter
17
+
18
+ def initialize(adapter)
19
+ @adapter = adapter
20
+ @options = adapter.options
21
+ @cache = {}
22
+ end
23
+
24
+ def query_api_cached(url)
25
+ @cache[url] || @cache[url] = query_api(url)
26
+ end
27
+
28
+ def query_api(url)
29
+ response = {}
30
+ resources = []
31
+
32
+ begin
33
+ print '.'
34
+ url = response.fetch(:next_page_url) { url }
35
+
36
+ response = execute_with_retry(
37
+ exception_types: [Net::ReadTimeout, Errors::Network::InternalServerError],
38
+ backoff_exceptions: Errors::Network::TooManyRequests) do
39
+ puts Gitlab::Triage::UI.debug "query_api: #{url}" if options.debug
40
+
41
+ @adapter.get(token, url)
42
+ end
43
+
44
+ results = response.delete(:results)
45
+
46
+ case results
47
+ when Array
48
+ resources.concat(results)
49
+ when Hash
50
+ resources << results
51
+ else
52
+ raise_unexpected_response(results)
53
+ end
54
+
55
+ rate_limit_debug(response) if options.debug
56
+ rate_limit_wait(response)
57
+ end while response.delete(:more_pages)
58
+
59
+ resources.map!(&:with_indifferent_access)
60
+ rescue Net::ReadTimeout
61
+ []
62
+ end
63
+
64
+ def post_api(url, body)
65
+ response = execute_with_retry(
66
+ exception_types: Net::ReadTimeout,
67
+ backoff_exceptions: Errors::Network::TooManyRequests) do
68
+ puts Gitlab::Triage::UI.debug "post_api: #{url}" if options.debug
69
+
70
+ @adapter.post(token, url, body)
71
+ end
72
+
73
+ rate_limit_debug(response) if options.debug
74
+ rate_limit_wait(response)
75
+
76
+ results = response.delete(:results)
77
+
78
+ case results
79
+ when Hash
80
+ results.with_indifferent_access
81
+ else
82
+ raise_unexpected_response(results)
83
+ end
84
+ rescue Net::ReadTimeout
85
+ {}
86
+ end
87
+
88
+ private
89
+
90
+ def token
91
+ options.token || raise(TokenNotFound)
92
+ end
93
+
94
+ def rate_limit_debug(response)
95
+ rate_limit_infos = "Rate limit remaining: #{response[:ratelimit_remaining]} (reset at #{response[:ratelimit_reset_at]})"
96
+ puts Gitlab::Triage::UI.debug "rate_limit_infos: #{rate_limit_infos}"
97
+ end
98
+
99
+ def rate_limit_wait(response)
100
+ return unless response.delete(:ratelimit_remaining) < MINIMUM_RATE_LIMIT
101
+
102
+ puts Gitlab::Triage::UI.debug "Rate limit almost exceeded, sleeping for #{response[:ratelimit_reset_at] - Time.now} seconds" if options.debug
103
+ sleep(1) until Time.now >= response[:ratelimit_reset_at]
104
+ end
105
+
106
+ def raise_unexpected_response(results)
107
+ raise Errors::Network::UnexpectedResponse, "Unexpected response: #{results.inspect}"
108
+ end
109
+ end
110
+ end
111
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Gitlab
4
4
  module Triage
5
- VERSION = '1.23.1'
5
+ VERSION = '1.24.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.23.1
4
+ version: 1.24.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - GitLab
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-04-27 00:00:00.000000000 Z
11
+ date: 2022-09-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -153,6 +153,7 @@ files:
153
153
  - ".gitlab/merge_request_templates/Default.md"
154
154
  - ".gitlab/merge_request_templates/Release.md"
155
155
  - ".rubocop.yml"
156
+ - ".rubocop_todo.yml"
156
157
  - ".ruby-version"
157
158
  - ".tool-versions"
158
159
  - ".yardopts"
@@ -170,6 +171,7 @@ files:
170
171
  - lib/gitlab/triage/action/base.rb
171
172
  - lib/gitlab/triage/action/comment.rb
172
173
  - lib/gitlab/triage/action/comment_on_summary.rb
174
+ - lib/gitlab/triage/action/issue.rb
173
175
  - lib/gitlab/triage/action/summarize.rb
174
176
  - lib/gitlab/triage/api_query_builders/base_query_param_builder.rb
175
177
  - lib/gitlab/triage/api_query_builders/date_query_param_builder.rb
@@ -185,6 +187,7 @@ files:
185
187
  - lib/gitlab/triage/command_builders/text_content_builder.rb
186
188
  - lib/gitlab/triage/engine.rb
187
189
  - lib/gitlab/triage/entity_builders/issue_builder.rb
190
+ - lib/gitlab/triage/entity_builders/summary_builder.rb
188
191
  - lib/gitlab/triage/errors.rb
189
192
  - lib/gitlab/triage/errors/network.rb
190
193
  - lib/gitlab/triage/expand_condition.rb
@@ -228,9 +231,11 @@ files:
228
231
  - lib/gitlab/triage/resource/issue.rb
229
232
  - lib/gitlab/triage/resource/label.rb
230
233
  - lib/gitlab/triage/resource/label_event.rb
234
+ - lib/gitlab/triage/resource/linked_issue.rb
231
235
  - lib/gitlab/triage/resource/merge_request.rb
232
236
  - lib/gitlab/triage/resource/milestone.rb
233
237
  - lib/gitlab/triage/resource/shared/issuable.rb
238
+ - lib/gitlab/triage/rest_api_network.rb
234
239
  - lib/gitlab/triage/retryable.rb
235
240
  - lib/gitlab/triage/ui.rb
236
241
  - lib/gitlab/triage/url_builders/url_builder.rb