gitlab-triage 0.8.1 → 0.9.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ff4171b6337da58a1c33a3cd9bc903e1d2c31292462117b1b479b1445948a603
4
- data.tar.gz: 219a8eac9342d358f4a6bccd2e1abc4338d5b10a766d3a7b81ab41a5ed797cbd
3
+ metadata.gz: 565a738248a7ded7917c1dd9f43962bc564b86a391083c93b91a8e1dc70b40f3
4
+ data.tar.gz: 2faa52c47be6f7823b9a70efa108bdea330b42a60defaa72ceba563cf7820a76
5
5
  SHA512:
6
- metadata.gz: 7123c4fc8ea9722acd47db48aafa651437e64a78bc5c5172aec35cf48a7714650d8385dd0842a4a7bc68a27bccf3af1d0275b29b139a4f9de14b8a36db99c9ec
7
- data.tar.gz: d9308ca1eed39ca94712203796015763c846a042db1a1865001d2046ef94b146696aa3f26a0ad4f6a3474f335b4947cf61659eae38294655a1c76ea662fee506
6
+ metadata.gz: a248b17fdaf0faf77e7462e2d69d29efdee698680398643bf575961860a06f996f2114856cc05b146570eac93872786db654c7496389f734084de89bbe129142
7
+ data.tar.gz: 16bcfc3e4ed9e2db7d52358d4630e94cc5bf4158b37a63000e19ce75a5ad9f5b0cb013ec4e8f43f52782e33632476eea25798fd7a7a86a9c13ab65f397a841b6
data/README.md CHANGED
@@ -54,7 +54,7 @@ resource_rules:
54
54
  mention:
55
55
  - markglenfletcher
56
56
  comment: |
57
- {{author}} This issue is unlabelled after 5 days. It needs attention.
57
+ {{author}} This issue is unlabelled after 5 days. It needs attention. Please take care of this before the end of #{2.days.from_now.strftime('%Y-%m-%d')}
58
58
  merge_requests:
59
59
  rules:
60
60
  []
@@ -358,31 +358,8 @@ conditions:
358
358
  This will make it only act on resources which have active milestones and
359
359
  there exists next milestone which has already started.
360
360
 
