gitlab-triage 1.4.2 → 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitlab-ci.yml +1 -1
- data/.rubocop.yml +7 -0
- data/Gemfile +1 -3
- data/README.md +53 -46
- data/gitlab-triage.gemspec +1 -1
- data/lib/gitlab/triage/action/summarize.rb +20 -2
- data/lib/gitlab/triage/engine.rb +12 -2
- data/lib/gitlab/triage/entity_builders/issue_builder.rb +5 -0
- data/lib/gitlab/triage/filters/{date_conditions_filter.rb → issuable_date_conditions_filter.rb} +6 -2
- data/lib/gitlab/triage/filters/merge_request_date_conditions_filter.rb +23 -0
- data/lib/gitlab/triage/resource/label_event.rb +1 -0
- data/lib/gitlab/triage/resource/milestone.rb +16 -0
- data/lib/gitlab/triage/retryable.rb +1 -1
- data/lib/gitlab/triage/version.rb +1 -1
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9187a7efe616ce28b479744eea6e43478dd680e33c026db5994a031b36432102
|
4
|
+
data.tar.gz: 89e49562453a05254b5734d735849db38ab5ad4a6e0fc38b9f3d159011f264af
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5486b57cda76c1943e85ecab08a4613ba4a139874602abc034983d967a18fbe48f876607f7d0f26c8f7109538754c4e0f868b07c96c486ac6b370c2586bb1e0a
|
7
|
+
data.tar.gz: ef43a5344601f546c8c634da223c4d36cfec219a3fd85f98a3f56cff7f88df917bf1a5d100d0524745941db98d2bbb12fce31a941a65940dc0675dbda6c375dc
|
data/.gitlab-ci.yml
CHANGED
data/.rubocop.yml
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
# GitLab Triage Project
|
4
4
|
|
5
|
-
This project contains the library and pipeline definition to enable automated triaging of issues in the [GitLab
|
5
|
+
This project contains the library and pipeline definition to enable automated triaging of issues in the [GitLab Project](https://gitlab.com/gitlab-org/gitlab).
|
6
6
|
|
7
7
|
## gitlab-triage gem
|
8
8
|
|
@@ -33,7 +33,7 @@ create a summary issue with all the sub-policies' summaries, see
|
|
33
33
|
Policies are defined in a policy file (by default `./.triage-policies.yml`).
|
34
34
|
The format of the file is [YAML](https://en.wikipedia.org/wiki/YAML).
|
35
35
|
|
36
|
-
> Note
|
36
|
+
> **Note:** You can use the [`--init`](#usage) option to add an example
|
37
37
|
[`.triage-policies.yml` file](support/.triage-policies.example.yml) to your
|
38
38
|
project.
|
39
39
|
|
@@ -69,6 +69,7 @@ resource_rules:
|
|
69
69
|
comment: |
|
70
70
|
{{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')}
|
71
71
|
summarize:
|
72
|
+
destination: gitlab-org/gitlab-triage
|
72
73
|
title: |
|
73
74
|
#{resource[:type].capitalize} require labels
|
74
75
|
item: |
|
@@ -127,12 +128,14 @@ Available condition types:
|
|
127
128
|
|
128
129
|
Accepts a hash of fields.
|
129
130
|
|
130
|
-
| Field | Type | Values
|
131
|
-
| --------- | ---- | ----
|
132
|
-
| `attribute` | string | `created_at`, `updated_at`
|
133
|
-
| `condition` | string | `older_than`, `newer_than`
|
134
|
-
| `interval_type` | string | `days`, `weeks`, `months`, `years`
|
135
|
-
| `interval` | integer | integer
|
131
|
+
| Field | Type | Values | Required |
|
132
|
+
| --------- | ---- | ---- | -------- |
|
133
|
+
| `attribute` | string | `created_at`, `updated_at`, `merged_at` | yes |
|
134
|
+
| `condition` | string | `older_than`, `newer_than` | yes |
|
135
|
+
| `interval_type` | string | `days`, `weeks`, `months`, `years` | yes |
|
136
|
+
| `interval` | integer | integer | yes |
|
137
|
+
|
138
|
+
> **Note:** `merged_at` only works on merge requests.
|
136
139
|
|
137
140
|
Example:
|
138
141
|
|
@@ -197,7 +200,7 @@ conditions:
|
|
197
200
|
|
198
201
|
Accepts an array of strings. Each element in the array represents the name of a label to filter on.
|
199
202
|
|
200
|
-
> Note
|
203
|
+
> **Note:** **All** specified labels must be present on the resource for the condition to be satisfied
|
201
204
|
|
202
205
|
Example:
|
203
206
|
|
@@ -216,7 +219,7 @@ expansion:
|
|
216
219
|
1. List: `{ apple, orange }`
|
217
220
|
2. Sequence: `{1..4}`
|
218
221
|
|
219
|
-
> Note
|
222
|
+
> **Note:**
|
220
223
|
> - Spaces around the items are ignored.
|
221
224
|
> - Do not rely on the expansion ordering. This is subject to change.
|
222
225
|
|
@@ -264,7 +267,7 @@ resource_rules:
|
|
264
267
|
- Quality
|
265
268
|
```
|
266
269
|
|
267
|
-
> Note
|
270
|
+
> **Note:**
|
268
271
|
> If you want to define a full label expansion, you'll need to [force string](https://yaml.org/YAML_for_ruby.html#forcing_strings) or [quote string](https://yaml.org/YAML_for_ruby.html#single-quoted_strings) because otherwise it won't be considered a string due to the YAML parser.
|
269
272
|
> For example, we can quote the expression like `'{ apple, orange }'`, which will create 2 rules, for the two specified labels.
|
270
273
|
|
@@ -337,7 +340,7 @@ resource_rules:
|
|
337
340
|
|
338
341
|
Accepts an array of strings. Each element in the array represents the name of a label to filter on.
|
339
342
|
|
340
|
-
> Note
|
343
|
+
> **Note:** **All** specified labels must be absent on the resource for the condition to be satisfied
|
341
344
|
|
342
345
|
Example:
|
343
346
|
|
@@ -373,7 +376,7 @@ Accepts a hash of fields.
|
|
373
376
|
| --------- | ---- | ---- | -------- |
|
374
377
|
| `source` | string | `group`, `project` | yes |
|
375
378
|
| `condition` | string | `member_of`, `not_member_of` | yes |
|
376
|
-
| `source_id` | integer or string | gitlab-org/gitlab
|
379
|
+
| `source_id` | integer or string | gitlab-org/gitlab | yes |
|
377
380
|
|
378
381
|
Example:
|
379
382
|
|
@@ -395,7 +398,7 @@ Accepts a hash of fields.
|
|
395
398
|
| --------- | ---- | ---- | -------- |
|
396
399
|
| `source` | string | `group`, `project` | yes |
|
397
400
|
| `condition` | string | `member_of`, `not_member_of` | yes |
|
398
|
-
| `source_id` | integer or string | gitlab-org/gitlab
|
401
|
+
| `source_id` | integer or string | gitlab-org/gitlab | yes |
|
399
402
|
|
400
403
|
Example:
|
401
404
|
|
@@ -658,14 +661,14 @@ actions:
|
|
658
661
|
See [Ruby expression API](#ruby-expression-api) for the list of currently
|
659
662
|
available API.
|
660
663
|
|
661
|
-
**Note:** If you get a syntax error due to stray braces (`{` or `}`), use `\`
|
664
|
+
> **Note:** If you get a syntax error due to stray braces (`{` or `}`), use `\`
|
662
665
|
to escape it. For example:
|
663
|
-
|
664
|
-
```yml
|
665
|
-
actions:
|
666
|
-
comment: |
|
667
|
-
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.
|
668
|
-
```
|
666
|
+
>
|
667
|
+
> ```yml
|
668
|
+
> actions:
|
669
|
+
> comment: |
|
670
|
+
> 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.
|
671
|
+
> ```
|
669
672
|
|
670
673
|
##### Summarize action
|
671
674
|
|
@@ -673,29 +676,14 @@ Generates an issue summarizing what was triaged.
|
|
673
676
|
|
674
677
|
Accepts a hash of fields.
|
675
678
|
|
676
|
-
| Field
|
677
|
-
| ----
|
678
|
-
| `title`
|
679
|
-
| `
|
680
|
-
| `
|
679
|
+
| Field | Type | Description | Required | Placeholders | Ruby expression | Default |
|
680
|
+
| ---- | ---- | ---- | ---- | ---- | ---- | ---- |
|
681
|
+
| `title` | string | The title of the generated issue | yes | yes | no | |
|
682
|
+
| `destination` | integer or string | The project ID or path to create the generated issue in | no | no | no | source project |
|
683
|
+
| `item` | string | Template representing each triaged resource | no | yes | yes | |
|
684
|
+
| `summary` | string | The description of the generated issue | no | Only `{{title}}`, `{{items}}`, `{{type}}` | yes | |
|
681
685
|
| `redact_confidential_resources` | boolean | Whether redact fields for confidential resources | no | no | no | true |
|
682
686
|
|
683
|
-
**Note:**: Both `item` and `summary` fields act like a
|
684
|
-
[comment action](#comment-action), therefore
|
685
|
-
[Ruby expression](#ruby-expression) is supported.
|
686
|
-
Placeholders work regularly for `item`, but for `summary` only
|
687
|
-
`{{title}}`, `{{items}}`, `{{type}}` are supported because it's not tied to a
|
688
|
-
particular resource like the comment action.
|
689
|
-
|
690
|
-
**Note:**: No issues will be created if the specific policy doesn't yield
|
691
|
-
any resources.
|
692
|
-
|
693
|
-
**Note:**: `redact_confidential_resources` defaults to `true`, so fields on
|
694
|
-
confidential resources will be converted to `(confidential)` except for
|
695
|
-
`{{web_url}}`. Setting it to `false` will reveal the confidential fields.
|
696
|
-
This will be useful if the summary is confidential itself (not implemented
|
697
|
-
yet), or if we're posting to another private project (not implemented yet).
|
698
|
-
|
699
687
|
The following placeholders are supported for `summary`:
|
700
688
|
|
701
689
|
- `title`: The title of the generated issue
|
@@ -703,6 +691,21 @@ The following placeholders are supported for `summary`:
|
|
703
691
|
- `type`: The resource type for the summary. For now `issues` or
|
704
692
|
`merge_requests`
|
705
693
|
|
694
|
+
> **Note:**
|
695
|
+
> - Both `item` and `summary` fields act like a [comment action](#comment-action),
|
696
|
+
> therefore [Ruby expression](#ruby-expression) is supported.
|
697
|
+
> - Placeholders work regularly for `item`, but for `summary` only `{{title}}`,
|
698
|
+
> `{{items}}`, `{{type}}` are supported because it's not tied to a particular
|
699
|
+
> resource like the comment action.
|
700
|
+
> - No issues will be created if:
|
701
|
+
> - the specific policy doesn't yield any resources; or
|
702
|
+
> - the source type is a group and `destination` is not set.
|
703
|
+
> - `redact_confidential_resources` defaults to `true`, so fields on
|
704
|
+
> confidential resources will be converted to `(confidential)` except for
|
705
|
+
> `{{web_url}}`. Setting it to `false` will reveal the confidential fields.
|
706
|
+
> This will be useful if the summary is confidential itself (not implemented
|
707
|
+
> yet), or if we're posting to another private project (not implemented yet).
|
708
|
+
|
706
709
|
Example:
|
707
710
|
|
708
711
|
```yml
|
@@ -828,9 +831,9 @@ Which could generate an issue like:
|
|
828
831
|
/label ~"needs attention"
|
829
832
|
```
|
830
833
|
|
831
|
-
**Note
|
832
|
-
the corresponding description. If all policies yield no resources,
|
833
|
-
no issues will be created.
|
834
|
+
> **Note:** If a specific policy doesn't yield any resources, it will not
|
835
|
+
> generate the corresponding description. If all policies yield no resources,
|
836
|
+
> then no issues will be created.
|
834
837
|
|
835
838
|
### Ruby expression API
|
836
839
|
|
@@ -865,6 +868,10 @@ Here's a list of currently available Ruby expression API:
|
|
865
868
|
| created_at | Time | The created timestamp of the milestone |
|
866
869
|
| succ | Milestone | The next active milestone beside this milestone |
|
867
870
|
| active? | Boolean | `true` if `state` is `active`; `false` otherwise |
|
871
|
+
| closed? | Boolean | `true` if `state` is `closed`; `false` otherwise |
|
872
|
+
| started? | Boolean | `true` if `start_date` exists and in the past; `false` otherwise |
|
873
|
+
| expired? | Boolean | `true` if `due_date` exists and in the past; `false` otherwise |
|
874
|
+
| in_progress?| Boolean | `true` if `started?` and `!expired`; `false` otherwise |
|
868
875
|
|
869
876
|
##### Methods for `Label`
|
870
877
|
|
@@ -952,7 +959,7 @@ run:triage:triage:
|
|
952
959
|
- schedules
|
953
960
|
```
|
954
961
|
|
955
|
-
> Note
|
962
|
+
> **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
|
956
963
|
|
957
964
|
#### Can I use gitlab-triage for my self-hosted GitLab instance?
|
958
965
|
|
data/gitlab-triage.gemspec
CHANGED
@@ -6,7 +6,7 @@ Gem::Specification.new do |spec|
|
|
6
6
|
spec.name = 'gitlab-triage'
|
7
7
|
spec.version = Gitlab::Triage::VERSION
|
8
8
|
spec.authors = ['GitLab']
|
9
|
-
spec.email = ['
|
9
|
+
spec.email = ['gitlab_rubygems@gitlab.com']
|
10
10
|
|
11
11
|
spec.summary = 'GitLab triage automation project.'
|
12
12
|
spec.homepage = 'https://gitlab.com/gitlab-org/gitlab-triage'
|
@@ -10,7 +10,12 @@ module Gitlab
|
|
10
10
|
private
|
11
11
|
|
12
12
|
def perform
|
13
|
-
|
13
|
+
if group_summary_without_destination?
|
14
|
+
puts Gitlab::Triage::UI.warn("No issue will be created: No summary destination specified when source is 'groups'.")
|
15
|
+
return
|
16
|
+
end
|
17
|
+
|
18
|
+
puts "The following issue would be created in project `#{destination}` for the rule **#{policy.name}**:\n\n"
|
14
19
|
puts ">>>"
|
15
20
|
puts "* Title: #{issue.title}"
|
16
21
|
puts "* Description: #{issue.description}"
|
@@ -25,6 +30,11 @@ module Gitlab
|
|
25
30
|
private
|
26
31
|
|
27
32
|
def perform
|
33
|
+
if group_summary_without_destination?
|
34
|
+
puts Gitlab::Triage::UI.warn("Issue was not created: No summary destination specified when source is 'groups'.")
|
35
|
+
return
|
36
|
+
end
|
37
|
+
|
28
38
|
network.post_api(post_issue_url, post_issue_body)
|
29
39
|
end
|
30
40
|
|
@@ -32,12 +42,20 @@ module Gitlab
|
|
32
42
|
@issue ||= policy.build_issue
|
33
43
|
end
|
34
44
|
|
45
|
+
def destination
|
46
|
+
issue.destination || network.options.source_id
|
47
|
+
end
|
48
|
+
|
49
|
+
def group_summary_without_destination?
|
50
|
+
network.options.source == :groups && !issue.destination
|
51
|
+
end
|
52
|
+
|
35
53
|
def post_issue_url
|
36
54
|
# POST /projects/:id/issues
|
37
55
|
# https://docs.gitlab.com/ee/api/issues.html#new-issue
|
38
56
|
post_url = UrlBuilders::UrlBuilder.new(
|
39
57
|
network_options: network.options,
|
40
|
-
source_id:
|
58
|
+
source_id: destination,
|
41
59
|
resource_type: 'issues'
|
42
60
|
).build
|
43
61
|
|
data/lib/gitlab/triage/engine.rb
CHANGED
@@ -2,7 +2,8 @@ require 'active_support/all'
|
|
2
2
|
require 'active_support/inflector'
|
3
3
|
|
4
4
|
require_relative 'expand_condition'
|
5
|
-
require_relative 'filters/
|
5
|
+
require_relative 'filters/issuable_date_conditions_filter'
|
6
|
+
require_relative 'filters/merge_request_date_conditions_filter'
|
6
7
|
require_relative 'filters/votes_conditions_filter'
|
7
8
|
require_relative 'filters/forbidden_labels_conditions_filter'
|
8
9
|
require_relative 'filters/no_additional_labels_conditions_filter'
|
@@ -185,7 +186,16 @@ module Gitlab
|
|
185
186
|
resources.select do |resource|
|
186
187
|
results = []
|
187
188
|
|
188
|
-
|
189
|
+
if conditions[:date]
|
190
|
+
results << case resource[:type]
|
191
|
+
when 'issues'
|
192
|
+
Filters::IssuableDateConditionsFilter.new(resource, conditions[:date]).calculate
|
193
|
+
when 'merge_requests'
|
194
|
+
Filters::MergeRequestDateConditionsFilter.new(resource, conditions[:date]).calculate
|
195
|
+
else
|
196
|
+
raise "Unknown resource type: #{resource[:type]}"
|
197
|
+
end
|
198
|
+
end
|
189
199
|
results << Filters::VotesConditionsFilter.new(resource, conditions[:upvotes]).calculate if conditions[:upvotes]
|
190
200
|
results << Filters::ForbiddenLabelsConditionsFilter.new(resource, conditions[:forbidden_labels]).calculate if conditions[:forbidden_labels]
|
191
201
|
results << Filters::NoAdditionalLabelsConditionsFilter.new(resource, conditions.fetch(:labels) { [] }).calculate if conditions[:no_additional_labels]
|
@@ -14,6 +14,7 @@ module Gitlab
|
|
14
14
|
@item_template = action[:item]
|
15
15
|
@title_template = action[:title]
|
16
16
|
@summary_template = action[:summary]
|
17
|
+
@summary_destination = action[:destination]
|
17
18
|
@redact_confidentials =
|
18
19
|
action[:redact_confidential_resources] != false
|
19
20
|
@resources = resources
|
@@ -29,6 +30,10 @@ module Gitlab
|
|
29
30
|
@description ||= build_text(description_resource, @summary_template)
|
30
31
|
end
|
31
32
|
|
33
|
+
def destination
|
34
|
+
@summary_destination
|
35
|
+
end
|
36
|
+
|
32
37
|
def valid?
|
33
38
|
title =~ /\S+/ && any_resources?
|
34
39
|
end
|
data/lib/gitlab/triage/filters/{date_conditions_filter.rb → issuable_date_conditions_filter.rb}
RENAMED
@@ -3,17 +3,21 @@ require_relative 'base_conditions_filter'
|
|
3
3
|
module Gitlab
|
4
4
|
module Triage
|
5
5
|
module Filters
|
6
|
-
class
|
6
|
+
class IssuableDateConditionsFilter < BaseConditionsFilter
|
7
7
|
ATTRIBUTES = %w[updated_at created_at].freeze
|
8
8
|
CONDITIONS = %w[older_than newer_than].freeze
|
9
9
|
INTERVAL_TYPES = %w[days weeks months years].freeze
|
10
10
|
|
11
|
+
def self.allowed_attributes
|
12
|
+
self::ATTRIBUTES
|
13
|
+
end
|
14
|
+
|
11
15
|
def self.filter_parameters
|
12
16
|
[
|
13
17
|
{
|
14
18
|
name: :attribute,
|
15
19
|
type: String,
|
16
|
-
values:
|
20
|
+
values: allowed_attributes
|
17
21
|
},
|
18
22
|
{
|
19
23
|
name: :condition,
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require_relative 'issuable_date_conditions_filter'
|
2
|
+
|
3
|
+
module Gitlab
|
4
|
+
module Triage
|
5
|
+
module Filters
|
6
|
+
class MergeRequestDateConditionsFilter < IssuableDateConditionsFilter
|
7
|
+
ATTRIBUTES = %w[updated_at created_at merged_at].freeze
|
8
|
+
|
9
|
+
# Guard against merge requests with no merged_at values
|
10
|
+
def resource_value
|
11
|
+
super if @resource[@attribute]
|
12
|
+
end
|
13
|
+
|
14
|
+
# Guard against merge requests with no merged_at values
|
15
|
+
def calculate
|
16
|
+
return false unless resource_value
|
17
|
+
|
18
|
+
super
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -58,6 +58,22 @@ module Gitlab
|
|
58
58
|
state == 'active'
|
59
59
|
end
|
60
60
|
|
61
|
+
def closed?
|
62
|
+
state == 'closed'
|
63
|
+
end
|
64
|
+
|
65
|
+
def started?(now = Time.now)
|
66
|
+
start_date && start_date <= now
|
67
|
+
end
|
68
|
+
|
69
|
+
def expired?(now = Time.now)
|
70
|
+
due_date && due_date <= now
|
71
|
+
end
|
72
|
+
|
73
|
+
def in_progress?(now = Time.now)
|
74
|
+
started?(now) && !expired?(now)
|
75
|
+
end
|
76
|
+
|
61
77
|
private
|
62
78
|
|
63
79
|
def current_index
|
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.
|
4
|
+
version: 1.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- GitLab
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-01-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -110,7 +110,7 @@ dependencies:
|
|
110
110
|
version: '3.4'
|
111
111
|
description:
|
112
112
|
email:
|
113
|
-
-
|
113
|
+
- gitlab_rubygems@gitlab.com
|
114
114
|
executables:
|
115
115
|
- gitlab-triage
|
116
116
|
extensions: []
|
@@ -153,9 +153,10 @@ files:
|
|
153
153
|
- lib/gitlab/triage/filters/assignee_member_conditions_filter.rb
|
154
154
|
- lib/gitlab/triage/filters/author_member_conditions_filter.rb
|
155
155
|
- lib/gitlab/triage/filters/base_conditions_filter.rb
|
156
|
-
- lib/gitlab/triage/filters/date_conditions_filter.rb
|
157
156
|
- lib/gitlab/triage/filters/forbidden_labels_conditions_filter.rb
|
157
|
+
- lib/gitlab/triage/filters/issuable_date_conditions_filter.rb
|
158
158
|
- lib/gitlab/triage/filters/member_conditions_filter.rb
|
159
|
+
- lib/gitlab/triage/filters/merge_request_date_conditions_filter.rb
|
159
160
|
- lib/gitlab/triage/filters/name_conditions_filter.rb
|
160
161
|
- lib/gitlab/triage/filters/no_additional_labels_conditions_filter.rb
|
161
162
|
- lib/gitlab/triage/filters/ruby_conditions_filter.rb
|