payping-gitlab-triage 0.1.1
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 +7 -0
- data/.codeclimate.yml +19 -0
- data/.gitignore +15 -0
- data/.gitlab/CODEOWNERS +2 -0
- data/.gitlab/changelog_config.yml +13 -0
- data/.gitlab/issue_templates/Default.md +13 -0
- data/.gitlab/merge_request_templates/Default.md +11 -0
- data/.gitlab/merge_request_templates/Release.md +13 -0
- data/.gitlab-ci.yml +146 -0
- data/.rubocop.yml +21 -0
- data/.rubocop_todo.yml +145 -0
- data/.ruby-version +1 -0
- data/.tool-versions +1 -0
- data/.yardopts +4 -0
- data/CONTRIBUTING.md +31 -0
- data/Dangerfile +5 -0
- data/Gemfile +15 -0
- data/Guardfile +70 -0
- data/LICENSE.md +25 -0
- data/README.md +1480 -0
- data/Rakefile +6 -0
- data/bin/gitlab-triage +19 -0
- data/gitlab-triage.gemspec +41 -0
- data/lib/gitlab/triage/action/base.rb +14 -0
- data/lib/gitlab/triage/action/comment.rb +104 -0
- data/lib/gitlab/triage/action/comment_on_summary.rb +83 -0
- data/lib/gitlab/triage/action/delete.rb +56 -0
- data/lib/gitlab/triage/action/issue.rb +64 -0
- data/lib/gitlab/triage/action/summarize.rb +82 -0
- data/lib/gitlab/triage/action.rb +36 -0
- data/lib/gitlab/triage/api_query_builders/base_query_param_builder.rb +27 -0
- data/lib/gitlab/triage/api_query_builders/date_query_param_builder.rb +42 -0
- data/lib/gitlab/triage/api_query_builders/multi_query_param_builder.rb +28 -0
- data/lib/gitlab/triage/api_query_builders/single_query_param_builder.rb +13 -0
- data/lib/gitlab/triage/command_builders/base_command_builder.rb +40 -0
- data/lib/gitlab/triage/command_builders/cc_command_builder.rb +19 -0
- data/lib/gitlab/triage/command_builders/comment_command_builder.rb +19 -0
- data/lib/gitlab/triage/command_builders/label_command_builder.rb +40 -0
- data/lib/gitlab/triage/command_builders/move_command_builder.rb +19 -0
- data/lib/gitlab/triage/command_builders/remove_label_command_builder.rb +15 -0
- data/lib/gitlab/triage/command_builders/status_command_builder.rb +23 -0
- data/lib/gitlab/triage/command_builders/text_content_builder.rb +138 -0
- data/lib/gitlab/triage/engine.rb +635 -0
- data/lib/gitlab/triage/entity_builders/issue_builder.rb +54 -0
- data/lib/gitlab/triage/entity_builders/summary_builder.rb +82 -0
- data/lib/gitlab/triage/errors/network.rb +11 -0
- data/lib/gitlab/triage/errors.rb +1 -0
- data/lib/gitlab/triage/expand_condition/expansion.rb +203 -0
- data/lib/gitlab/triage/expand_condition/list.rb +25 -0
- data/lib/gitlab/triage/expand_condition/sequence.rb +25 -0
- data/lib/gitlab/triage/expand_condition.rb +23 -0
- data/lib/gitlab/triage/filters/assignee_member_conditions_filter.rb +13 -0
- data/lib/gitlab/triage/filters/author_member_conditions_filter.rb +13 -0
- data/lib/gitlab/triage/filters/base_conditions_filter.rb +58 -0
- data/lib/gitlab/triage/filters/branch_date_filter.rb +73 -0
- data/lib/gitlab/triage/filters/branch_protected_filter.rb +26 -0
- data/lib/gitlab/triage/filters/discussions_conditions_filter.rb +58 -0
- data/lib/gitlab/triage/filters/issue_date_conditions_filter.rb +78 -0
- data/lib/gitlab/triage/filters/member_conditions_filter.rb +84 -0
- data/lib/gitlab/triage/filters/merge_request_date_conditions_filter.rb +13 -0
- data/lib/gitlab/triage/filters/name_conditions_filter.rb +26 -0
- data/lib/gitlab/triage/filters/no_additional_labels_conditions_filter.rb +30 -0
- data/lib/gitlab/triage/filters/ruby_conditions_filter.rb +33 -0
- data/lib/gitlab/triage/filters/votes_conditions_filter.rb +54 -0
- data/lib/gitlab/triage/graphql_network.rb +81 -0
- data/lib/gitlab/triage/graphql_queries/query_builder.rb +158 -0
- data/lib/gitlab/triage/graphql_queries/query_param_builders/base_param_builder.rb +30 -0
- data/lib/gitlab/triage/graphql_queries/query_param_builders/date_param_builder.rb +35 -0
- data/lib/gitlab/triage/graphql_queries/query_param_builders/labels_param_builder.rb +18 -0
- data/lib/gitlab/triage/limiters/base_limiter.rb +35 -0
- data/lib/gitlab/triage/limiters/date_field_limiter.rb +45 -0
- data/lib/gitlab/triage/network.rb +39 -0
- data/lib/gitlab/triage/network_adapters/base_adapter.rb +17 -0
- data/lib/gitlab/triage/network_adapters/graphql_adapter.rb +92 -0
- data/lib/gitlab/triage/network_adapters/httparty_adapter.rb +116 -0
- data/lib/gitlab/triage/network_adapters/test_adapter.rb +39 -0
- data/lib/gitlab/triage/option_parser.rb +105 -0
- data/lib/gitlab/triage/options.rb +30 -0
- data/lib/gitlab/triage/param_builders/date_param_builder.rb +64 -0
- data/lib/gitlab/triage/policies/base_policy.rb +80 -0
- data/lib/gitlab/triage/policies/rule_policy.rb +36 -0
- data/lib/gitlab/triage/policies/summary_policy.rb +29 -0
- data/lib/gitlab/triage/policies_resources/rule_resources.rb +11 -0
- data/lib/gitlab/triage/policies_resources/summary_resources.rb +11 -0
- data/lib/gitlab/triage/resource/base.rb +102 -0
- data/lib/gitlab/triage/resource/branch.rb +13 -0
- data/lib/gitlab/triage/resource/context.rb +47 -0
- data/lib/gitlab/triage/resource/epic.rb +20 -0
- data/lib/gitlab/triage/resource/instance_version.rb +35 -0
- data/lib/gitlab/triage/resource/issue.rb +52 -0
- data/lib/gitlab/triage/resource/label.rb +56 -0
- data/lib/gitlab/triage/resource/label_event.rb +48 -0
- data/lib/gitlab/triage/resource/linked_issue.rb +15 -0
- data/lib/gitlab/triage/resource/merge_request.rb +23 -0
- data/lib/gitlab/triage/resource/milestone.rb +98 -0
- data/lib/gitlab/triage/resource/shared/issuable.rb +119 -0
- data/lib/gitlab/triage/rest_api_network.rb +125 -0
- data/lib/gitlab/triage/retryable.rb +33 -0
- data/lib/gitlab/triage/ui.rb +23 -0
- data/lib/gitlab/triage/url_builders/url_builder.rb +54 -0
- data/lib/gitlab/triage/utils.rb +13 -0
- data/lib/gitlab/triage/validators/limiter_validator.rb +21 -0
- data/lib/gitlab/triage/validators/params_validator.rb +43 -0
- data/lib/gitlab/triage/version.rb +7 -0
- data/lib/gitlab/triage.rb +6 -0
- data/support/.gitlab-ci.example.yml +22 -0
- data/support/.triage-policies.example.yml +51 -0
- metadata +280 -0
data/README.md
ADDED
|
@@ -0,0 +1,1480 @@
|
|
|
1
|
+
[](https://gitlab.com/gitlab-org/ruby/gems/gitlab-triage/-/commits/master)
|
|
2
|
+
|
|
3
|
+
# GitLab Triage Project
|
|
4
|
+
|
|
5
|
+
This project allows to automate triaging of issues and merge requests for GitLab projects or groups.
|
|
6
|
+
|
|
7
|
+
## gitlab-triage gem
|
|
8
|
+
|
|
9
|
+
### Abstract
|
|
10
|
+
|
|
11
|
+
The `gitlab-triage` gem aims to enable project managers and maintainers to
|
|
12
|
+
automatically triage Issues and Merge Requests in GitLab projects or groups
|
|
13
|
+
based on defined policies.
|
|
14
|
+
|
|
15
|
+
See [Running with the installed gem](#running-with-the-installed-gem) for how to specify a project or a
|
|
16
|
+
group.
|
|
17
|
+
|
|
18
|
+
### What is a triage policy?
|
|
19
|
+
|
|
20
|
+
Triage policies are defined on a resource level basis, resources being:
|
|
21
|
+
- Epics
|
|
22
|
+
- Issues
|
|
23
|
+
- Merge Requests
|
|
24
|
+
- Branches
|
|
25
|
+
|
|
26
|
+
Each policy can declare a number of conditions that must all be satisfied before
|
|
27
|
+
a number of actions are carried out.
|
|
28
|
+
|
|
29
|
+
Summary policies are special policies that join multiple policies together to
|
|
30
|
+
create a summary issue with all the sub-policies' summaries, see
|
|
31
|
+
[Summary policies](#summary-policies).
|
|
32
|
+
|
|
33
|
+
### Defining a policy
|
|
34
|
+
|
|
35
|
+
Policies are defined in a policy file (by default `./.triage-policies.yml`).
|
|
36
|
+
The format of the file is [YAML](https://en.wikipedia.org/wiki/YAML).
|
|
37
|
+
|
|
38
|
+
> **Note:** You can use the [`--init`](#usage) option to add an example
|
|
39
|
+
[`.triage-policies.yml` file](support/.triage-policies.example.yml) to your
|
|
40
|
+
project.
|
|
41
|
+
|
|
42
|
+
Select which resource to add the policy to:
|
|
43
|
+
- `epics`
|
|
44
|
+
- `issues`
|
|
45
|
+
- `merge_requests`
|
|
46
|
+
- `branches`
|
|
47
|
+
|
|
48
|
+
And create an array of `rules` to define your policies:
|
|
49
|
+
|
|
50
|
+
For example:
|
|
51
|
+
|
|
52
|
+
```yml
|
|
53
|
+
resource_rules:
|
|
54
|
+
epics:
|
|
55
|
+
rules:
|
|
56
|
+
- name: My epic policy
|
|
57
|
+
conditions:
|
|
58
|
+
date:
|
|
59
|
+
attribute: updated_at
|
|
60
|
+
condition: older_than
|
|
61
|
+
interval_type: days
|
|
62
|
+
interval: 5
|
|
63
|
+
state: opened
|
|
64
|
+
labels:
|
|
65
|
+
- None
|
|
66
|
+
actions:
|
|
67
|
+
labels:
|
|
68
|
+
- needs attention
|
|
69
|
+
mention:
|
|
70
|
+
- markglenfletcher
|
|
71
|
+
comment: |
|
|
72
|
+
{{author}} This epic 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')}
|
|
73
|
+
issues:
|
|
74
|
+
rules:
|
|
75
|
+
- name: My issue policy
|
|
76
|
+
conditions:
|
|
77
|
+
date:
|
|
78
|
+
attribute: updated_at
|
|
79
|
+
condition: older_than
|
|
80
|
+
interval_type: days
|
|
81
|
+
interval: 5
|
|
82
|
+
state: opened
|
|
83
|
+
labels:
|
|
84
|
+
- None
|
|
85
|
+
limits:
|
|
86
|
+
most_recent: 50
|
|
87
|
+
actions:
|
|
88
|
+
labels:
|
|
89
|
+
- needs attention
|
|
90
|
+
mention:
|
|
91
|
+
- markglenfletcher
|
|
92
|
+
move: gitlab-org/backlog
|
|
93
|
+
comment: |
|
|
94
|
+
{{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')}
|
|
95
|
+
summarize:
|
|
96
|
+
destination: gitlab-org/ruby/gems/gitlab-triage
|
|
97
|
+
title: |
|
|
98
|
+
#{resource[:type].capitalize} require labels
|
|
99
|
+
item: |
|
|
100
|
+
- [ ] [{{title}}]({{web_url}}) {{labels}}
|
|
101
|
+
redact_confidential_resources: false
|
|
102
|
+
summary: |
|
|
103
|
+
The following issues require labels:
|
|
104
|
+
|
|
105
|
+
{{items}}
|
|
106
|
+
|
|
107
|
+
Please take care of them before the end of #{7.days.from_now.strftime('%Y-%m-%d')}
|
|
108
|
+
|
|
109
|
+
/label ~"needs attention"
|
|
110
|
+
merge_requests:
|
|
111
|
+
rules:
|
|
112
|
+
- name: My merge request policy
|
|
113
|
+
conditions:
|
|
114
|
+
state: opened
|
|
115
|
+
labels:
|
|
116
|
+
- None
|
|
117
|
+
limits:
|
|
118
|
+
most_recent: 50
|
|
119
|
+
actions:
|
|
120
|
+
labels:
|
|
121
|
+
- needs attention
|
|
122
|
+
comment_type: thread
|
|
123
|
+
comment: |
|
|
124
|
+
{{author}} This issue is unlabelled. Please add one or more labels.
|
|
125
|
+
branches:
|
|
126
|
+
rules:
|
|
127
|
+
- name: My branch policy
|
|
128
|
+
conditions:
|
|
129
|
+
date:
|
|
130
|
+
attribute: committed_date
|
|
131
|
+
condition: older_than
|
|
132
|
+
interval_type: 6
|
|
133
|
+
interval: months
|
|
134
|
+
name: ^feature
|
|
135
|
+
actions:
|
|
136
|
+
delete: true
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Real world example
|
|
140
|
+
|
|
141
|
+
We're enforcing multiple polices with pipeline schedules at [triage-ops](
|
|
142
|
+
https://gitlab.com/gitlab-org/quality/triage-ops), where we're also
|
|
143
|
+
extensively utilizing the [plugins system](#can-i-customize).
|
|
144
|
+
|
|
145
|
+
### Fields
|
|
146
|
+
|
|
147
|
+
A policy consists of the following fields:
|
|
148
|
+
- [Name field](#name-field)
|
|
149
|
+
- [Conditions field](#conditions-field)
|
|
150
|
+
- [Limits field](#limits-field)
|
|
151
|
+
- [Actions field](#actions-field)
|
|
152
|
+
|
|
153
|
+
#### Name field
|
|
154
|
+
|
|
155
|
+
The name field is used to describe the purpose of the individual policy.
|
|
156
|
+
|
|
157
|
+
Example:
|
|
158
|
+
|
|
159
|
+
```yml
|
|
160
|
+
name: Policy name
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
#### Conditions field
|
|
164
|
+
|
|
165
|
+
Used to declare a condition that must be satisfied by a resource before actions will be taken.
|
|
166
|
+
|
|
167
|
+
Available condition types:
|
|
168
|
+
- [`date` condition](#date-condition)
|
|
169
|
+
- [`milestone` condition](#milestone-condition)
|
|
170
|
+
- [`iteration` condition](#iteration-condition)
|
|
171
|
+
- [`state` condition](#state-condition)
|
|
172
|
+
- [`votes` condition](#votes-condition)
|
|
173
|
+
- [`labels` condition](#labels-condition)
|
|
174
|
+
- [`forbidden_labels` condition](#forbidden-labels-condition)
|
|
175
|
+
- [`no_additional_labels` condition](#no-additional-labels-condition)
|
|
176
|
+
- [`author_member` condition](#author-member-condition)
|
|
177
|
+
- [`assignee_member` condition](#assignee-member-condition)
|
|
178
|
+
- [`draft` condition](#draft-condition)
|
|
179
|
+
- [`source_branch` condition](#source-branch-condition)
|
|
180
|
+
- [`target_branch` condition](#target-branch-condition)
|
|
181
|
+
- [`health_status` condition](#health-status-condition)
|
|
182
|
+
- [`weight` condition](#weight-condition)
|
|
183
|
+
- [`discussions` condition](#discussions-condition)
|
|
184
|
+
- [`protected` condition](#protected-condition)
|
|
185
|
+
- [`ruby` condition](#ruby-condition)
|
|
186
|
+
- [`reviewer_id` condition](#reviewer-id-condition)
|
|
187
|
+
|
|
188
|
+
##### Date condition
|
|
189
|
+
|
|
190
|
+
Accepts a hash of fields.
|
|
191
|
+
|
|
192
|
+
| Field | Type | Values | Required |
|
|
193
|
+
| --------- | ---- |----------------------------------------------------------------------------| -------- |
|
|
194
|
+
| `attribute` | string | `created_at`, `updated_at`, `merged_at`, `authored_date`, `committed_date` | yes |
|
|
195
|
+
| `condition` | string | `older_than`, `newer_than` | yes |
|
|
196
|
+
| `interval_type` | string | `minutes`, `hours`, `days`, `weeks`, `months`, `years` | yes |
|
|
197
|
+
| `interval` | integer | integer | yes |
|
|
198
|
+
> **Note:**
|
|
199
|
+
> - `merged_at` only works on merge requests.
|
|
200
|
+
> - `closed_at` is not supported in the GitLab API, but can be used in a [`ruby` condition](#ruby-condition).
|
|
201
|
+
> - `committed_date` and `authored_date` only works for branches.
|
|
202
|
+
|
|
203
|
+
Example:
|
|
204
|
+
|
|
205
|
+
```yml
|
|
206
|
+
conditions:
|
|
207
|
+
date:
|
|
208
|
+
attribute: updated_at
|
|
209
|
+
condition: older_than
|
|
210
|
+
interval_type: months
|
|
211
|
+
interval: 12
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
> **Note:** If the GitLab server is giving 500 error with this option, it
|
|
215
|
+
> can mean that it's taking too much time to query this, and it's timing out.
|
|
216
|
+
> A workaround for this is that we can filter in Ruby. If you need this
|
|
217
|
+
> workaround, specify this with `filter_in_ruby: true`
|
|
218
|
+
>
|
|
219
|
+
> ```yaml
|
|
220
|
+
> conditions:
|
|
221
|
+
> date:
|
|
222
|
+
> attribute: updated_at
|
|
223
|
+
> condition: older_than
|
|
224
|
+
> interval_type: months
|
|
225
|
+
> interval: 12
|
|
226
|
+
> filter_in_ruby: true
|
|
227
|
+
> ```
|
|
228
|
+
|
|
229
|
+
##### Milestone condition
|
|
230
|
+
|
|
231
|
+
Accepts the name of a milestone to filter upon. Also accepts the following timebox values:
|
|
232
|
+
|
|
233
|
+
- `none`
|
|
234
|
+
- `any`
|
|
235
|
+
- `upcoming`
|
|
236
|
+
- `started`
|
|
237
|
+
|
|
238
|
+
See the [`milestone_id` API field documentation](https://docs.gitlab.com/ee/api/issues.html) for their meaning.
|
|
239
|
+
|
|
240
|
+
Example:
|
|
241
|
+
|
|
242
|
+
```yml
|
|
243
|
+
conditions:
|
|
244
|
+
milestone: v1
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
##### Iteration condition
|
|
248
|
+
|
|
249
|
+
Accepts the name of an iteration to filter upon. Also accepts the following
|
|
250
|
+
timebox values:
|
|
251
|
+
|
|
252
|
+
- `none`
|
|
253
|
+
- `any`
|
|
254
|
+
|
|
255
|
+
See the [`iteration_id` API field documentation](https://docs.gitlab.com/ee/api/issues.html) for their meaning.
|
|
256
|
+
|
|
257
|
+
Example:
|
|
258
|
+
|
|
259
|
+
```yml
|
|
260
|
+
conditions:
|
|
261
|
+
iteration: none
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
> **Note:** This query is not supported using GraphQL yet.
|
|
265
|
+
|
|
266
|
+
##### State condition
|
|
267
|
+
|
|
268
|
+
Accepts a string.
|
|
269
|
+
|
|
270
|
+
| State | Type | Value |
|
|
271
|
+
| --------- | ---- | ------ |
|
|
272
|
+
| Closed issues/MRs | string | `closed` |
|
|
273
|
+
| Open issues/MRs | string | `opened` |
|
|
274
|
+
| Locked issues | string | `locked` |
|
|
275
|
+
| Merged merge requests | string | `merged` |
|
|
276
|
+
|
|
277
|
+
Example:
|
|
278
|
+
|
|
279
|
+
```yml
|
|
280
|
+
conditions:
|
|
281
|
+
state: opened
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
##### Votes condition
|
|
285
|
+
|
|
286
|
+
Accepts a hash of fields.
|
|
287
|
+
|
|
288
|
+
| Field | Type | Values | Required |
|
|
289
|
+
| --------- | ---- | ---- | -------- |
|
|
290
|
+
| `attribute` | string | `upvotes`, `downvotes` | yes |
|
|
291
|
+
| `condition` | string | `less_than`, `greater_than` | yes |
|
|
292
|
+
| `threshold` | integer | integer | yes |
|
|
293
|
+
|
|
294
|
+
Example:
|
|
295
|
+
|
|
296
|
+
```yml
|
|
297
|
+
conditions:
|
|
298
|
+
votes:
|
|
299
|
+
attribute: upvotes
|
|
300
|
+
condition: less_than
|
|
301
|
+
threshold: 10
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
##### Labels condition
|
|
305
|
+
|
|
306
|
+
Accepts an array of strings. Each element in the array represents the name of a label to filter on.
|
|
307
|
+
|
|
308
|
+
> **Note:** **All** specified labels must be present on the resource for the condition to be satisfied
|
|
309
|
+
|
|
310
|
+
Example:
|
|
311
|
+
|
|
312
|
+
```yml
|
|
313
|
+
conditions:
|
|
314
|
+
labels:
|
|
315
|
+
- feature proposal
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
###### Predefined special label names
|
|
319
|
+
|
|
320
|
+
Basing on the [issues API](https://docs.gitlab.com/ee/api/issues.html), there
|
|
321
|
+
are two special predefined label names we can use here:
|
|
322
|
+
|
|
323
|
+
* `None`: This indicates that no labels were present
|
|
324
|
+
* `Any`: This indicates that any labels were presented
|
|
325
|
+
|
|
326
|
+
Example:
|
|
327
|
+
|
|
328
|
+
```yml
|
|
329
|
+
conditions:
|
|
330
|
+
labels:
|
|
331
|
+
- None
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
###### Labels brace expansion
|
|
335
|
+
|
|
336
|
+
We could expand the labels by using brace expansion, which is a pattern
|
|
337
|
+
surrounded by using braces: `{}`. For now, we support 2 kinds of brace
|
|
338
|
+
expansion:
|
|
339
|
+
|
|
340
|
+
1. List: `{ apple, orange }`
|
|
341
|
+
2. Sequence: `{1..4}`
|
|
342
|
+
|
|
343
|
+
> **Note:**
|
|
344
|
+
> - Spaces around the items are ignored.
|
|
345
|
+
> - Do not rely on the expansion ordering. This is subject to change.
|
|
346
|
+
|
|
347
|
+
###### List
|
|
348
|
+
|
|
349
|
+
The name of a label can contain a list of items, written like
|
|
350
|
+
`{ apple, orange }`. For each item, the rule will be duplicated with the new
|
|
351
|
+
label name.
|
|
352
|
+
|
|
353
|
+
Example:
|
|
354
|
+
|
|
355
|
+
```yml
|
|
356
|
+
resource_rules:
|
|
357
|
+
issues:
|
|
358
|
+
rules:
|
|
359
|
+
- name: Add missing ~Quality label
|
|
360
|
+
conditions:
|
|
361
|
+
labels:
|
|
362
|
+
- Quality:test-{ gap, infra }
|
|
363
|
+
actions:
|
|
364
|
+
labels:
|
|
365
|
+
- Quality
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
Which will be expanded into:
|
|
369
|
+
|
|
370
|
+
```yml
|
|
371
|
+
resource_rules:
|
|
372
|
+
issues:
|
|
373
|
+
rules:
|
|
374
|
+
- name: Add missing ~Quality label
|
|
375
|
+
conditions:
|
|
376
|
+
labels:
|
|
377
|
+
- Quality:test-gap
|
|
378
|
+
actions:
|
|
379
|
+
labels:
|
|
380
|
+
- Quality
|
|
381
|
+
|
|
382
|
+
- name: Add missing ~Quality label
|
|
383
|
+
conditions:
|
|
384
|
+
labels:
|
|
385
|
+
- Quality:test-infra
|
|
386
|
+
actions:
|
|
387
|
+
labels:
|
|
388
|
+
- Quality
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
> **Note:**
|
|
392
|
+
> 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.
|
|
393
|
+
> For example, we can quote the expression like `'{ apple, orange }'`, which will create 2 rules, for the two specified labels.
|
|
394
|
+
|
|
395
|
+
###### Sequence
|
|
396
|
+
|
|
397
|
+
The name of a label can contain one or more sequence conditions, written
|
|
398
|
+
like `{0..9}`, which means `0`, `1`, `2`, and so on up to `9`. For each
|
|
399
|
+
number, the rule will be duplicated with the new label name.
|
|
400
|
+
|
|
401
|
+
Example:
|
|
402
|
+
|
|
403
|
+
```yml
|
|
404
|
+
resource_rules:
|
|
405
|
+
issues:
|
|
406
|
+
rules:
|
|
407
|
+
- name: Add missing ~"missed\-deliverable" label
|
|
408
|
+
conditions:
|
|
409
|
+
labels:
|
|
410
|
+
- missed:{10..11}.{0..1}
|
|
411
|
+
- deliverable
|
|
412
|
+
actions:
|
|
413
|
+
labels:
|
|
414
|
+
- missed deliverable
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
Which will be expanded into:
|
|
418
|
+
|
|
419
|
+
```yml
|
|
420
|
+
resource_rules:
|
|
421
|
+
issues:
|
|
422
|
+
rules:
|
|
423
|
+
- name: Add missing ~"missed\-deliverable" label
|
|
424
|
+
conditions:
|
|
425
|
+
labels:
|
|
426
|
+
- missed:10.0
|
|
427
|
+
- deliverable
|
|
428
|
+
actions:
|
|
429
|
+
labels:
|
|
430
|
+
- missed deliverable
|
|
431
|
+
|
|
432
|
+
- name: Add missing ~"missed\-deliverable" label
|
|
433
|
+
conditions:
|
|
434
|
+
labels:
|
|
435
|
+
- missed:10.1
|
|
436
|
+
- deliverable
|
|
437
|
+
actions:
|
|
438
|
+
labels:
|
|
439
|
+
- missed deliverable
|
|
440
|
+
|
|
441
|
+
- name: Add missing ~"missed\-deliverable" label
|
|
442
|
+
conditions:
|
|
443
|
+
labels:
|
|
444
|
+
- missed:11.0
|
|
445
|
+
- deliverable
|
|
446
|
+
actions:
|
|
447
|
+
labels:
|
|
448
|
+
- missed deliverable
|
|
449
|
+
|
|
450
|
+
- name: Add missing ~"missed\-deliverable" label
|
|
451
|
+
conditions:
|
|
452
|
+
labels:
|
|
453
|
+
- missed:11.1
|
|
454
|
+
- deliverable
|
|
455
|
+
actions:
|
|
456
|
+
labels:
|
|
457
|
+
- missed deliverable
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
##### Forbidden labels condition
|
|
461
|
+
|
|
462
|
+
Accepts an array of strings. Each element in the array represents the name of a label to filter on.
|
|
463
|
+
|
|
464
|
+
> **Note:** **All** specified labels must be absent on the resource for the condition to be satisfied
|
|
465
|
+
|
|
466
|
+
Example:
|
|
467
|
+
|
|
468
|
+
```yml
|
|
469
|
+
conditions:
|
|
470
|
+
forbidden_labels:
|
|
471
|
+
- awaiting feedback
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
##### No additional labels condition
|
|
475
|
+
|
|
476
|
+
Accepts a boolean. If `true` the resource cannot have more labels than those specified by the `labels` condition.
|
|
477
|
+
|
|
478
|
+
Example:
|
|
479
|
+
|
|
480
|
+
```yml
|
|
481
|
+
conditions:
|
|
482
|
+
labels:
|
|
483
|
+
- feature proposal
|
|
484
|
+
no_additional_labels: true
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
##### Author Member condition
|
|
488
|
+
|
|
489
|
+
This condition determines whether the author of a resource is a member of the specified group or project.
|
|
490
|
+
|
|
491
|
+
This is useful for determining whether Issues or Merge Requests have been raised by a Community Contributor.
|
|
492
|
+
|
|
493
|
+
Accepts a hash of fields.
|
|
494
|
+
|
|
495
|
+
| Field | Type | Values | Required |
|
|
496
|
+
| --------- | ---- | ---- | -------- |
|
|
497
|
+
| `source` | string | `group`, `project` | yes |
|
|
498
|
+
| `condition` | string | `member_of`, `not_member_of` | yes |
|
|
499
|
+
| `source_id` | integer or string | gitlab-org/gitlab | yes |
|
|
500
|
+
|
|
501
|
+
Example:
|
|
502
|
+
|
|
503
|
+
```yml
|
|
504
|
+
conditions:
|
|
505
|
+
author_member:
|
|
506
|
+
source: group
|
|
507
|
+
condition: not_member_of
|
|
508
|
+
source_id: 9970
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
##### Assignee member condition
|
|
512
|
+
|
|
513
|
+
This condition determines whether the assignee of a resource is a member of the specified group or project.
|
|
514
|
+
|
|
515
|
+
Accepts a hash of fields.
|
|
516
|
+
|
|
517
|
+
| Field | Type | Values | Required |
|
|
518
|
+
| --------- | ---- | ---- | -------- |
|
|
519
|
+
| `source` | string | `group`, `project` | yes |
|
|
520
|
+
| `condition` | string | `member_of`, `not_member_of` | yes |
|
|
521
|
+
| `source_id` | integer or string | gitlab-org/gitlab | yes |
|
|
522
|
+
|
|
523
|
+
Example:
|
|
524
|
+
|
|
525
|
+
```yml
|
|
526
|
+
conditions:
|
|
527
|
+
assignee_member:
|
|
528
|
+
source: group
|
|
529
|
+
condition: not_member_of
|
|
530
|
+
source_id: 9970
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
##### Draft condition
|
|
534
|
+
|
|
535
|
+
**This condition is only applicable for merge requests.**
|
|
536
|
+
|
|
537
|
+
Accepts a boolean. If `true`, only draft MRs are returned. If `false`, only non-draft MRs are returned.
|
|
538
|
+
|
|
539
|
+
Example:
|
|
540
|
+
|
|
541
|
+
```yml
|
|
542
|
+
conditions:
|
|
543
|
+
draft: true
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
##### Source branch condition
|
|
547
|
+
|
|
548
|
+
**This condition is only applicable for merge requests.**
|
|
549
|
+
|
|
550
|
+
Accepts the name of a source branch to filter upon.
|
|
551
|
+
|
|
552
|
+
Example:
|
|
553
|
+
|
|
554
|
+
```yml
|
|
555
|
+
conditions:
|
|
556
|
+
source_branch: 'feature-branch'
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
##### Target branch condition
|
|
560
|
+
|
|
561
|
+
**This condition is only applicable for merge requests.**
|
|
562
|
+
|
|
563
|
+
Accepts the name of a target branch to filter upon.
|
|
564
|
+
|
|
565
|
+
Example:
|
|
566
|
+
|
|
567
|
+
```yml
|
|
568
|
+
conditions:
|
|
569
|
+
target_branch: 'master'
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
##### Health Status condition
|
|
573
|
+
|
|
574
|
+
**This condition is only applicable for issues.**
|
|
575
|
+
|
|
576
|
+
Accepts a string per the [API documentation](https://docs.gitlab.com/ee/api/issues.html#list-issues).
|
|
577
|
+
|
|
578
|
+
| State | Type | Value |
|
|
579
|
+
| --------- | ---- | ------ |
|
|
580
|
+
| Any health status | string | `Any` |
|
|
581
|
+
| No health status | string | `None` |
|
|
582
|
+
| Specific health status | string | One of `on_track`, `needs_attention` or `at_risk` |
|
|
583
|
+
|
|
584
|
+
Example:
|
|
585
|
+
|
|
586
|
+
```yml
|
|
587
|
+
conditions:
|
|
588
|
+
health_status: Any
|
|
589
|
+
|
|
590
|
+
> **Note:** This query is not supported using GraphQL yet.
|
|
591
|
+
|
|
592
|
+
##### Weight condition
|
|
593
|
+
|
|
594
|
+
**This condition is only applicable for issues.**
|
|
595
|
+
|
|
596
|
+
Accepts a string per the [API documentation](https://docs.gitlab.com/ee/api/issues.html#list-issues).
|
|
597
|
+
|
|
598
|
+
| State | Type | Value |
|
|
599
|
+
| --------- | ---- | ------ |
|
|
600
|
+
| Any weight | string | `Any` |
|
|
601
|
+
| No weight | string | `None` |
|
|
602
|
+
| Specific weight | integer | integer |
|
|
603
|
+
|
|
604
|
+
Example:
|
|
605
|
+
|
|
606
|
+
```yml
|
|
607
|
+
conditions:
|
|
608
|
+
weight: Any
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
##### Discussions condition
|
|
612
|
+
|
|
613
|
+
Accepts a hash of fields.
|
|
614
|
+
|
|
615
|
+
| Field | Type | Values | Required |
|
|
616
|
+
| --------- | ---- | ---- | -------- |
|
|
617
|
+
| `attribute` | string | `threads`, `notes` | yes |
|
|
618
|
+
| `condition` | string | `less_than`, `greater_than` | yes |
|
|
619
|
+
| `threshold` | integer | integer | yes |
|
|
620
|
+
|
|
621
|
+
Example:
|
|
622
|
+
|
|
623
|
+
```yml
|
|
624
|
+
conditions:
|
|
625
|
+
discussions:
|
|
626
|
+
attribute: threads
|
|
627
|
+
condition: greater_than
|
|
628
|
+
threshold: 15
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
##### Protected condition
|
|
632
|
+
|
|
633
|
+
** This condition is only applicable for branches**
|
|
634
|
+
|
|
635
|
+
Accept a boolean.
|
|
636
|
+
If not specified, default to `false` to filter out protected branches.
|
|
637
|
+
|
|
638
|
+
##### Ruby condition
|
|
639
|
+
|
|
640
|
+
This condition allows users to write a Ruby expression to be evaluated for
|
|
641
|
+
each resource. If it evaluates to a truthy value, it satisfies the condition.
|
|
642
|
+
If it evaluates to a falsey value, it does not satisfy the condition.
|
|
643
|
+
|
|
644
|
+
Accepts a string as the Ruby expression.
|
|
645
|
+
|
|
646
|
+
Example:
|
|
647
|
+
|
|
648
|
+
```yml
|
|
649
|
+
conditions:
|
|
650
|
+
ruby: Date.today > milestone.succ.start_date
|
|
651
|
+
```
|
|
652
|
+
|
|
653
|
+
In the above example, this describes that we want to act on the resources
|
|
654
|
+
which passed the next active milestone's starting date.
|
|
655
|
+
|
|
656
|
+
Here `milestone` will return a `Gitlab::Triage::Resource::Milestone` object,
|
|
657
|
+
representing the milestone of the questioning resource. `Milestone#succ` would
|
|
658
|
+
return the next active milestone, based on the `start_date` of all milestones
|
|
659
|
+
along with the representing milestone. If the milestone was coming from a
|
|
660
|
+
project, then it's based on all active milestones in that project. If the
|
|
661
|
+
milestone was coming from a group, then it's based on all active milestones
|
|
662
|
+
in the group.
|
|
663
|
+
|
|
664
|
+
If we also want to handle some edge cases, for example, a resource might not
|
|
665
|
+
have a milestone, and a milestone might not be active, and there might not
|
|
666
|
+
have a next milestone. We could instead write something like:
|
|
667
|
+
|
|
668
|
+
```yml
|
|
669
|
+
conditions:
|
|
670
|
+
ruby: milestone&.active? && milestone&.succ && Date.today > milestone.succ.start_date
|
|
671
|
+
```
|
|
672
|
+
|
|
673
|
+
This will make it only act on resources which have active milestones and
|
|
674
|
+
there exists next milestone which has already started.
|
|
675
|
+
|
|
676
|
+
Since `closed_at` is not a queryable attribute in the GitLab API, we can use a Ruby expression to filter resources like:
|
|
677
|
+
|
|
678
|
+
```yml
|
|
679
|
+
conditions:
|
|
680
|
+
ruby: resource[:closed_at] > 7.days.ago.strftime('%Y-%m-%dT00:00:00.000Z')
|
|
681
|
+
```
|
|
682
|
+
|
|
683
|
+
See [Ruby expression API](#ruby-expression-api) for the list of currently
|
|
684
|
+
available API.
|
|
685
|
+
|
|
686
|
+
#### Limits field
|
|
687
|
+
|
|
688
|
+
Limits restrict the number of resources on which an action is carried out. They
|
|
689
|
+
can be useful when combined with conditions that return a large number of
|
|
690
|
+
resources. For example, if the conditions are satisfied by thousands of issues a
|
|
691
|
+
limit can be configured to process only fifty of them to avoid making an
|
|
692
|
+
overwhelming number of changes at once.
|
|
693
|
+
|
|
694
|
+
Accepts a key and value pair where the key is `most_recent` or `oldest`and the
|
|
695
|
+
value is the number of resources to act on. The following table outlines how
|
|
696
|
+
each key affects the sorting and order of resources that it limits.
|
|
697
|
+
|
|
698
|
+
| Name / Key | Sorted by | Order |
|
|
699
|
+
| --------- | ---- | ------ |
|
|
700
|
+
| `most_recent` | `created_at` | descending |
|
|
701
|
+
| `oldest` | `created_at` | ascending |
|
|
702
|
+
|
|
703
|
+
Example:
|
|
704
|
+
|
|
705
|
+
```yml
|
|
706
|
+
limits:
|
|
707
|
+
most_recent: 50
|
|
708
|
+
```
|
|
709
|
+
|
|
710
|
+
##### Reviewer id condition
|
|
711
|
+
|
|
712
|
+
**This condition is only applicable for merge requests.**
|
|
713
|
+
|
|
714
|
+
Accepts the id of a user to filter on. Also accepts `none` or `any`.
|
|
715
|
+
|
|
716
|
+
Example:
|
|
717
|
+
|
|
718
|
+
```yml
|
|
719
|
+
conditions:
|
|
720
|
+
reviewer_id: any
|
|
721
|
+
```
|
|
722
|
+
|
|
723
|
+
#### Actions field
|
|
724
|
+
|
|
725
|
+
Used to declare an action to be carried out on a resource if **all** conditions are satisfied.
|
|
726
|
+
|
|
727
|
+
Available action types:
|
|
728
|
+
- [`labels` action](#labels-action)
|
|
729
|
+
- [`remove_labels` action](#remove-labels-action)
|
|
730
|
+
- [`status` action](#status-action)
|
|
731
|
+
- [`mention` action](#mention-action)
|
|
732
|
+
- [`move` action](#move-action)
|
|
733
|
+
- [`comment` action](#comment-action)
|
|
734
|
+
- [`comment_type` action option](#comment-type-action-option)
|
|
735
|
+
- [`summarize` action](#summarize-action)
|
|
736
|
+
- [`comment_on_summary` action](#comment-on-summary-action)
|
|
737
|
+
- [`issue` action](#create-a-new-issue-from-each-resource)
|
|
738
|
+
- [`delete` action](#delete-action)
|
|
739
|
+
|
|
740
|
+
##### Labels action
|
|
741
|
+
|
|
742
|
+
Adds a number of labels to the resource.
|
|
743
|
+
|
|
744
|
+
Accepts an array of strings. Each element is the name of a label to add.
|
|
745
|
+
|
|
746
|
+
If any of the labels doesn't exist, the automation will stop immediately so
|
|
747
|
+
that if a label is renamed or deleted, you'll have to explicitly update or remove
|
|
748
|
+
it in your policy file.
|
|
749
|
+
|
|
750
|
+
Example:
|
|
751
|
+
|
|
752
|
+
```yml
|
|
753
|
+
actions:
|
|
754
|
+
labels:
|
|
755
|
+
- feature proposal
|
|
756
|
+
- awaiting feedback
|
|
757
|
+
```
|
|
758
|
+
|
|
759
|
+
##### Remove labels action
|
|
760
|
+
|
|
761
|
+
Removes a number of labels from the resource.
|
|
762
|
+
|
|
763
|
+
Accepts an array of strings. Each element is the name of a label to remove.
|
|
764
|
+
|
|
765
|
+
If any of the labels doesn't exist, the automation will stop immediately so
|
|
766
|
+
that if a label is renamed or deleted, you'll have to explicitly update or remove
|
|
767
|
+
it in your policy file.
|
|
768
|
+
|
|
769
|
+
Example:
|
|
770
|
+
|
|
771
|
+
```yml
|
|
772
|
+
actions:
|
|
773
|
+
remove_labels:
|
|
774
|
+
- feature proposal
|
|
775
|
+
- awaiting feedback
|
|
776
|
+
```
|
|
777
|
+
|
|
778
|
+
##### Status action
|
|
779
|
+
|
|
780
|
+
Changes the status of the resource.
|
|
781
|
+
|
|
782
|
+
Accepts a string.
|
|
783
|
+
|
|
784
|
+
| State transition | Type | Value |
|
|
785
|
+
| --------- | ---- | ------ |
|
|
786
|
+
| Close the resource | string | `close` |
|
|
787
|
+
| Reopen the resource | string | `reopen` |
|
|
788
|
+
|
|
789
|
+
Example:
|
|
790
|
+
|
|
791
|
+
```yml
|
|
792
|
+
actions:
|
|
793
|
+
status: close
|
|
794
|
+
```
|
|
795
|
+
|
|
796
|
+
##### Mention action
|
|
797
|
+
|
|
798
|
+
Mentions a number of users.
|
|
799
|
+
|
|
800
|
+
Accepts an array of strings. Each element is the username of a user to mention.
|
|
801
|
+
|
|
802
|
+
Example:
|
|
803
|
+
|
|
804
|
+
```yml
|
|
805
|
+
actions:
|
|
806
|
+
mention:
|
|
807
|
+
- rymai
|
|
808
|
+
- markglenfletcher
|
|
809
|
+
```
|
|
810
|
+
|
|
811
|
+
##### Move action
|
|
812
|
+
|
|
813
|
+
Moves an issue (merge request is not supported yet) to the specified project.
|
|
814
|
+
|
|
815
|
+
Accepts a string containing the target project path.
|
|
816
|
+
|
|
817
|
+
Example:
|
|
818
|
+
|
|
819
|
+
```yml
|
|
820
|
+
actions:
|
|
821
|
+
move: target/project_path
|
|
822
|
+
```
|
|
823
|
+
|
|
824
|
+
##### Comment action
|
|
825
|
+
|
|
826
|
+
Adds a comment to the resource.
|
|
827
|
+
|
|
828
|
+
Accepts a string, and placeholders. Placeholders should be wrapped in double
|
|
829
|
+
curly braces, e.g. `{{author}}`.
|
|
830
|
+
|
|
831
|
+
The following placeholders are supported:
|
|
832
|
+
|
|
833
|
+
- `created_at`: the resource's creation date
|
|
834
|
+
- `updated_at`: the resource's last update date
|
|
835
|
+
- `closed_at`: the resource's closed date (if applicable)
|
|
836
|
+
- `merged_at`: the resource's merged date (if applicable)
|
|
837
|
+
- `state`: the resources's current state: `opened`, `closed`, `merged`
|
|
838
|
+
- `author`: the username of the resource's author as `@user1`
|
|
839
|
+
- `assignee`: the username of the resource's assignee as `@user1`
|
|
840
|
+
- `assignees`: the usernames of the resource's assignees as `@user1, @user2`
|
|
841
|
+
- `closed_by`: the user that closed the resource as `@user1` (if applicable)
|
|
842
|
+
- `merged_by`: the user that merged the resource as `@user1` (if applicable)
|
|
843
|
+
- `milestone`: the resource's current milestone
|
|
844
|
+
- `labels`: the resource's labels as `~label1, ~label2`
|
|
845
|
+
- `upvotes`: the resources's upvotes count
|
|
846
|
+
- `downvotes`: the resources's downvotes count
|
|
847
|
+
- `title`: the resource's title
|
|
848
|
+
- `web_url`: the web URL pointing to the resource
|
|
849
|
+
- `full_reference`: the full reference of the resource as `namespace/project#12`, `namespace/project!42`, `namespace/project&72`
|
|
850
|
+
- `type`: the type of the resources. For now, only `issues`, `merge_requests`, and `epics` are supported.
|
|
851
|
+
|
|
852
|
+
If the resource doesn't respond to the placeholder, or if the field is `nil`,
|
|
853
|
+
the placeholder is not replaced.
|
|
854
|
+
|
|
855
|
+
Example without placeholders:
|
|
856
|
+
|
|
857
|
+
```yml
|
|
858
|
+
actions:
|
|
859
|
+
comment: |
|
|
860
|
+
Closing this issue automatically
|
|
861
|
+
```
|
|
862
|
+
|
|
863
|
+
Example with placeholders:
|
|
864
|
+
|
|
865
|
+
```yml
|
|
866
|
+
actions:
|
|
867
|
+
comment: |
|
|
868
|
+
{{author}} Are you still interested in finishing this merge request?
|
|
869
|
+
```
|
|
870
|
+
|
|
871
|
+
##### Comment type action option
|
|
872
|
+
|
|
873
|
+
Determines the type of comment to be added to the resource.
|
|
874
|
+
|
|
875
|
+
The following comment types are supported:
|
|
876
|
+
|
|
877
|
+
- `comment` (default): creates a regular comment on the resource
|
|
878
|
+
- `thread`: starts a resolvable thread (discussion) on the resource
|
|
879
|
+
|
|
880
|
+
For merge requests, if `comment_type` is set to `thread`, we can also configure that [all threads should be resolved before merging](https://docs.gitlab.com/ee/user/discussions/#only-allow-merge-requests-to-be-merged-if-all-threads-are-resolved), therefore this comment can prevent it from merging.
|
|
881
|
+
|
|
882
|
+
Example:
|
|
883
|
+
|
|
884
|
+
```yml
|
|
885
|
+
actions:
|
|
886
|
+
comment_type: thread
|
|
887
|
+
comment: |
|
|
888
|
+
{{author}} Are you still interested in finishing this merge request?
|
|
889
|
+
```
|
|
890
|
+
|
|
891
|
+
###### Harnessing Quick Actions
|
|
892
|
+
|
|
893
|
+
[GitLab's quick actions feature](https://docs.gitlab.com/ce/user/project/quick_actions.html) is available in Core.
|
|
894
|
+
All of the operations supported by executing a quick action can be carried out via the comment action.
|
|
895
|
+
|
|
896
|
+
If GitLab triage does not support an operation natively, it may be possible via a quick action in a comment.
|
|
897
|
+
|
|
898
|
+
For example:
|
|
899
|
+
- Flagging an issue as [confidential](https://docs.gitlab.com/ce/user/project/issues/confidential_issues.html)
|
|
900
|
+
- [Locking issue discussion](https://docs.gitlab.com/ce/user/discussions/#lock-discussions)
|
|
901
|
+
|
|
902
|
+
```yml
|
|
903
|
+
resource_rules:
|
|
904
|
+
issues:
|
|
905
|
+
rules:
|
|
906
|
+
- name: Mark bugs as confidential
|
|
907
|
+
conditions:
|
|
908
|
+
state: opened
|
|
909
|
+
ruby: !resource[:confidential]
|
|
910
|
+
labels:
|
|
911
|
+
- bug
|
|
912
|
+
actions:
|
|
913
|
+
comment: |
|
|
914
|
+
/confidential
|
|
915
|
+
```
|
|
916
|
+
|
|
917
|
+
###### Ruby expression
|
|
918
|
+
|
|
919
|
+
The comment can also contain Ruby expression, using Ruby's own string
|
|
920
|
+
interpolation syntax: `#{ expression }`. This gives you the most flexibility.
|
|
921
|
+
Suppose you want to mention the next active milestone relative to the one
|
|
922
|
+
associated with the resource, you can write:
|
|
923
|
+
|
|
924
|
+
```yml
|
|
925
|
+
actions:
|
|
926
|
+
comment: |
|
|
927
|
+
Please move this to %"#{milestone.succ.title}".
|
|
928
|
+
```
|
|
929
|
+
|
|
930
|
+
See [Ruby expression API](#ruby-expression-api) for the list of currently
|
|
931
|
+
available API.
|
|
932
|
+
|
|
933
|
+
> **Note:** If you get a syntax error due to stray braces (`{` or `}`), use `\`
|
|
934
|
+
to escape it. For example:
|
|
935
|
+
>
|
|
936
|
+
> ```yml
|
|
937
|
+
> actions:
|
|
938
|
+
> comment: |
|
|
939
|
+
> 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.
|
|
940
|
+
> ```
|
|
941
|
+
|
|
942
|
+
##### Summarize action
|
|
943
|
+
|
|
944
|
+
Generates an issue summarizing what was triaged.
|
|
945
|
+
|
|
946
|
+
Accepts a hash of fields.
|
|
947
|
+
|
|
948
|
+
| Field | Type | Description | Required | Placeholders | Ruby expression | Default |
|
|
949
|
+
| ---- | ---- | ---- | ---- | ---- | ---- | ---- |
|
|
950
|
+
| `title` | string | The title of the generated issue | yes | yes | yes | |
|
|
951
|
+
| `destination` | integer or string | The project ID or path to create the generated issue in | no | no | no | source project |
|
|
952
|
+
| `item` | string | Template representing each triaged resource | no | yes | yes | |
|
|
953
|
+
| `summary` | string | The description of the generated issue | no | Only `{{title}}`, `{{items}}`, `{{type}}` | yes | |
|
|
954
|
+
| `redact_confidential_resources` | boolean | Whether redact fields for confidential resources | no | no | no | true |
|
|
955
|
+
|
|
956
|
+
The following placeholders are supported for `summary`:
|
|
957
|
+
|
|
958
|
+
- `title`: The title of the generated issue
|
|
959
|
+
- `items`: Concatenated markdown separated by a newline for each `item`
|
|
960
|
+
- `type`: The resource type for the summary. For now `issues`, `merge_requests`, or `epics`,
|
|
961
|
+
|
|
962
|
+
> **Note:**
|
|
963
|
+
> - Both `item` and `summary` fields act like a [comment action](#comment-action),
|
|
964
|
+
> therefore [Ruby expression](#ruby-expression) is supported.
|
|
965
|
+
> - Placeholders work regularly for `item`, but for `summary` only `{{title}}`,
|
|
966
|
+
> `{{items}}`, `{{type}}` are supported because it's not tied to a particular
|
|
967
|
+
> resource like the comment action.
|
|
968
|
+
> - No issues will be created if:
|
|
969
|
+
> - the specific policy doesn't yield any resources; or
|
|
970
|
+
> - the source type is a group and `destination` is not set.
|
|
971
|
+
> - `redact_confidential_resources` defaults to `true`, so fields on
|
|
972
|
+
> confidential resources will be converted to `(confidential)` except for
|
|
973
|
+
> `{{web_url}}`. Setting it to `false` will reveal the confidential fields.
|
|
974
|
+
> This will be useful if the summary is confidential itself (not implemented
|
|
975
|
+
> yet), or if we're posting to another private project (not implemented yet).
|
|
976
|
+
|
|
977
|
+
Example:
|
|
978
|
+
|
|
979
|
+
```yml
|
|
980
|
+
resource_rules:
|
|
981
|
+
issues:
|
|
982
|
+
rules:
|
|
983
|
+
- name: Issues require labels
|
|
984
|
+
limits:
|
|
985
|
+
most_recent: 15
|
|
986
|
+
actions:
|
|
987
|
+
summarize:
|
|
988
|
+
title: |
|
|
989
|
+
#{resource[:type].capitalize} require labels
|
|
990
|
+
item: |
|
|
991
|
+
- [ ] [{{title}}]({{web_url}}) {{labels}}
|
|
992
|
+
summary: |
|
|
993
|
+
The following {{type}} require labels:
|
|
994
|
+
|
|
995
|
+
{{items}}
|
|
996
|
+
|
|
997
|
+
Please take care of them before the end of #{7.days.from_now.strftime('%Y-%m-%d')}
|
|
998
|
+
|
|
999
|
+
/label ~"needs attention"
|
|
1000
|
+
```
|
|
1001
|
+
|
|
1002
|
+
Which could generate an issue like:
|
|
1003
|
+
|
|
1004
|
+
* Title:
|
|
1005
|
+
```
|
|
1006
|
+
Issues require labels
|
|
1007
|
+
```
|
|
1008
|
+
* Description:
|
|
1009
|
+
``` markdown
|
|
1010
|
+
The following issues require labels:
|
|
1011
|
+
|
|
1012
|
+
- [ ] [An example issue](http://example.com/group/project/issues/1) ~"label A", ~"label B"
|
|
1013
|
+
- [ ] [Another issue](http://example.com/group/project/issues/2) ~"label B", ~"label C"
|
|
1014
|
+
|
|
1015
|
+
Please take care of them before the end of 2000-01-01
|
|
1016
|
+
|
|
1017
|
+
/label ~"needs attention"
|
|
1018
|
+
```
|
|
1019
|
+
|
|
1020
|
+
##### Comment on summary action
|
|
1021
|
+
|
|
1022
|
+
Generates one comment for each resource, attaching these comments to the summary
|
|
1023
|
+
created by the [`summarize` action](#summarize-action).
|
|
1024
|
+
|
|
1025
|
+
The use case for this is wanting to create a summary with an overview, and then
|
|
1026
|
+
a threaded discussion for each resource, with a header comment starting each
|
|
1027
|
+
discussion.
|
|
1028
|
+
|
|
1029
|
+
Accepts a single string value: the template used to generate the comments. For
|
|
1030
|
+
details of the syntax of this template, see the [comment action](#comment-action).
|
|
1031
|
+
|
|
1032
|
+
Since this action depends on the summary, it is invalid to supply a
|
|
1033
|
+
`comment_on_summary` action without an accompanying `summarize` sibling action.
|
|
1034
|
+
The `summarize` action will always be completed first.
|
|
1035
|
+
|
|
1036
|
+
Just like for [comment action](#comment-action), setting `comment_type` in the
|
|
1037
|
+
`actions` set controls whether the comment must be resolved for merge requests.
|
|
1038
|
+
See: [`comment_type` action option](#comment-type-action-option).
|
|
1039
|
+
|
|
1040
|
+
Example:
|
|
1041
|
+
|
|
1042
|
+
```yml
|
|
1043
|
+
resource_rules:
|
|
1044
|
+
issues:
|
|
1045
|
+
rules:
|
|
1046
|
+
- name: List of issues to discuss
|
|
1047
|
+
limits:
|
|
1048
|
+
most_recent: 15
|
|
1049
|
+
actions:
|
|
1050
|
+
comment_type: thread
|
|
1051
|
+
comment_on_summary: |
|
|
1052
|
+
# {{title}}
|
|
1053
|
+
|
|
1054
|
+
author: {{author}}
|
|
1055
|
+
summarize:
|
|
1056
|
+
title: |
|
|
1057
|
+
#{resource[:type].capitalize} require labels
|
|
1058
|
+
item: |
|
|
1059
|
+
- [ ] [{{title}}]({{web_url}}) {{labels}}
|
|
1060
|
+
summary: |
|
|
1061
|
+
The following {{type}} require labels:
|
|
1062
|
+
|
|
1063
|
+
{{items}}
|
|
1064
|
+
|
|
1065
|
+
Please take care of them before the end of #{7.days.from_now.strftime('%Y-%m-%d')}
|
|
1066
|
+
|
|
1067
|
+
/label ~"needs attention"
|
|
1068
|
+
```
|
|
1069
|
+
|
|
1070
|
+
##### Create a new issue from each resource
|
|
1071
|
+
|
|
1072
|
+
Generates one issue for each resource, by default in the same project as the resource.
|
|
1073
|
+
|
|
1074
|
+
The use case for this is, for example, creating test issues in the same (or different)
|
|
1075
|
+
project for issues labeled "extended-testing"; or automatically splitting one issue with a
|
|
1076
|
+
certain label into multiple ones.
|
|
1077
|
+
|
|
1078
|
+
Accepts a hash of fields.
|
|
1079
|
+
|
|
1080
|
+
| Field | Type | Description | Required | Placeholders | Ruby expression | Default |
|
|
1081
|
+
| ---- | ---- | ---- | ---- | ---- | ---- | ---- |
|
|
1082
|
+
| `title` | string | The title of the generated issue | yes | yes | yes | |
|
|
1083
|
+
| `destination` | integer or string | The project ID or path to create the generated issue in | no | no | no | source project |
|
|
1084
|
+
| `description` | string | The description of the generated issue | no | yes | yes | |
|
|
1085
|
+
| `redact_confidential_resources` | boolean | Whether redact fields for confidential resources | no | no | no | true |
|
|
1086
|
+
|
|
1087
|
+
The placeholders available in `title` and `destination` are the properties of the resource being used to generate the issue.
|
|
1088
|
+
|
|
1089
|
+
Example
|
|
1090
|
+
|
|
1091
|
+
```yml
|
|
1092
|
+
resource_rules:
|
|
1093
|
+
issues:
|
|
1094
|
+
rules:
|
|
1095
|
+
- name: Issues requiring extra testing
|
|
1096
|
+
labels:
|
|
1097
|
+
- needs-testing
|
|
1098
|
+
actions:
|
|
1099
|
+
issue:
|
|
1100
|
+
title: |
|
|
1101
|
+
Testing: {{ title }}
|
|
1102
|
+
description: |
|
|
1103
|
+
The issue {{ full_reference }} needs testing.
|
|
1104
|
+
|
|
1105
|
+
Please take care of them before the end of #{7.days.from_now.strftime('%Y-%m-%d')}
|
|
1106
|
+
|
|
1107
|
+
/label ~"needs attention"
|
|
1108
|
+
```
|
|
1109
|
+
|
|
1110
|
+
##### Delete action
|
|
1111
|
+
|
|
1112
|
+
**This action is only applicable for branches.**
|
|
1113
|
+
|
|
1114
|
+
Delete the resource.
|
|
1115
|
+
|
|
1116
|
+
Accept a boolean. Set to `true` to enable.
|
|
1117
|
+
|
|
1118
|
+
Example :
|
|
1119
|
+
```yaml
|
|
1120
|
+
resource_rules:
|
|
1121
|
+
branches:
|
|
1122
|
+
rules:
|
|
1123
|
+
- name: My branch policy
|
|
1124
|
+
conditions:
|
|
1125
|
+
date:
|
|
1126
|
+
attribute: committed_date
|
|
1127
|
+
condition: older_than
|
|
1128
|
+
interval_type: months
|
|
1129
|
+
interval: 30
|
|
1130
|
+
actions:
|
|
1131
|
+
delete: true
|
|
1132
|
+
```
|
|
1133
|
+
|
|
1134
|
+
### Summary policies
|
|
1135
|
+
|
|
1136
|
+
Summary policies are special policies that join multiple rule policies together
|
|
1137
|
+
to create a summary issue with all the sub-policies' summaries.
|
|
1138
|
+
They have the same structure as Rule policies that define `actions.summarize`.
|
|
1139
|
+
|
|
1140
|
+
One key difference is that the `{{items}}` placeholder represents the array of
|
|
1141
|
+
sub-policies' summary.
|
|
1142
|
+
|
|
1143
|
+
Note that only the `summarize` keys in the sub-policies' `actions` is used. Any
|
|
1144
|
+
other keys (e.g. `mention`, `comment`, `labels` etc.) are ignored.
|
|
1145
|
+
|
|
1146
|
+
You can define such policy as follows:
|
|
1147
|
+
|
|
1148
|
+
```yml
|
|
1149
|
+
resource_rules:
|
|
1150
|
+
issues:
|
|
1151
|
+
summaries:
|
|
1152
|
+
- name: Newest and oldest issues summary
|
|
1153
|
+
rules:
|
|
1154
|
+
- name: New issues
|
|
1155
|
+
conditions:
|
|
1156
|
+
state: opened
|
|
1157
|
+
limits:
|
|
1158
|
+
most_recent: 2
|
|
1159
|
+
actions:
|
|
1160
|
+
summarize:
|
|
1161
|
+
item: "- [ ] [{{title}}]({{web_url}}) {{labels}}"
|
|
1162
|
+
summary: |
|
|
1163
|
+
Please triage the following new {{type}}:
|
|
1164
|
+
|
|
1165
|
+
{{items}}
|
|
1166
|
+
- name: Old issues
|
|
1167
|
+
conditions:
|
|
1168
|
+
state: opened
|
|
1169
|
+
limits:
|
|
1170
|
+
oldest: 2
|
|
1171
|
+
actions:
|
|
1172
|
+
summarize:
|
|
1173
|
+
item: "- [ ] [{{title}}]({{web_url}}) {{labels}}"
|
|
1174
|
+
summary: |
|
|
1175
|
+
Please triage the following old {{type}}:
|
|
1176
|
+
|
|
1177
|
+
{{items}}
|
|
1178
|
+
actions:
|
|
1179
|
+
summarize:
|
|
1180
|
+
title: "Newest and oldest {{type}} summary"
|
|
1181
|
+
summary: |
|
|
1182
|
+
Please triage the following {{type}}:
|
|
1183
|
+
|
|
1184
|
+
{{items}}
|
|
1185
|
+
|
|
1186
|
+
Please take care of them before the end of #{7.days.from_now.strftime('%Y-%m-%d')}
|
|
1187
|
+
|
|
1188
|
+
/label ~"needs attention"
|
|
1189
|
+
```
|
|
1190
|
+
|
|
1191
|
+
Which could generate an issue like:
|
|
1192
|
+
|
|
1193
|
+
* Title:
|
|
1194
|
+
```
|
|
1195
|
+
Newest and oldest issues summary
|
|
1196
|
+
```
|
|
1197
|
+
* Description:
|
|
1198
|
+
``` markdown
|
|
1199
|
+
Please triage the following issues:
|
|
1200
|
+
|
|
1201
|
+
Please triage the following new issues:
|
|
1202
|
+
|
|
1203
|
+
- [ ] [A new issue](http://example.com/group/project/issues/4)
|
|
1204
|
+
- [ ] [Another new issue](http://example.com/group/project/issues/3) ~"label B", ~"label C"
|
|
1205
|
+
|
|
1206
|
+
Please triage the following old issues:
|
|
1207
|
+
|
|
1208
|
+
- [ ] [An old issue](http://example.com/group/project/issues/1) ~"label A", ~"label B"
|
|
1209
|
+
- [ ] [Another old issue](http://example.com/group/project/issues/2) ~"label C"
|
|
1210
|
+
|
|
1211
|
+
Please take care of them before the end of 2000-01-01
|
|
1212
|
+
|
|
1213
|
+
/label ~"needs attention"
|
|
1214
|
+
```
|
|
1215
|
+
|
|
1216
|
+
> **Note:** If a specific policy doesn't yield any resources, it will not
|
|
1217
|
+
> generate the corresponding description. If all policies yield no resources,
|
|
1218
|
+
> then no issues will be created.
|
|
1219
|
+
|
|
1220
|
+
### Ruby expression API
|
|
1221
|
+
|
|
1222
|
+
Here's a list of currently available Ruby expression API:
|
|
1223
|
+
|
|
1224
|
+
##### Methods for `Issue` and `MergeRequest` (the context)
|
|
1225
|
+
|
|
1226
|
+
| Name | Return type | Description |
|
|
1227
|
+
| ---- | ---- | ---- |
|
|
1228
|
+
| resource | Hash | The hash containing the raw data of the resource. Note that `resource[:type]` is the type of the policy (`issues`, `merge_requests`, or `epics`), not the API `type` field. |
|
|
1229
|
+
| author | String | The username of the resource author |
|
|
1230
|
+
| state | String | The state of the resource |
|
|
1231
|
+
| milestone | Milestone | The milestone attached to the resource |
|
|
1232
|
+
| labels | [Label] | A list of labels, having only names |
|
|
1233
|
+
| labels_with_details | [Label] | A list of labels which has more information loaded from another API request |
|
|
1234
|
+
| labels_chronologically | [Label] | Same as `labels_with_details` but sorted chronologically |
|
|
1235
|
+
| label_events | [LabelEvent] | A list of label events on the resource |
|
|
1236
|
+
| instance_version | InstanceVersion | The version for the GitLab instance we're triaging with |
|
|
1237
|
+
| project_path | String | The path with namespace to the issues or merge requests project |
|
|
1238
|
+
| full_resource_reference | String | A full reference including project path to the issue or merge request |
|
|
1239
|
+
|
|
1240
|
+
##### Methods for `Issue` and `LinkedIssue` (issue context)
|
|
1241
|
+
|
|
1242
|
+
| Name | Return type | Description |
|
|
1243
|
+
| ---- | ---- | ---- |
|
|
1244
|
+
| merge_requests_count | Integer | The number of merge requests related to the issue |
|
|
1245
|
+
| related_merge_requests | [MergeRequest] | The list of merge requests related to the issue |
|
|
1246
|
+
| closed_by | [MergeRequest] | The list of merge requests that close the issue |
|
|
1247
|
+
| linked_issues | [LinkedIssue] | The list of issues that are linked to the issue |
|
|
1248
|
+
| due_date | Date | The due date of the issue. Could be `nil` |
|
|
1249
|
+
|
|
1250
|
+
##### Methods for `LinkedIssue`
|
|
1251
|
+
|
|
1252
|
+
| Method | Return type | Description |
|
|
1253
|
+
| ---- | ---- | ---- |
|
|
1254
|
+
| link_type | String | The link type of the linked issue (`blocks`, `is_blocked_by`, or `relates_to`) |
|
|
1255
|
+
|
|
1256
|
+
##### Methods for `MergeRequest` (merge request context)
|
|
1257
|
+
|
|
1258
|
+
| Method | Return type | Description |
|
|
1259
|
+
| ---- | ---- | ---- |
|
|
1260
|
+
| first_contribution? | Boolean | `true` if it's the author's first contribution to the project; `false` otherwise. This API requires an additional API request for the merge request, thus would be slower. |
|
|
1261
|
+
|
|
1262
|
+
##### Methods for `Milestone`
|
|
1263
|
+
|
|
1264
|
+
| Method | Return type | Description |
|
|
1265
|
+
| ---- | ---- | ---- |
|
|
1266
|
+
| id | Integer | The id of the milestone |
|
|
1267
|
+
| iid | Integer | The iid of the milestone |
|
|
1268
|
+
| project_id | Integer | The project id of the milestone if available |
|
|
1269
|
+
| group_id | Integer | The group id of the milestone if available |
|
|
1270
|
+
| title | String | The title of the milestone |
|
|
1271
|
+
| description | String | The description of the milestone |
|
|
1272
|
+
| state | String | The state of the milestone. Could be `active` or `closed` |
|
|
1273
|
+
| due_date | Date | The due date of the milestone. Could be `nil` |
|
|
1274
|
+
| start_date | Date | The start date of the milestone. Could be `nil` |
|
|
1275
|
+
| updated_at | Time | The updated timestamp of the milestone |
|
|
1276
|
+
| created_at | Time | The created timestamp of the milestone |
|
|
1277
|
+
| succ | Milestone | The next active milestone beside this milestone |
|
|
1278
|
+
| active? | Boolean | `true` if `state` is `active`; `false` otherwise |
|
|
1279
|
+
| closed? | Boolean | `true` if `state` is `closed`; `false` otherwise |
|
|
1280
|
+
| started? | Boolean | `true` if `start_date` exists and in the past; `false` otherwise |
|
|
1281
|
+
| expired? | Boolean | `true` if `due_date` exists and in the past; `false` otherwise |
|
|
1282
|
+
| in_progress?| Boolean | `true` if `started?` and `!expired`; `false` otherwise |
|
|
1283
|
+
|
|
1284
|
+
##### Methods for `Label`
|
|
1285
|
+
|
|
1286
|
+
| Method | Return type | Description |
|
|
1287
|
+
| ---- | ---- | ---- |
|
|
1288
|
+
| id | Integer | The id of the label |
|
|
1289
|
+
| project_id | Integer | The project id of the label if available |
|
|
1290
|
+
| group_id | Integer | The group id of the label if available |
|
|
1291
|
+
| name | String | The name of the label |
|
|
1292
|
+
| description | String | The description of the label |
|
|
1293
|
+
| color | String | The color of the label in RGB |
|
|
1294
|
+
| priority | Integer | The priority of the label |
|
|
1295
|
+
| added_at | Time | When the label was added to the resource |
|
|
1296
|
+
|
|
1297
|
+
##### Methods for `LabelEvent`
|
|
1298
|
+
|
|
1299
|
+
| Method | Return type | Description |
|
|
1300
|
+
| ---- | ---- | ---- |
|
|
1301
|
+
| id | Integer | The id of the label event |
|
|
1302
|
+
| resource_type | String | The resource type of the event. Could be `Issue` or `MergeRequest` |
|
|
1303
|
+
| resource_id | Integer | The id of the resource |
|
|
1304
|
+
| action | String | The action of the event. Could be `add` or `remove` |
|
|
1305
|
+
| created_at | Time | When the event happened |
|
|
1306
|
+
|
|
1307
|
+
##### Methods for `InstanceVersion`
|
|
1308
|
+
|
|
1309
|
+
| Method | Return type | Description |
|
|
1310
|
+
| ---- | ---- | ---- |
|
|
1311
|
+
| version | String | The full string of version. e.g. `11.3.0-rc11-ee` |
|
|
1312
|
+
| version_short | String | The short string of version. e.g. `11.3` |
|
|
1313
|
+
| revision | String | The revision of GitLab. e.g. `231b0c7` |
|
|
1314
|
+
|
|
1315
|
+
### Installation
|
|
1316
|
+
|
|
1317
|
+
gem install gitlab-triage
|
|
1318
|
+
|
|
1319
|
+
### Usage
|
|
1320
|
+
|
|
1321
|
+
gitlab-triage --help
|
|
1322
|
+
|
|
1323
|
+
Will show:
|
|
1324
|
+
|
|
1325
|
+
```
|
|
1326
|
+
Usage: gitlab-triage [options]
|
|
1327
|
+
|
|
1328
|
+
-n, --dry-run Don't actually update anything, just print
|
|
1329
|
+
-f, --policies-file [string] A valid policies YML file
|
|
1330
|
+
--all-projects Process all projects the token has access to
|
|
1331
|
+
-s, --source [type] The source type between [ projects or groups ], default value: projects
|
|
1332
|
+
-i, --source-id [string] Source ID or path
|
|
1333
|
+
-p, --project-id [string] [Deprecated] A project ID or path, please use `--source-id`
|
|
1334
|
+
--resource-reference [string]
|
|
1335
|
+
Resource short-reference, e.g. #42, !33, or &99
|
|
1336
|
+
-t, --token [string] A valid API token
|
|
1337
|
+
-H, --host-url [string] A valid host url
|
|
1338
|
+
-r, --require [string] Require a file before performing
|
|
1339
|
+
-d, --debug Print debug information
|
|
1340
|
+
-h, --help Print help message
|
|
1341
|
+
-v, --version Print version
|
|
1342
|
+
--init Initialize the project with a policy file
|
|
1343
|
+
--init-ci Initialize the project with a .gitlab-ci.yml file
|
|
1344
|
+
```
|
|
1345
|
+
|
|
1346
|
+
#### Running with the installed gem
|
|
1347
|
+
|
|
1348
|
+
Triaging against a specific project:
|
|
1349
|
+
|
|
1350
|
+
```
|
|
1351
|
+
gitlab-triage --dry-run --token $GITLAB_API_TOKEN --source-id gitlab-org/triage
|
|
1352
|
+
```
|
|
1353
|
+
|
|
1354
|
+
Triaging against a whole group:
|
|
1355
|
+
|
|
1356
|
+
```
|
|
1357
|
+
gitlab-triage --dry-run --token $GITLAB_API_TOKEN --source-id gitlab-org --source groups
|
|
1358
|
+
```
|
|
1359
|
+
|
|
1360
|
+
Triaging against an entire instance:
|
|
1361
|
+
|
|
1362
|
+
```
|
|
1363
|
+
gitlab-triage --dry-run --token $GITLAB_API_TOKEN --all-projects
|
|
1364
|
+
```
|
|
1365
|
+
|
|
1366
|
+
> **Note:** The `--all-projects` option will process all resources for all projects visible to the specified `$GITLAB_API_TOKEN`
|
|
1367
|
+
|
|
1368
|
+
#### Running from source
|
|
1369
|
+
|
|
1370
|
+
Execute the `gitlab-triage` script from the `./bin` directory.
|
|
1371
|
+
|
|
1372
|
+
For example- after cloning this project, from the root `gitlab-triage` directory:
|
|
1373
|
+
|
|
1374
|
+
```
|
|
1375
|
+
bundle exec bin/gitlab-triage --dry-run --token $GITLAB_API_TOKEN --source-id gitlab-org/triage
|
|
1376
|
+
```
|
|
1377
|
+
|
|
1378
|
+
Triaging against specific resource:
|
|
1379
|
+
|
|
1380
|
+
```
|
|
1381
|
+
gitlab-triage --dry-run --token $API_TOKEN --source-id gitlab-org/triage --resource-reference '#42'
|
|
1382
|
+
gitlab-triage --dry-run --token $API_TOKEN --source-id gitlab-org/triage --resource-reference '!33'
|
|
1383
|
+
gitlab-triage --dry-run --token $API_TOKEN --source groups --source-id gitlab-org --resource-reference '&99'
|
|
1384
|
+
```
|
|
1385
|
+
|
|
1386
|
+
#### Running on GitLab CI pipeline
|
|
1387
|
+
|
|
1388
|
+
You can enforce policies using a scheduled pipeline:
|
|
1389
|
+
|
|
1390
|
+
```yml
|
|
1391
|
+
run:triage:triage:
|
|
1392
|
+
stage: triage
|
|
1393
|
+
script:
|
|
1394
|
+
- gem install gitlab-triage
|
|
1395
|
+
- gitlab-triage --token $GITLAB_API_TOKEN --source-id $CI_PROJECT_PATH
|
|
1396
|
+
rules:
|
|
1397
|
+
- if: $CI_PIPELINE_SOURCE == "schedule"
|
|
1398
|
+
```
|
|
1399
|
+
|
|
1400
|
+
> **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
|
|
1401
|
+
|
|
1402
|
+
#### Can I use gitlab-triage for my self-hosted GitLab instance?
|
|
1403
|
+
|
|
1404
|
+
Yes, you can override the host url using the following options:
|
|
1405
|
+
|
|
1406
|
+
##### CLI
|
|
1407
|
+
|
|
1408
|
+
```
|
|
1409
|
+
gitlab-triage --dry-run --token $GITLAB_API_TOKEN --source-id gitlab-org/triage --host-url https://gitlab.host.com
|
|
1410
|
+
```
|
|
1411
|
+
|
|
1412
|
+
##### Policy file
|
|
1413
|
+
|
|
1414
|
+
```yml
|
|
1415
|
+
host_url: https://gitlab.host.com
|
|
1416
|
+
resource_rules:
|
|
1417
|
+
```
|
|
1418
|
+
|
|
1419
|
+
#### Can I customize?
|
|
1420
|
+
|
|
1421
|
+
You can take the advantage of command line option `-r` or `--require` to
|
|
1422
|
+
load a Ruby file before performing the actions. This allows you to do
|
|
1423
|
+
whatever you want. For example, you can put this in a file like `my_plugin.rb`:
|
|
1424
|
+
|
|
1425
|
+
```ruby
|
|
1426
|
+
module MyPlugin
|
|
1427
|
+
def has_severity_label?
|
|
1428
|
+
labels.grep(/^S\d+$/).any?
|
|
1429
|
+
end
|
|
1430
|
+
|
|
1431
|
+
def has_priority_label?
|
|
1432
|
+
labels.grep(/^P\d+$/).any?
|
|
1433
|
+
end
|
|
1434
|
+
|
|
1435
|
+
def labels
|
|
1436
|
+
resource[:labels]
|
|
1437
|
+
end
|
|
1438
|
+
end
|
|
1439
|
+
|
|
1440
|
+
Gitlab::Triage::Resource::Context.include MyPlugin
|
|
1441
|
+
```
|
|
1442
|
+
|
|
1443
|
+
And then run it with:
|
|
1444
|
+
|
|
1445
|
+
```shell
|
|
1446
|
+
gitlab-triage -r ./my_plugin.rb --token $GITLAB_API_TOKEN --source-id gitlab-org/triage
|
|
1447
|
+
```
|
|
1448
|
+
|
|
1449
|
+
This allows you to use `has_severity_label?` in the Ruby condition:
|
|
1450
|
+
|
|
1451
|
+
```yml
|
|
1452
|
+
resource_rules:
|
|
1453
|
+
issues:
|
|
1454
|
+
rules:
|
|
1455
|
+
- name: Apply default severity or priority labels
|
|
1456
|
+
conditions:
|
|
1457
|
+
ruby: |
|
|
1458
|
+
!has_severity_label? || !has_priority_label?
|
|
1459
|
+
actions:
|
|
1460
|
+
comment: |
|
|
1461
|
+
#{'/label ~S3' unless has_severity_label?}
|
|
1462
|
+
#{'/label ~P3' unless has_priority_label?}
|
|
1463
|
+
```
|
|
1464
|
+
|
|
1465
|
+
### Contributing
|
|
1466
|
+
|
|
1467
|
+
Please refer to the [Contributing Guide](CONTRIBUTING.md).
|
|
1468
|
+
|
|
1469
|
+
## Release Process
|
|
1470
|
+
|
|
1471
|
+
We release `gitlab-triage` on an ad-hoc basis. There is no regularity to when
|
|
1472
|
+
we release, we just release when we make a change - no matter the size of the
|
|
1473
|
+
change.
|
|
1474
|
+
|
|
1475
|
+
To release a new version:
|
|
1476
|
+
|
|
1477
|
+
1. Create a Merge Request.
|
|
1478
|
+
1. Use Merge Request template [Release.md](https://gitlab.com/gitlab-org/ruby/gems/gitlab-triage/-/blob/master/.gitlab/merge_request_templates/Release.md).
|
|
1479
|
+
1. Follow the instructions.
|
|
1480
|
+
1. After the Merge Request has been merged, a new gem version is [published automatically](https://gitlab.com/gitlab-org/quality/pipeline-common/-/blob/master/ci/gem-release.yml)
|