361
- Here's a list of currently available API:
362
-
363
- ##### API
364
-
365
- | Name | Return type | Description |
366
- | ---- | ---- | ---- |
367
- | milestone | Milestone | The milestone attached to the resource |
368
-
369
- ##### Methods for `Milestone`
370
-
371
- | Method | Return type | Description |
372
- | ---- | ---- | ---- |
373
- | id | Integer | The id of the milestone |
374
- | iid | Integer | The iid of the milestone |
375
- | project_id | Integer | The project id of the milestone if available |
376
- | group_id | Integer | The group id of the milestone if available |
377
- | title | String | The title of the milestone |
378
- | description | String | The description of the milestone |
379
- | state | String | The state of the milestone. Could be `active` or `closed` |
380
- | due_date | Date | The due date of the milestone. Could be `nil` |
381
- | start_date | Date | The start date of the milestone. Could be `nil` |
382
- | updated_at | Time | The updated timestamp of the milestone |
383
- | created_at | Time | The created timestamp of the milestone |
384
- | succ | Milestone | The next active milestone beside this milestone |
385
- | active? | Boolean | `true` if `state` is `active`; `false` otherwise |
361
+ See [Ruby expression API](#ruby-expression-api) for the list of currently
362
+ available API.
386
363
 
387
364
  #### Limits field
388
365
 
@@ -525,6 +502,60 @@ actions:
525
502
  @{{author}} Are you still interested in finishing this merge request?
526
503
  ```
527
504
 
505
+ ###### Ruby expression
506
+
507
+ The comment can also contain Ruby expression, using Ruby's own string
508
+ interpolation syntax: `#{ expression }`. This gives you the most flexibility.
509
+ Suppose you want to mention the next active milestone relative to the one
510
+ associated with the resource, you can write:
511
+
512
+ ```yml
513
+ actions:
514
+ comment: |
515
+ Please move this to %"#{milestone.succ.title}".
516
+ ```
517
+
518
+ See [Ruby expression API](#ruby-expression-api) for the list of currently
519
+ available API.
520
+
521
+ **Note:** If you get a syntax error due to stray braces (`{` or `}`), use `\`
522
+ to escape it. For example:
523
+
524
+ ```yml
525
+ actions:
526
+ comment: |
527
+ If \} comes first and/or following \{, you'll need to escape them. If it's just { wrapping something } then you don't need to, but it's also fine to escape them like \{ this \} if you prefer.
528
+ ```
529
+
530
+ ### Ruby expression API
531
+
532
+ Here's a list of currently available Ruby expression API:
533
+
534
+ ##### API
535
+
536
+ | Name | Return type | Description |
537
+ | ---- | ---- | ---- |
538
+ | resource | Hash | The hash containing the raw data of the resource |
539
+ | milestone | Milestone | The milestone attached to the resource |
540
+
541
+ ##### Methods for `Milestone`
542
+
543
+ | Method | Return type | Description |
544
+ | ---- | ---- | ---- |
545
+ | id | Integer | The id of the milestone |
546
+ | iid | Integer | The iid of the milestone |
547
+ | project_id | Integer | The project id of the milestone if available |
548
+ | group_id | Integer | The group id of the milestone if available |
549
+ | title | String | The title of the milestone |
550
+ | description | String | The description of the milestone |
551
+ | state | String | The state of the milestone. Could be `active` or `closed` |
552
+ | due_date | Date | The due date of the milestone. Could be `nil` |
553
+ | start_date | Date | The start date of the milestone. Could be `nil` |
554
+ | updated_at | Time | The updated timestamp of the milestone |
555
+ | created_at | Time | The created timestamp of the milestone |
556
+ | succ | Milestone | The next active milestone beside this milestone |
557
+ | active? | Boolean | `true` if `state` is `active`; `false` otherwise |
558
+
528
559
  ### Usage
529
560
 
530
561
  ```
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
9
9
  spec.email = ['remy@rymai.me']
10
10
 
11
11
  spec.summary = 'GitLab triage automation project.'
12
- spec.homepage = 'https://gitlab.com/gitlab-org/triage'
12
+ spec.homepage = 'https://gitlab.com/gitlab-org/gitlab-triage'
13
13
  spec.license = 'MIT'
14
14
 
15
15
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
@@ -0,0 +1,28 @@
1
+ require_relative 'action/comment'
2
+
3
+ module Gitlab
4
+ module Triage
5
+ module Action
6
+ def self.process(rules:, **args)
7
+ comment = rules.any? && rules
8
+
9
+ {
10
+ Comment => comment
11
+ }.compact.each do |action, rule|
12
+ act(action: action, rule: rule, **args)
13
+ end
14
+ end
15
+
16
+ def self.act(action:, dry:, **args)
17
+ klass =
18
+ if dry
19
+ action.const_get(:Dry)
20
+ else
21
+ action
22
+ end
23
+
24
+ klass.new(**args).act
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,17 @@
1
+ module Gitlab
2
+ module Triage
3
+ module Action
4
+ class Base
5
+ attr_reader :name, :type, :rule, :resources, :net
6
+
7
+ def initialize(name:, type:, rule:, resources:, net:)
8
+ @name = name
9
+ @type = type
10
+ @rule = rule
11
+ @resources = resources
12
+ @net = net
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,74 @@
1
+ require_relative 'base'
2
+ require_relative '../command_builders/comment_body_builder'
3
+ require_relative '../command_builders/comment_command_builder'
4
+ require_relative '../command_builders/label_command_builder'
5
+ require_relative '../command_builders/remove_label_command_builder'
6
+ require_relative '../command_builders/cc_command_builder'
7
+ require_relative '../command_builders/status_command_builder'
8
+
9
+ module Gitlab
10
+ module Triage
11
+ module Action
12
+ class Comment < Base
13
+ class Dry < Comment
14
+ def act
15
+ puts "\nThe following comments would be posted for the rule **#{name}**:\n\n"
16
+
17
+ super
18
+ end
19
+
20
+ private
21
+
22
+ def perform(resource, comment)
23
+ puts "# #{resource[:web_url]}\n```\n#{comment}\n```\n"
24
+ end
25
+ end
26
+
27
+ def act
28
+ resources.each do |resource|
29
+ comment = build_comment(resource)
30
+
31
+ perform(resource, comment)
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ def build_comment(resource)
38
+ CommandBuilders::CommentCommandBuilder.new(
39
+ [
40
+ CommandBuilders::CommentBodyBuilder.new(rule[:comment], resource: resource, net: net).build_command,
41
+ CommandBuilders::LabelCommandBuilder.new(rule[:labels]).build_command,
42
+ CommandBuilders::RemoveLabelCommandBuilder.new(rule[:remove_labels]).build_command,
43
+ CommandBuilders::CcCommandBuilder.new(rule[:mention]).build_command,
44
+ CommandBuilders::StatusCommandBuilder.new(rule[:status]).build_command
45
+ ]
46
+ ).build_command
47
+ end
48
+
49
+ def perform(resource, comment)
50
+ net[:network].post_api(
51
+ net[:token],
52
+ build_post_url(resource),
53
+ comment)
54
+ end
55
+
56
+ def build_post_url(resource)
57
+ # POST /projects/:id/issues/:issue_iid/notes
58
+ post_url = UrlBuilders::UrlBuilder.new(
59
+ host_url: net[:host_url],
60
+ api_version: net[:api_version],
61
+ source_id: net[:source_id],
62
+ resource_type: type,
63
+ resource_id: resource['iid'],
64
+ sub_resource_type: 'notes'
65
+ ).build
66
+
67
+ puts Gitlab::Triage::UI.debug "post_url: #{post_url}" if net[:debug]
68
+
69
+ post_url
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -1,32 +1,36 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'base_command_builder'
4
+ require_relative '../resource/context'
2
5
 
3
6
  module Gitlab
4
7
  module Triage
5
8
  module CommandBuilders
6
9
  class CommentBodyBuilder < BaseCommandBuilder
7
10
  SUPPORTED_PLACEHOLDERS = {
8
- created_at: "{{created_at}}".freeze,
9
- updated_at: "{{updated_at}}".freeze,
10
- closed_at: "{{closed_at}}".freeze,
11
- merged_at: "{{merged_at}}".freeze,
12
- state: "{{state}}".freeze,
13
- author: "@{{author.username}}".freeze,
14
- assignee: "@{{assignee.username}}".freeze,
15
- assignees: "@{{assignees.username}}".freeze,
16
- closed_by: "@{{closed_by.username}}".freeze,
17
- merged_by: "@{{merged_by.username}}".freeze,
18
- milestone: %(%"{{milestone.title}}").freeze,
19
- labels: %(~"{{labels}}").freeze,
20
- upvotes: "{{upvotes}}".freeze,
21
- downvotes: "{{downvotes}}".freeze
11
+ created_at: "{{created_at}}",
12
+ updated_at: "{{updated_at}}",
13
+ closed_at: "{{closed_at}}",
14
+ merged_at: "{{merged_at}}",
15
+ state: "{{state}}",
16
+ author: "@{{author.username}}",
17
+ assignee: "@{{assignee.username}}",
18
+ assignees: "@{{assignees.username}}",
19
+ closed_by: "@{{closed_by.username}}",
20
+ merged_by: "@{{merged_by.username}}",
21
+ milestone: %(%"{{milestone.title}}"),
22
+ labels: %(~"{{labels}}"),
23
+ upvotes: "{{upvotes}}",
24
+ downvotes: "{{downvotes}}"
22
25
  }.freeze
23
26
  PLACEHOLDER_REGEX = /{{([\w\.]+)}}/
24
27
 
25
- attr_reader :resource
28
+ attr_reader :resource, :net
26
29
 
27
- def initialize(items, resource: nil)
30
+ def initialize(items, resource: nil, net: {})
28
31
  super(items)
29
32
  @resource = resource
33
+ @net = net
30
34
  end
31
35
 
32
36
  private
@@ -38,6 +42,16 @@ module Gitlab
38
42
  def format_item(item)
39
43
  return item unless resource
40
44
 
45
+ replace_placeholders(eval_interpolation(item))
46
+ end
47
+
48
+ def eval_interpolation(item)
49
+ quoted_comment = "%Q{#{item}}"
50
+
51
+ Resource::Context.new(resource, net).eval(quoted_comment)
52
+ end
53
+
54
+ def replace_placeholders(item)
41
55
  SUPPORTED_PLACEHOLDERS.inject(item) do |comment, (placeholder, formatted_text)|
42
56
  next comment unless comment.include?("{{#{placeholder}}}")
43
57
 
@@ -9,12 +9,7 @@ require_relative 'filters/author_member_conditions_filter'
9
9
  require_relative 'filters/assignee_member_conditions_filter'
10
10
  require_relative 'filters/ruby_conditions_filter'
11
11
  require_relative 'limiters/date_field_limiter'
12
- require_relative 'command_builders/comment_body_builder'
13
- require_relative 'command_builders/comment_command_builder'
14
- require_relative 'command_builders/label_command_builder'
15
- require_relative 'command_builders/remove_label_command_builder'
16
- require_relative 'command_builders/cc_command_builder'
17
- require_relative 'command_builders/status_command_builder'
12
+ require_relative 'action'
18
13
  require_relative 'api_query_builders/single_query_param_builder'
19
14
  require_relative 'api_query_builders/multi_query_param_builder'
20
15
  require_relative 'url_builders/url_builder'
@@ -77,6 +72,17 @@ module Gitlab
77
72
  @network ||= Network.new(network_adapter, options)
78
73
  end
79
74
 
75
+ def net
76
+ @net ||= {
77
+ host_url: host_url,
78
+ api_version: api_version,
79
+ token: options.token,
80
+ source_id: options.project_id,
81
+ debug: options.debug,
82
+ network: network
83
+ }
84
+ end
85
+
80
86
  def network_adapter
81
87
  @network_adapter ||= @network_adapter_class.new(options)
82
88
  end
@@ -105,13 +111,17 @@ module Gitlab
105
111
  print "* Limiting resources..."
106
112
  resources = limit_resources(resources, rule_limits(rule))
107
113
  puts "\n* Total after limiting: #{resources.count} resources"
108
- process_resources(resource_type, resources, rule)
114
+ Action.process(
115
+ name: rule[:name],
116
+ type: resource_type,
117
+ rules: rule_actions(rule),
118
+ resources: resources,
119
+ net: net,
120
+ dry: options.dry_run)
109
121
  end
110
122
  end
111
123
 
112
124
  def filter_resources(resources, conditions)
113
- net = { host_url: host_url, api_version: api_version, token: options.token, network: network }
114
-
115
125
  resources.select do |resource|
116
126
  results = []
117
127
 
@@ -135,34 +145,6 @@ module Gitlab
135
145
  end
136
146
  end
137
147
 
138
- def process_resources(resource_type, resources, rule)
139
- if options.dry_run
140
- puts "\nThe following comments would be posted for the rule **#{rule[:name]}**:\n\n"
141
- end
142
-
143
- resources.each do |resource|
144
- comment = build_comment(rule_actions(rule), resource: resource)
145
-
146
- if options.dry_run
147
- puts "# #{resource[:web_url]}\n```\n#{comment}\n```\n"
148
- else
149
- network.post_api(options.token, build_post_url(resource_type, resource), comment)
150
- end
151
- end
152
- end
153
-
154
- def build_comment(actions, resource: nil)
155
- CommandBuilders::CommentCommandBuilder.new(
156
- [
157
- CommandBuilders::CommentBodyBuilder.new(actions[:comment], resource: resource).build_command,
158
- CommandBuilders::LabelCommandBuilder.new(actions[:labels]).build_command,
159
- CommandBuilders::RemoveLabelCommandBuilder.new(actions[:remove_labels]).build_command,
160
- CommandBuilders::CcCommandBuilder.new(actions[:mention]).build_command,
161
- CommandBuilders::StatusCommandBuilder.new(actions[:status]).build_command
162
- ]
163
- ).build_command
164
- end
165
-
166
148
  def build_get_url(resource_type, conditions)
167
149
  # Example issues query with state and labels
168
150
  # https://gitlab.com/api/v4/projects/test-triage%2Fissue-project/issues?state=open&labels=project%20label%20with%20spaces,group_label_no_spaces
@@ -191,22 +173,6 @@ module Gitlab
191
173
 
192
174
  get_url
193
175
  end
194
-
195
- def build_post_url(resource_type, resource)
196
- # POST /projects/:id/issues/:issue_iid/notes
197
- post_url = UrlBuilders::UrlBuilder.new(
198
- host_url: host_url,
199
- api_version: api_version,
200
- source_id: options.project_id,
201
- resource_type: resource_type,
202
- resource_id: resource['iid'],
203
- sub_resource_type: 'notes'
204
- ).build
205
-
206
- puts Gitlab::Triage::UI.debug "post_url: #{post_url}" if options.debug
207
-
208
- post_url
209
- end
210
176
  end
211
177
  end
212
178
  end
@@ -27,10 +27,10 @@ module Gitlab
27
27
 
28
28
  def limit
29
29
  case @criterion
30
- when :most_recent
31
- @resources.first(@threshold)
32
30
  when :oldest
33
- @resources.last(@threshold)
31
+ @resources.first(@threshold)
32
+ when :most_recent
33
+ @resources.last(@threshold).reverse
34
34
  end
35
35
  end
36
36
 
@@ -1,5 +1,5 @@
1
1
  module Gitlab
2
2
  module Triage
3
- VERSION = '0.8.1'.freeze
3
+ VERSION = '0.9.0'.freeze
4
4
  end
5
5
  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: 0.8.1
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - GitLab
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-08-15 00:00:00.000000000 Z
11
+ date: 2018-09-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -131,6 +131,9 @@ files:
131
131
  - bin/gitlab-triage
132
132
  - gitlab-triage.gemspec
133
133
  - lib/gitlab/triage.rb
134
+ - lib/gitlab/triage/action.rb
135
+ - lib/gitlab/triage/action/base.rb
136
+ - lib/gitlab/triage/action/comment.rb
134
137
  - lib/gitlab/triage/api_query_builders/base_query_param_builder.rb
135
138
  - lib/gitlab/triage/api_query_builders/multi_query_param_builder.rb
136
139
  - lib/gitlab/triage/api_query_builders/single_query_param_builder.rb
@@ -172,7 +175,7 @@ files:
172
175
  - lib/gitlab/triage/version.rb
173
176
  - support/.gitlab-ci.example.yml
174
177
  - support/.triage-policies.example.yml
175
- homepage: https://gitlab.com/gitlab-org/triage
178
+ homepage: https://gitlab.com/gitlab-org/gitlab-triage
176
179
  licenses:
177
180
  - MIT
178
181
  metadata: {}