gitlab-triage 0.13.0 → 0.14.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: 587e71e912b7d8906ecbc081b1a07258aa6cfe80643ea34c38314256a97ab503
4
- data.tar.gz: a83dc6d31326079c8315d24af259d9015c13bb2d759684831633fd295dd0b8a1
3
+ metadata.gz: 636a4953fea53e7808b154e352c37d6a33c3cd9a57695f98d05fa90a44efd2cc
4
+ data.tar.gz: 82d61352bdd126d09650ed9f0efcbf1714783444f172a559571d33530000bef2
5
5
  SHA512:
6
- metadata.gz: ec66a05c1d691deb9eab031e69062762e492301a09fe27e2c3bf85f876b36459d715d2e6c82b6dd39706e568171643316a7558046347d07bb034740d5e0a4d32
7
- data.tar.gz: '0359fe4894503d53ba358687b66854d9153a53177ba04ebbadb9106d6d824ae0ad1bc3c2d570834b3ed81cb3ca90f2dd67555c3758ae7e254e1ca8555a825aa8'
6
+ metadata.gz: '069d55c96a5fac0989ced21614782c06b11ef8a56fb59e7bc457e73fa9bb77c223680ee1399f4854bc22697075880ef3233575a1a332ab75b934664a2a654d44'
7
+ data.tar.gz: 1521bc67e168f19d231112782f9d12457a422b93e86cfa10a4565eebb026bdbfb8e6401764ecb84a25bbbfe3687f7c189323e106de4ad2b6020618f75dde99fb
data/README.md CHANGED
@@ -6,9 +6,11 @@ This project contains the library and pipeline definition to enable automated tr
6
6
 
7
7
  ## gitlab-triage gem
8
8
 
9
- ### Summary
9
+ ### Abstract
10
10
 
11
- The `gitlab-triage` gem aims to enable project managers and maintainers to automatically triage Issues and Merge Requests in GitLab projects based on defined policies.
11
+ The `gitlab-triage` gem aims to enable project managers and maintainers to
12
+ automatically triage Issues and Merge Requests in GitLab projects based on
13
+ defined policies.
12
14
 
13
15
  ### What is a triage policy?
14
16
 
@@ -16,14 +18,21 @@ Triage policies are defined on a resource level basis, resources being:
16
18
  - Issues
17
19
  - Merge Requests
18
20
 
19
- Each policy can declare a number of conditions that must all be satisfied before a number of actions are carried out.
21
+ Each policy can declare a number of conditions that must all be satisfied before
22
+ a number of actions are carried out.
23
+
24
+ Summary policies are special policies that join multiple policies together to
25
+ create a summary issue with all the sub-policies' summaries, see
26
+ [Summary policies](#summary-policies).
20
27
 
21
28
  ### Defining a policy
22
29
 
23
30
  Policies are defined in a policy file (by default `./.triage-policies.yml`).
24
31
  The format of the file is [YAML](https://en.wikipedia.org/wiki/YAML).
25
32
 
26
- > Note: You can use the [`--init`](#usage) option to add an example [`.triage-policies.yml` file](support/.triage-policies.example.yml) to your project
33
+ > Note: You can use the [`--init`](#usage) option to add an example
34
+ [`.triage-policies.yml` file](support/.triage-policies.example.yml) to your
35
+ project.
27
36
 
28
37
  Select which resource to add the policy to:
29
38
  - `issues`
@@ -602,11 +611,11 @@ Generates an issue summarizing what was triaged.
602
611
 
603
612
  Accepts a hash of fields.
604
613
 
605
- | Field | Type | Description | Required | Placeholders | Ruby expression |
606
- | ---- | ---- | ---- | ---- | ---- | ---- |
607
- | `title` | string | The title of the generated issue | yes | no | no |
614
+ | Field | Type | Description | Required | Placeholders | Ruby expression |
615
+ | ---- | ---- | ---- | ---- | ---- | ---- |
616
+ | `title` | string | The title of the generated issue | yes | no | no |
608
617
  | `item` | string | Template representing each triaged resource | no | yes | yes |
609
- | `summary` | string | The description of the generated issue | no | Only `{{items}}` and `{{title}}` | yes |
618
+ | `summary` | string | The description of the generated issue | no | Only `{{items}}` and `{{title}}` | yes |
610
619
 
611
620
  **Note:**: Both `item` and `summary` fields act like a
612
621
  [comment action](#comment-action), therefore
@@ -658,6 +667,86 @@ Which could generate an issue like:
658
667
  /label ~"needs attention"
659
668
  ```
660
669
 
670
+ ### Summary policies
671
+
672
+ Summary policies are special policies that join multiple rule policies together
673
+ to create a summary issue with all the sub-policies' summaries.
674
+ They have the same structure as Rule policies that define `actions.summarize`.
675
+
676
+ One key difference is that the `{{items}}` placeholder represents the array of
677
+ sub-policies' summary.
678
+
679
+ Note that only the `item` and `summary` keys in the sub-policies' `actions` are
680
+ used. Any other keys (e.g. `mention`, `comment`, `labels` etc.) are ignored.
681
+
682
+ You can define such policy as follows:
683
+
684
+ ```yml
685
+ resource_rules:
686
+ issues:
687
+ summaries:
688
+ - name: Newest and oldest issues summary
689
+ actions:
690
+ summarize:
691
+ title: "Newest and oldest issues summary"
692
+ summary: |
693
+ Please triage the following issues:
694
+
695
+ {{items}}
696
+
697
+ Please take care of them before the end of #{7.days.from_now.strftime('%Y-%m-%d')}
698
+
699
+ /label ~"needs attention"
700
+ rules:
701
+ - name: New issues
702
+ conditions:
703
+ state: opened
704
+ limits:
705
+ most_recent: 2
706
+ actions:
707
+ item: "- [ ] [{{title}}]({{web_url}}) {{labels}}"
708
+ summary: |
709
+ Please triage the following new issues:
710
+
711
+ {{items}}
712
+ - name: Old issues
713
+ conditions:
714
+ state: opened
715
+ limits:
716
+ oldest: 2
717
+ actions:
718
+ item: "- [ ] [{{title}}]({{web_url}}) {{labels}}"
719
+ summary: |
720
+ Please triage the following old issues:
721
+
722
+ {{items}}
723
+ ```
724
+
725
+ Which could generate an issue like:
726
+
727
+ * Title:
728
+ ```
729
+ Newest and oldest issues summary
730
+ ```
731
+ * Description:
732
+ ``` markdown
733
+ Please triage the following issues:
734
+
735
+ Please triage the following new issues:
736
+
737
+ - [ ] [A new issue](http://example.com/group/project/issues/4)
738
+ - [ ] [Another new issue](http://example.com/group/project/issues/3) ~"label B", ~"label C"
739
+
740
+ Please triage the following old issues:
741
+
742
+ - [ ] [An old issue](http://example.com/group/project/issues/1) ~"label A", ~"label B"
743
+ - [ ] [Another old issue](http://example.com/group/project/issues/2) ~"label C"
744
+
745
+ Please take care of them before the end of 2000-01-01
746
+
747
+ /label ~"needs attention"
748
+ ```
749
+
661
750
  ### Ruby expression API
662
751
 
663
752
  Here's a list of currently available Ruby expression API:
@@ -741,11 +830,13 @@ run:triage:triage:
741
830
  Yes, you can override the host url using the following options:
742
831
 
743
832
  ##### CLI
833
+
744
834
  ```
745
835
  gitlab-triage --dry-run --token $API_TOKEN --project-id gitlab-org/triage --host-url https://gitlab.host.com
746
836
  ```
747
837
 
748
838
  ##### Policy file
839
+
749
840
  ```yml
750
841
  host_url: https://gitlab.host.com
751
842
  resource_rules:
@@ -753,4 +844,4 @@ resource_rules:
753
844
 
754
845
  ### Contributing
755
846
 
756
- Please refer to the [Contributing Guide](CONTRIBUTING.md)
847
+ Please refer to the [Contributing Guide](CONTRIBUTING.md).
@@ -4,15 +4,12 @@ require_relative 'action/comment'
4
4
  module Gitlab
5
5
  module Triage
6
6
  module Action
7
- def self.process(rules:, **args)
8
- summarize = rules.delete(:summarize)
9
- comment = rules.any? && rules
10
-
7
+ def self.process(policy:, **args)
11
8
  {
12
- Summarize => summarize,
13
- Comment => comment
14
- }.compact.each do |action, rule|
15
- act(action: action, rule: rule, **args) if rule
9
+ Summarize => policy.summarize?,
10
+ Comment => policy.comment?
11
+ }.compact.each do |action, active|
12
+ act(action: action, policy: policy, **args) if active
16
13
  end
17
14
  end
18
15
 
@@ -2,13 +2,10 @@ module Gitlab
2
2
  module Triage
3
3
  module Action
4
4
  class Base
5
- attr_reader :name, :type, :rule, :resources, :net
5
+ attr_reader :policy, :net
6
6
 
7
- def initialize(name:, type:, rule:, resources:, net:)
8
- @name = name
9
- @type = type
10
- @rule = rule
11
- @resources = resources
7
+ def initialize(policy:, net:)
8
+ @policy = policy
12
9
  @net = net
13
10
  end
14
11
  end
@@ -14,7 +14,7 @@ module Gitlab
14
14
  class Comment < Base
15
15
  class Dry < Comment
16
16
  def act
17
- puts "\nThe following comments would be posted for the rule **#{name}**:\n\n"
17
+ puts "The following comments would be posted for the rule **#{policy.name}**:\n\n"
18
18
 
19
19
  super
20
20
  end
@@ -27,7 +27,7 @@ module Gitlab
27
27
  end
28
28
 
29
29
  def act
30
- resources.each do |resource|
30
+ policy.resources.each do |resource|
31
31
  comment = build_comment(resource).strip
32
32
 
33
33
  perform(resource, comment) unless comment.empty?
@@ -39,11 +39,11 @@ module Gitlab
39
39
  def build_comment(resource)
40
40
  CommandBuilders::CommentCommandBuilder.new(
41
41
  [
42
- CommandBuilders::TextContentBuilder.new(rule[:comment], resource: resource, net: net).build_command,
43
- CommandBuilders::LabelCommandBuilder.new(rule[:labels]).build_command,
44
- CommandBuilders::RemoveLabelCommandBuilder.new(rule[:remove_labels]).build_command,
45
- CommandBuilders::CcCommandBuilder.new(rule[:mention]).build_command,
46
- CommandBuilders::StatusCommandBuilder.new(rule[:status]).build_command
42
+ CommandBuilders::TextContentBuilder.new(policy.actions[:comment], resource: resource, net: net).build_command,
43
+ CommandBuilders::LabelCommandBuilder.new(policy.actions[:labels]).build_command,
44
+ CommandBuilders::RemoveLabelCommandBuilder.new(policy.actions[:remove_labels]).build_command,
45
+ CommandBuilders::CcCommandBuilder.new(policy.actions[:mention]).build_command,
46
+ CommandBuilders::StatusCommandBuilder.new(policy.actions[:status]).build_command
47
47
  ]
48
48
  ).build_command
49
49
  end
@@ -61,7 +61,7 @@ module Gitlab
61
61
  host_url: net[:host_url],
62
62
  api_version: net[:api_version],
63
63
  source_id: net[:source_id],
64
- resource_type: type,
64
+ resource_type: policy.type,
65
65
  resource_id: resource['iid'],
66
66
  sub_resource_type: 'notes'
67
67
  ).build
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'base'
4
- require_relative 'summarize/issue_builder'
5
4
 
6
5
  module Gitlab
7
6
  module Triage
@@ -11,7 +10,7 @@ module Gitlab
11
10
  private
12
11
 
13
12
  def perform
14
- puts "\nThe following issue would be created for the rule **#{name}**:\n\n"
13
+ puts "The following issue would be created for the rule **#{policy.name}**:\n\n"
15
14
  puts ">>>"
16
15
  puts "* Title: #{issue.title}"
17
16
  puts "* Description: #{issue.description}"
@@ -20,7 +19,7 @@ module Gitlab
20
19
  end
21
20
 
22
21
  def act
23
- perform if resources.any? && issue.valid?
22
+ perform if policy.resources.any? && issue.valid?
24
23
  end
25
24
 
26
25
  private
@@ -30,7 +29,7 @@ module Gitlab
30
29
  end
31
30
 
32
31
  def issue
33
- @issue ||= IssueBuilder.new(rule, resources, net)
32
+ @issue ||= policy.build_issue
34
33
  end
35
34
 
36
35
  def post_issue_url
@@ -10,6 +10,8 @@ require_relative 'filters/assignee_member_conditions_filter'
10
10
  require_relative 'filters/ruby_conditions_filter'
11
11
  require_relative 'limiters/date_field_limiter'
12
12
  require_relative 'action'
13
+ require_relative 'policies/rule_policy'
14
+ require_relative 'policies/summary_policy'
13
15
  require_relative 'api_query_builders/single_query_param_builder'
14
16
  require_relative 'api_query_builders/multi_query_param_builder'
15
17
  require_relative 'url_builders/url_builder'
@@ -42,14 +44,12 @@ module Gitlab
42
44
  puts Gitlab::Triage::UI.header("Triaging the `#{options.project_id}` project", char: '=')
43
45
  puts
44
46
 
45
- resource_rules.each do |type, resource|
46
- puts Gitlab::Triage::UI.header("Processing rules for #{type}", char: '-')
47
+ resource_rules.each do |resource_type, resource|
48
+ puts Gitlab::Triage::UI.header("Processing rules for #{resource_type}", char: '-')
47
49
  puts
48
- resource[:rules].each do |rule|
49
- puts Gitlab::Triage::UI.header("Processing rule: **#{rule[:name]}**", char: '-')
50
- puts
51
- process_rule(type, rule)
52
- end
50
+
51
+ process_summaries(resource_type, resource[:summaries])
52
+ process_rules(resource_type, resource[:rules])
53
53
  end
54
54
  end
55
55
 
@@ -94,15 +94,45 @@ module Gitlab
94
94
  rule.fetch(:conditions) { {} }
95
95
  end
96
96
 
97
- def rule_actions(rule)
98
- rule.fetch(:actions) { {} }
99
- end
100
-
101
97
  def rule_limits(rule)
102
98
  rule.fetch(:limits) { {} }
103
99
  end
104
100
 
105
- def process_rule(resource_type, rule)
101
+ def process_summaries(resource_type, summaries)
102
+ return if summaries.blank?
103
+
104
+ summaries.each do |summary|
105
+ process_summary(resource_type, summary)
106
+ end
107
+ end
108
+
109
+ def process_rules(resource_type, rules)
110
+ return if rules.blank?
111
+
112
+ rules.each do |rule|
113
+ process_action(Policies::RulePolicy.new(resource_type, rule, resources_for_rule(resource_type, rule), net))
114
+ end
115
+ end
116
+
117
+ def process_summary(resource_type, summary)
118
+ puts Gitlab::Triage::UI.header("Processing summary: **#{summary[:name]}**", char: '~')
119
+ puts
120
+
121
+ resources = resources_for_rules(resource_type, summary[:rules])
122
+ # { summary_rule => resources }
123
+ summary_parts = Hash[summary[:rules].zip(resources)]
124
+
125
+ process_action(Policies::SummaryPolicy.new(resource_type, summary, summary_parts, net))
126
+ end
127
+
128
+ def resources_for_rules(resource_type, rules)
129
+ rules.map { |rule| resources_for_rule(resource_type, rule) }
130
+ end
131
+
132
+ def resources_for_rule(resource_type, rule)
133
+ puts Gitlab::Triage::UI.header("Processing rule: **#{rule[:name]}**", char: '-')
134
+ resources = []
135
+
106
136
  ExpandCondition.perform(rule_conditions(rule)) do |conditions|
107
137
  # retrieving the resources for every rule is inefficient
108
138
  # however, previous rules may affect those upcoming
@@ -114,14 +144,18 @@ module Gitlab
114
144
  print "* Limiting resources..."
115
145
  resources = limit_resources(resources, rule_limits(rule))
116
146
  puts "\n* Total after limiting: #{resources.count} resources"
117
- Action.process(
118
- name: rule[:name],
119
- type: resource_type,
120
- rules: rule_actions(rule),
121
- resources: resources,
122
- net: net,
123
- dry: options.dry_run)
147
+ puts
124
148
  end
149
+
150
+ resources
151
+ end
152
+
153
+ def process_action(policy)
154
+ Action.process(
155
+ policy: policy,
156
+ net: net,
157
+ dry: options.dry_run)
158
+ puts
125
159
  end
126
160
 
127
161
  def filter_resources(resources, conditions)
@@ -1,13 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../base'
4
- require_relative '../../command_builders/text_content_builder'
3
+ require_relative '../command_builders/text_content_builder'
5
4
 
6
5
  module Gitlab
7
6
  module Triage
8
- module Action
7
+ module EntityBuilders
9
8
  class IssueBuilder
10
9
  attr_reader :title
10
+ attr_writer :description, :items
11
11
 
12
12
  def initialize(action, resources, net)
13
13
  @title = action[:title]
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gitlab
4
+ module Triage
5
+ module Policies
6
+ BasePolicy = Struct.new(:type, :policy_spec, :resources, :net) do
7
+ def name
8
+ @name ||= (policy_spec[:name] || "#{type}-#{object_id}")
9
+ end
10
+
11
+ def actions
12
+ @actions ||= policy_spec.fetch(:actions) { {} }
13
+ end
14
+
15
+ def summarize?
16
+ actions.key?(:summarize)
17
+ end
18
+
19
+ def comment?
20
+ (actions.keys - [:summarize]).any?
21
+ end
22
+
23
+ def build_issue
24
+ raise NotImplementedError
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base_policy'
4
+ require_relative '../entity_builders/issue_builder'
5
+
6
+ module Gitlab
7
+ module Triage
8
+ module Policies
9
+ class RulePolicy < BasePolicy
10
+ # Build an issue from a single rule policy
11
+ def build_issue
12
+ EntityBuilders::IssueBuilder.new(actions, resources, net)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base_policy'
4
+ require_relative 'rule_policy'
5
+ require_relative '../entity_builders/issue_builder'
6
+
7
+ module Gitlab
8
+ module Triage
9
+ module Policies
10
+ class SummaryPolicy < BasePolicy
11
+ # Build an issue from several rules policies
12
+ def build_issue
13
+ EntityBuilders::IssueBuilder.new(actions[:summarize].merge(item: "{{description}}\n\n"), [], net).tap do |issue|
14
+ issue.items =
15
+ resources.map do |inner_policy_spec, inner_resources|
16
+ Policies::RulePolicy.new(type, inner_policy_spec, inner_resources, net).build_issue.description
17
+ end.join("\n\n")
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -1,5 +1,5 @@
1
1
  module Gitlab
2
2
  module Triage
3
- VERSION = '0.13.0'.freeze
3
+ VERSION = '0.14.0'.freeze
4
4
  end
5
5
  end
@@ -14,6 +14,36 @@ resource_rules:
14
14
  actions:
15
15
  comment: |
16
16
  This issue has been open for one week and is unlabelled
17
+ summaries:
18
+ - name: Newest and oldest issues summary
19
+ actions:
20
+ summarize:
21
+ title: "Newest and oldest issues summary"
22
+ summary: |
23
+ Please triage the following issues:
24
+
25
+ {{items}}
26
+
27
+ Please take care of them before the end of #{7.days.from_now.strftime('%Y-%m-%d')}
28
+
29
+ /label ~"needs attention"
30
+ rules:
31
+ - name: New issues
32
+ conditions:
33
+ state: opened
34
+ limits:
35
+ most_recent: 2
36
+ actions:
37
+ item: "- [ ] [{{title}}]({{web_url}}) {{labels}}"
38
+ summary: "Please triage the following new issues:\n\n{{items}}"
39
+ - name: Old issues
40
+ conditions:
41
+ state: opened
42
+ limits:
43
+ oldest: 2
44
+ actions:
45
+ item: "- [ ] [{{title}}]({{web_url}}) {{labels}}"
46
+ summary: "Please triage the following old issues:\n\n{{items}}"
17
47
  merge_requests:
18
48
  rules:
19
49
  []
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.13.0
4
+ version: 0.14.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-11-12 00:00:00.000000000 Z
11
+ date: 2018-11-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -135,7 +135,6 @@ files:
135
135
  - lib/gitlab/triage/action/base.rb
136
136
  - lib/gitlab/triage/action/comment.rb
137
137
  - lib/gitlab/triage/action/summarize.rb
138
- - lib/gitlab/triage/action/summarize/issue_builder.rb
139
138
  - lib/gitlab/triage/api_query_builders/base_query_param_builder.rb
140
139
  - lib/gitlab/triage/api_query_builders/multi_query_param_builder.rb
141
140
  - lib/gitlab/triage/api_query_builders/single_query_param_builder.rb
@@ -147,6 +146,7 @@ files:
147
146
  - lib/gitlab/triage/command_builders/status_command_builder.rb
148
147
  - lib/gitlab/triage/command_builders/text_content_builder.rb
149
148
  - lib/gitlab/triage/engine.rb
149
+ - lib/gitlab/triage/entity_builders/issue_builder.rb
150
150
  - lib/gitlab/triage/expand_condition.rb
151
151
  - lib/gitlab/triage/expand_condition/expansion.rb
152
152
  - lib/gitlab/triage/expand_condition/list.rb
@@ -167,6 +167,9 @@ files:
167
167
  - lib/gitlab/triage/network_adapters/base_adapter.rb
168
168
  - lib/gitlab/triage/network_adapters/httparty_adapter.rb
169
169
  - lib/gitlab/triage/network_adapters/test_adapter.rb
170
+ - lib/gitlab/triage/policies/base_policy.rb
171
+ - lib/gitlab/triage/policies/rule_policy.rb
172
+ - lib/gitlab/triage/policies/summary_policy.rb
170
173
  - lib/gitlab/triage/resource/base.rb
171
174
  - lib/gitlab/triage/resource/context.rb
172
175
  - lib/gitlab/triage/resource/instance_version.rb