gitlab-triage 1.56.2 → 1.57.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 +4 -4
- data/.gitlab/CODEOWNERS +1 -2
- data/.gitlab-ci.yml +0 -13
- data/.rubocop.yml +5 -0
- data/.rubocop_todo.yml +0 -21
- data/Gemfile.lock +39 -26
- data/README.md +15 -11
- data/gitlab-triage.gemspec +4 -7
- data/lib/gitlab/triage/action/issue.rb +1 -1
- data/lib/gitlab/triage/action/summarize.rb +1 -1
- data/lib/gitlab/triage/action/work_item.rb +42 -13
- data/lib/gitlab/triage/engine.rb +1 -1
- data/lib/gitlab/triage/network_adapters/base_adapter.rb +17 -0
- data/lib/gitlab/triage/network_adapters/graphql_adapter.rb +1 -19
- data/lib/gitlab/triage/network_adapters/httparty_adapter.rb +10 -10
- data/lib/gitlab/triage/option_parser.rb +4 -4
- data/lib/gitlab/triage/policies/base_policy.rb +1 -1
- data/lib/gitlab/triage/rest_api_network.rb +5 -2
- data/lib/gitlab/triage/validators/policy_validator.rb +12 -1
- data/lib/gitlab/triage/version.rb +1 -1
- metadata +12 -7
- data/.codeclimate.yml +0 -19
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 624c4d1977031933b3f310f7ae5c82778ac62bcda0f86a0c2c4a375504b6e715
|
|
4
|
+
data.tar.gz: 9545979a57eea0ff733995ac553271ad310c2c2710ac4fe78645daff962449b5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1721670e0285a0d0fffd8cb076becb239e859267d1da55f4819526f39128dec62e884251f6ae28e9d1550eeca2eb29f8c5b08c1c8d6e16b480f22254bf20016c
|
|
7
|
+
data.tar.gz: 7b233404c63d41de48dfadf6e7fdacadb6412c47fe00562d417626534ef21b5dd8c4c14340b1d46f27bad1c5c2e58378d55e80b6aac5c8e0f25f12d2075c3515
|
data/.gitlab/CODEOWNERS
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* @gl-quality/eng-prod
|
|
1
|
+
* @gl-dx
|
data/.gitlab-ci.yml
CHANGED
|
@@ -107,7 +107,6 @@ dry-run:custom:
|
|
|
107
107
|
allow_failure: true
|
|
108
108
|
|
|
109
109
|
include:
|
|
110
|
-
- component: gitlab.com/components/code-quality/code-quality@~latest
|
|
111
110
|
- component: gitlab.com/components/sast/sast@~latest
|
|
112
111
|
- component: gitlab.com/components/secret-detection/secret-detection@~latest
|
|
113
112
|
- component: gitlab.com/gitlab-org/components/danger-review/danger-review@~latest
|
|
@@ -119,18 +118,6 @@ include:
|
|
|
119
118
|
- if: '$CI_MERGE_REQUEST_IID'
|
|
120
119
|
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
|
|
121
120
|
|
|
122
|
-
code_quality:
|
|
123
|
-
# We explicitly set dependencies here as the component sets them to []
|
|
124
|
-
# This allows us to get the cache from the setup job that includes the Gemfile.lock
|
|
125
|
-
dependencies: ["setup-test-env"]
|
|
126
|
-
tags:
|
|
127
|
-
- gitlab-org-docker
|
|
128
|
-
# We explicitly override allow_failure here as the component sets it to true
|
|
129
|
-
# Can be moved to a component input after: https://gitlab.com/components/code-quality/-/merge_requests/8
|
|
130
|
-
allow_failure: false
|
|
131
|
-
<<: *ruby-cache
|
|
132
|
-
<<: *component-general-rules
|
|
133
|
-
|
|
134
121
|
semgrep-sast:
|
|
135
122
|
<<: *component-general-rules
|
|
136
123
|
|
data/.rubocop.yml
CHANGED
|
@@ -51,3 +51,8 @@ RSpec/IndexedLet:
|
|
|
51
51
|
# rationale: Maybe we should? For now, let's keep it disabled and revisit at a later point.
|
|
52
52
|
Rails/Output:
|
|
53
53
|
Enabled: false
|
|
54
|
+
|
|
55
|
+
# rationale: gitlab-triage has no database / ActiveRecord models
|
|
56
|
+
# context: https://gitlab.com/gitlab-org/ruby/gems/gitlab-triage/-/work_items/399
|
|
57
|
+
CodeReuse/ActiveRecord:
|
|
58
|
+
Enabled: false
|
data/.rubocop_todo.yml
CHANGED
|
@@ -6,27 +6,6 @@
|
|
|
6
6
|
# Note that changes in the inspected code, or installation of new
|
|
7
7
|
# versions of RuboCop, may require this file to be generated again.
|
|
8
8
|
|
|
9
|
-
# Offense count: 81
|
|
10
|
-
CodeReuse/ActiveRecord:
|
|
11
|
-
Exclude:
|
|
12
|
-
- 'lib/gitlab/triage/action/work_item.rb'
|
|
13
|
-
- 'lib/gitlab/triage/engine.rb'
|
|
14
|
-
- 'lib/gitlab/triage/filters/base_conditions_filter.rb'
|
|
15
|
-
- 'lib/gitlab/triage/filters/member_conditions_filter.rb'
|
|
16
|
-
- 'lib/gitlab/triage/graphql_network.rb'
|
|
17
|
-
- 'lib/gitlab/triage/normalizers/work_item_normalizer.rb'
|
|
18
|
-
- 'lib/gitlab/triage/validators/limiter_validator.rb'
|
|
19
|
-
- 'spec/gitlab/triage/action_spec.rb'
|
|
20
|
-
- 'spec/gitlab/triage/engine_spec.rb'
|
|
21
|
-
- 'spec/gitlab/triage/graphql_network_spec.rb'
|
|
22
|
-
- 'spec/gitlab/triage/network_adapters/graphql_adapter_spec.rb'
|
|
23
|
-
- 'spec/gitlab/triage/policies/rule_policy_spec.rb'
|
|
24
|
-
- 'spec/gitlab/triage/resource/label_spec.rb'
|
|
25
|
-
- 'spec/gitlab/triage/rest_api_network_spec.rb'
|
|
26
|
-
- 'spec/support/shared_examples/issuable_shared_examples.rb'
|
|
27
|
-
- 'spec/support/shared_examples/label_command_shared_examples.rb'
|
|
28
|
-
- 'spec/support/stub_api.rb'
|
|
29
|
-
|
|
30
9
|
# Offense count: 1
|
|
31
10
|
Lint/ToEnumArguments:
|
|
32
11
|
Exclude:
|
data/Gemfile.lock
CHANGED
|
@@ -1,31 +1,35 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
gitlab-triage (1.
|
|
4
|
+
gitlab-triage (1.57.0)
|
|
5
5
|
activesupport (>= 5.1)
|
|
6
6
|
globalid (~> 1.0, >= 1.0.1)
|
|
7
|
-
graphql (< 2.
|
|
8
|
-
graphql-client (~> 0.
|
|
7
|
+
graphql (>= 2.4.13, < 2.7.0)
|
|
8
|
+
graphql-client (~> 0.23)
|
|
9
9
|
httparty (>= 0.21.0, < 1.0)
|
|
10
10
|
|
|
11
11
|
GEM
|
|
12
12
|
remote: https://rubygems.org/
|
|
13
13
|
specs:
|
|
14
|
-
activesupport (7.1.
|
|
14
|
+
activesupport (7.1.6)
|
|
15
15
|
base64
|
|
16
|
+
benchmark (>= 0.3)
|
|
16
17
|
bigdecimal
|
|
17
18
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
18
19
|
connection_pool (>= 2.2.5)
|
|
19
20
|
drb
|
|
20
21
|
i18n (>= 1.6, < 2)
|
|
22
|
+
logger (>= 1.4.2)
|
|
21
23
|
minitest (>= 5.1)
|
|
22
24
|
mutex_m
|
|
25
|
+
securerandom (>= 0.3)
|
|
23
26
|
tzinfo (~> 2.0)
|
|
24
|
-
addressable (2.
|
|
25
|
-
public_suffix (>= 2.0.2, <
|
|
27
|
+
addressable (2.9.0)
|
|
28
|
+
public_suffix (>= 2.0.2, < 8.0)
|
|
26
29
|
ast (2.4.2)
|
|
27
|
-
base64 (0.
|
|
28
|
-
|
|
30
|
+
base64 (0.3.0)
|
|
31
|
+
benchmark (0.5.0)
|
|
32
|
+
bigdecimal (4.1.3)
|
|
29
33
|
claide (1.1.0)
|
|
30
34
|
claide-plugins (0.9.2)
|
|
31
35
|
cork
|
|
@@ -33,8 +37,8 @@ GEM
|
|
|
33
37
|
open4 (~> 1.3)
|
|
34
38
|
coderay (1.1.3)
|
|
35
39
|
colored2 (3.1.2)
|
|
36
|
-
concurrent-ruby (1.3.
|
|
37
|
-
connection_pool (2.
|
|
40
|
+
concurrent-ruby (1.3.8)
|
|
41
|
+
connection_pool (2.5.5)
|
|
38
42
|
cork (0.3.0)
|
|
39
43
|
colored2 (~> 3.1)
|
|
40
44
|
crack (1.0.0)
|
|
@@ -59,13 +63,16 @@ GEM
|
|
|
59
63
|
gitlab (~> 4.2, >= 4.2.0)
|
|
60
64
|
diff-lcs (1.5.1)
|
|
61
65
|
drb (2.2.3)
|
|
62
|
-
faraday (2.
|
|
63
|
-
faraday-net_http (>= 2.0, < 3.
|
|
66
|
+
faraday (2.14.4)
|
|
67
|
+
faraday-net_http (>= 2.0, < 3.5)
|
|
68
|
+
json
|
|
69
|
+
logger
|
|
64
70
|
faraday-http-cache (2.5.1)
|
|
65
71
|
faraday (>= 0.8)
|
|
66
|
-
faraday-net_http (3.
|
|
67
|
-
net-http
|
|
72
|
+
faraday-net_http (3.4.4)
|
|
73
|
+
net-http (~> 0.5)
|
|
68
74
|
ffi (1.17.0)
|
|
75
|
+
fiber-storage (1.0.0)
|
|
69
76
|
formatador (1.1.0)
|
|
70
77
|
git (1.19.1)
|
|
71
78
|
addressable (~> 2.8)
|
|
@@ -85,9 +92,11 @@ GEM
|
|
|
85
92
|
rubocop-rspec (~> 2.27.1)
|
|
86
93
|
globalid (1.2.1)
|
|
87
94
|
activesupport (>= 6.1)
|
|
88
|
-
graphql (2.
|
|
95
|
+
graphql (2.6.11)
|
|
89
96
|
base64
|
|
90
|
-
|
|
97
|
+
fiber-storage
|
|
98
|
+
logger
|
|
99
|
+
graphql-client (0.26.0)
|
|
91
100
|
activesupport (>= 3.0)
|
|
92
101
|
graphql (>= 1.13.0)
|
|
93
102
|
guard (2.18.1)
|
|
@@ -109,7 +118,7 @@ GEM
|
|
|
109
118
|
csv
|
|
110
119
|
mini_mime (>= 1.0.0)
|
|
111
120
|
multi_xml (>= 0.5.2)
|
|
112
|
-
i18n (1.14.
|
|
121
|
+
i18n (1.14.8)
|
|
113
122
|
concurrent-ruby (~> 1.0)
|
|
114
123
|
json (2.7.2)
|
|
115
124
|
kramdown (2.4.0)
|
|
@@ -121,16 +130,20 @@ GEM
|
|
|
121
130
|
listen (3.9.0)
|
|
122
131
|
rb-fsevent (~> 0.10, >= 0.10.3)
|
|
123
132
|
rb-inotify (~> 0.9, >= 0.9.10)
|
|
133
|
+
logger (1.7.0)
|
|
124
134
|
lumberjack (1.2.10)
|
|
125
135
|
method_source (1.1.0)
|
|
126
136
|
mini_mime (1.1.5)
|
|
127
137
|
minitest (5.26.1)
|
|
128
138
|
multi_xml (0.6.0)
|
|
129
|
-
mutex_m (0.
|
|
139
|
+
mutex_m (0.3.0)
|
|
130
140
|
nap (1.1.0)
|
|
131
141
|
nenv (0.3.0)
|
|
132
|
-
net-http (0.
|
|
133
|
-
|
|
142
|
+
net-http (0.10.0)
|
|
143
|
+
net-protocol (>= 0.3.0)
|
|
144
|
+
uri (>= 0.11.1)
|
|
145
|
+
net-protocol (0.4.0)
|
|
146
|
+
timeout
|
|
134
147
|
no_proxy_fix (0.1.2)
|
|
135
148
|
notiffany (0.1.3)
|
|
136
149
|
nenv (~> 0.1)
|
|
@@ -146,9 +159,9 @@ GEM
|
|
|
146
159
|
pry (0.14.2)
|
|
147
160
|
coderay (~> 1.1)
|
|
148
161
|
method_source (~> 1.0)
|
|
149
|
-
public_suffix (
|
|
162
|
+
public_suffix (6.0.2)
|
|
150
163
|
racc (1.8.0)
|
|
151
|
-
rack (3.
|
|
164
|
+
rack (3.2.7)
|
|
152
165
|
rainbow (3.1.1)
|
|
153
166
|
rake (13.2.1)
|
|
154
167
|
rb-fsevent (0.11.2)
|
|
@@ -156,8 +169,7 @@ GEM
|
|
|
156
169
|
ffi (~> 1.0)
|
|
157
170
|
rchardet (1.8.0)
|
|
158
171
|
regexp_parser (2.9.2)
|
|
159
|
-
rexml (3.
|
|
160
|
-
strscan
|
|
172
|
+
rexml (3.4.4)
|
|
161
173
|
rspec (3.13.0)
|
|
162
174
|
rspec-core (~> 3.13.0)
|
|
163
175
|
rspec-expectations (~> 3.13.0)
|
|
@@ -206,15 +218,16 @@ GEM
|
|
|
206
218
|
sawyer (0.9.2)
|
|
207
219
|
addressable (>= 2.3.5)
|
|
208
220
|
faraday (>= 0.17.3, < 3)
|
|
221
|
+
securerandom (0.3.2)
|
|
209
222
|
shellany (0.0.1)
|
|
210
|
-
strscan (3.1.0)
|
|
211
223
|
terminal-table (3.0.2)
|
|
212
224
|
unicode-display_width (>= 1.1.1, < 3)
|
|
213
225
|
thor (1.3.1)
|
|
226
|
+
timeout (0.6.1)
|
|
214
227
|
tzinfo (2.0.6)
|
|
215
228
|
concurrent-ruby (~> 1.0)
|
|
216
229
|
unicode-display_width (2.5.0)
|
|
217
|
-
uri (
|
|
230
|
+
uri (1.1.2)
|
|
218
231
|
webmock (3.23.1)
|
|
219
232
|
addressable (>= 2.8.0)
|
|
220
233
|
crack (>= 0.3.2)
|
data/README.md
CHANGED
|
@@ -203,6 +203,8 @@ resource_rules:
|
|
|
203
203
|
- `labels` - add labels by name. If any requested label does not exist, the
|
|
204
204
|
automation stops immediately (matching the `labels` action on the `issues`
|
|
205
205
|
type) rather than applying nothing.
|
|
206
|
+
- `remove_labels` - remove labels by name. Like `labels`, a requested label that
|
|
207
|
+
does not exist stops the automation rather than applying a partial change.
|
|
206
208
|
- `assignees` - set assignees by username. If any requested username does not
|
|
207
209
|
exist, the automation stops immediately rather than sending an empty
|
|
208
210
|
assignee set (which would unassign everyone).
|
|
@@ -215,7 +217,7 @@ resource_rules:
|
|
|
215
217
|
to comment.
|
|
216
218
|
|
|
217
219
|
Summaries are also supported (`summarize` and `comment_on_summary`). Any other
|
|
218
|
-
action key (for example `
|
|
220
|
+
action key (for example `mention`, `move`, or `status`) is
|
|
219
221
|
rejected at load time with an error listing the supported actions. On the
|
|
220
222
|
older policy types these keys are applied through comment quick actions, which
|
|
221
223
|
work items do not support yet - rejecting them up front beats silently doing
|
|
@@ -345,7 +347,7 @@ Accepts the name of a milestone to filter upon. Also accepts the following timeb
|
|
|
345
347
|
- `upcoming`
|
|
346
348
|
- `started`
|
|
347
349
|
|
|
348
|
-
See the [`milestone_id` API field documentation](https://docs.gitlab.com/
|
|
350
|
+
See the [`milestone_id` API field documentation](https://docs.gitlab.com/api/issues/) for their meaning.
|
|
349
351
|
|
|
350
352
|
Example:
|
|
351
353
|
|
|
@@ -362,7 +364,7 @@ timebox values:
|
|
|
362
364
|
- `none`
|
|
363
365
|
- `any`
|
|
364
366
|
|
|
365
|
-
See the [`iteration_id` API field documentation](https://docs.gitlab.com/
|
|
367
|
+
See the [`iteration_id` API field documentation](https://docs.gitlab.com/api/issues/) for their meaning.
|
|
366
368
|
|
|
367
369
|
Example:
|
|
368
370
|
|
|
@@ -427,7 +429,7 @@ conditions:
|
|
|
427
429
|
|
|
428
430
|
###### Predefined special label names
|
|
429
431
|
|
|
430
|
-
Basing on the [issues API](https://docs.gitlab.com/
|
|
432
|
+
Basing on the [issues API](https://docs.gitlab.com/api/issues/), there
|
|
431
433
|
are two special predefined label names we can use here:
|
|
432
434
|
|
|
433
435
|
* `None`: This indicates that no labels were present
|
|
@@ -694,7 +696,7 @@ conditions:
|
|
|
694
696
|
|
|
695
697
|
**This condition is only applicable for issues.**
|
|
696
698
|
|
|
697
|
-
Accepts a string per the [API documentation](https://docs.gitlab.com/
|
|
699
|
+
Accepts a string per the [API documentation](https://docs.gitlab.com/api/issues/#list-all-issues).
|
|
698
700
|
|
|
699
701
|
| State | Type | Value |
|
|
700
702
|
| --------- | ---- | ------ |
|
|
@@ -715,7 +717,7 @@ conditions:
|
|
|
715
717
|
|
|
716
718
|
**This condition is only applicable for issues.**
|
|
717
719
|
|
|
718
|
-
Accepts a string per the [API documentation](https://docs.gitlab.com/
|
|
720
|
+
Accepts a string per the [API documentation](https://docs.gitlab.com/api/issues/#list-all-issues).
|
|
719
721
|
|
|
720
722
|
| State | Type | Value |
|
|
721
723
|
| --------- | ---- | ------ |
|
|
@@ -734,7 +736,7 @@ conditions:
|
|
|
734
736
|
|
|
735
737
|
**This condition is only applicable for issues.**
|
|
736
738
|
|
|
737
|
-
Accepts a string per the [API documentation](https://docs.gitlab.com/
|
|
739
|
+
Accepts a string per the [API documentation](https://docs.gitlab.com/api/issues/#list-all-issues). This condition can only filter by one issue type.
|
|
738
740
|
|
|
739
741
|
| Issue type | Type | Value |
|
|
740
742
|
| --------- | ---- | ------ |
|
|
@@ -1260,7 +1262,7 @@ The following comment types are supported:
|
|
|
1260
1262
|
- `comment` (default): creates a regular comment on the resource
|
|
1261
1263
|
- `thread`: starts a resolvable thread (discussion) on the resource
|
|
1262
1264
|
|
|
1263
|
-
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/
|
|
1265
|
+
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/user/project/merge_requests/#prevent-merge-unless-all-threads-are-resolved), therefore this comment can prevent it from merging.
|
|
1264
1266
|
|
|
1265
1267
|
Example:
|
|
1266
1268
|
|
|
@@ -1289,14 +1291,14 @@ actions:
|
|
|
1289
1291
|
|
|
1290
1292
|
###### Harnessing Quick Actions
|
|
1291
1293
|
|
|
1292
|
-
[GitLab's quick actions feature](https://docs.gitlab.com/
|
|
1294
|
+
[GitLab's quick actions feature](https://docs.gitlab.com/user/project/quick_actions/) is available in Core.
|
|
1293
1295
|
All of the operations supported by executing a quick action can be carried out via the comment action.
|
|
1294
1296
|
|
|
1295
1297
|
If GitLab triage does not support an operation natively, it may be possible via a quick action in a comment.
|
|
1296
1298
|
|
|
1297
1299
|
For example:
|
|
1298
|
-
- Flagging an issue as [confidential](https://docs.gitlab.com/
|
|
1299
|
-
- [Locking issue discussion](https://docs.gitlab.com/
|
|
1300
|
+
- Flagging an issue as [confidential](https://docs.gitlab.com/user/project/issues/confidential_issues/)
|
|
1301
|
+
- [Locking issue discussion](https://docs.gitlab.com/user/discussions/#prevent-comments-by-locking-the-discussion)
|
|
1300
1302
|
|
|
1301
1303
|
```yml
|
|
1302
1304
|
resource_rules:
|
|
@@ -1801,6 +1803,8 @@ Usage: gitlab-triage [options]
|
|
|
1801
1803
|
-H, --host-url [string] A valid host url
|
|
1802
1804
|
-r, --require [string] Require a file before performing
|
|
1803
1805
|
-d, --debug Print debug information
|
|
1806
|
+
--summary Print API call summary at end of execution
|
|
1807
|
+
--quiet Suppress pagination dots and section headers
|
|
1804
1808
|
--cache-dir [string] Directory containing pre-fetched API responses.
|
|
1805
1809
|
Files must follow the naming convention: <source>_<id>_<resource>.json
|
|
1806
1810
|
(e.g. groups_9970_members.json).
|
data/gitlab-triage.gemspec
CHANGED
|
@@ -33,13 +33,10 @@ Gem::Specification.new do |spec|
|
|
|
33
33
|
|
|
34
34
|
spec.add_dependency 'activesupport', '>= 5.1'
|
|
35
35
|
spec.add_dependency 'globalid', '~> 1.0', '>= 1.0.1'
|
|
36
|
-
spec.add_dependency 'graphql-client', '~> 0.
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
|
|
40
|
-
# see: https://github.com/github/graphql-client/pull/314
|
|
41
|
-
# We can remove this check once PR 314 is merged and released
|
|
42
|
-
spec.add_dependency 'graphql', '< 2.1.0'
|
|
36
|
+
spec.add_dependency 'graphql-client', '~> 0.23'
|
|
37
|
+
# graphql >= 2.3.5 needs graphql-client >= 0.23 (possible_types keys changed).
|
|
38
|
+
# 2.4.13 is the patched version on the highest affected minor line.
|
|
39
|
+
spec.add_dependency 'graphql', '>= 2.4.13', '< 2.7.0'
|
|
43
40
|
spec.add_dependency 'httparty', '>= 0.21.0', '< 1.0'
|
|
44
41
|
|
|
45
42
|
spec.add_development_dependency 'gitlab-dangerfiles', '~> 2.11.0'
|
|
@@ -63,7 +63,7 @@ module Gitlab
|
|
|
63
63
|
|
|
64
64
|
def build_post_url(resource, issue)
|
|
65
65
|
# POST /projects/:id/issues
|
|
66
|
-
# https://docs.gitlab.com/
|
|
66
|
+
# https://docs.gitlab.com/api/issues/#create-an-issue
|
|
67
67
|
post_url = UrlBuilders::UrlBuilder.new(
|
|
68
68
|
network_options: network.options,
|
|
69
69
|
source_id: issue.destination || resource[policy.source_id_sym],
|
|
@@ -79,7 +79,7 @@ module Gitlab
|
|
|
79
79
|
|
|
80
80
|
def post_issue_url
|
|
81
81
|
# POST /projects/:id/issues
|
|
82
|
-
# https://docs.gitlab.com/
|
|
82
|
+
# https://docs.gitlab.com/api/issues/#create-an-issue
|
|
83
83
|
post_url = UrlBuilders::UrlBuilder.new(
|
|
84
84
|
network_options: network.options,
|
|
85
85
|
source_id: destination,
|
|
@@ -11,8 +11,9 @@ module Gitlab
|
|
|
11
11
|
# Applies work_items actions through the Work Items REST API update
|
|
12
12
|
# endpoint (PATCH) rather than the issues note/quick-action path.
|
|
13
13
|
#
|
|
14
|
-
# - labels
|
|
15
|
-
# -
|
|
14
|
+
# - labels -> features.labels.add_label_ids (resolved from names)
|
|
15
|
+
# - remove_labels -> features.labels.remove_label_ids (resolved from names)
|
|
16
|
+
# - assignees -> features.assignees.assignee_ids (resolved from usernames)
|
|
16
17
|
#
|
|
17
18
|
# Missing labels or usernames stop the automation immediately (matching
|
|
18
19
|
# the labels action behaviour on the issues type) rather than silently
|
|
@@ -36,13 +37,14 @@ module Gitlab
|
|
|
36
37
|
def perform(resource)
|
|
37
38
|
puts "# #{resource[:web_url]}"
|
|
38
39
|
puts "Would add labels: #{requested_labels.join(', ')}" if requested_labels.any?
|
|
40
|
+
puts "Would remove labels: #{requested_removed_labels.join(', ')}" if requested_removed_labels.any?
|
|
39
41
|
puts "Would assign: #{requested_assignees.join(', ')}" if requested_assignees.any?
|
|
40
42
|
puts "\n"
|
|
41
43
|
end
|
|
42
44
|
end
|
|
43
45
|
|
|
44
46
|
def act
|
|
45
|
-
return unless requested_labels.any? || requested_assignees.any?
|
|
47
|
+
return unless requested_labels.any? || requested_removed_labels.any? || requested_assignees.any?
|
|
46
48
|
|
|
47
49
|
policy.resources.each do |resource|
|
|
48
50
|
perform(resource)
|
|
@@ -63,7 +65,11 @@ module Gitlab
|
|
|
63
65
|
def build_features(resource)
|
|
64
66
|
features = {}
|
|
65
67
|
|
|
66
|
-
|
|
68
|
+
label_changes = {}
|
|
69
|
+
label_changes[:add_label_ids] = label_ids_to_add(resource) if requested_labels.any?
|
|
70
|
+
label_changes[:remove_label_ids] = label_ids_to_remove(resource) if requested_removed_labels.any?
|
|
71
|
+
features[:labels] = label_changes if label_changes.any?
|
|
72
|
+
|
|
67
73
|
features[:assignees] = { assignee_ids: assignee_ids } if requested_assignees.any?
|
|
68
74
|
|
|
69
75
|
features
|
|
@@ -73,6 +79,10 @@ module Gitlab
|
|
|
73
79
|
@requested_labels ||= Array(policy.actions[:labels])
|
|
74
80
|
end
|
|
75
81
|
|
|
82
|
+
def requested_removed_labels
|
|
83
|
+
@requested_removed_labels ||= Array(policy.actions[:remove_labels])
|
|
84
|
+
end
|
|
85
|
+
|
|
76
86
|
def requested_assignees
|
|
77
87
|
@requested_assignees ||= Array(policy.actions[:assignees])
|
|
78
88
|
end
|
|
@@ -82,14 +92,16 @@ module Gitlab
|
|
|
82
92
|
network: network, options: network.options)
|
|
83
93
|
end
|
|
84
94
|
|
|
85
|
-
# Label ids are namespace-specific, so resolve
|
|
86
|
-
#
|
|
87
|
-
#
|
|
95
|
+
# Label ids are namespace-specific, so resolve against the resource's
|
|
96
|
+
# own namespace. The fetched label list is cached per scope, so a rule
|
|
97
|
+
# that both adds and removes labels - or a group-spanning run - makes
|
|
98
|
+
# one lookup per distinct project/group.
|
|
88
99
|
def label_ids_to_add(resource)
|
|
89
|
-
|
|
100
|
+
resolve_label_ids(requested_labels, source.scope_for(resource))
|
|
101
|
+
end
|
|
90
102
|
|
|
91
|
-
|
|
92
|
-
|
|
103
|
+
def label_ids_to_remove(resource)
|
|
104
|
+
resolve_label_ids(requested_removed_labels, source.scope_for(resource))
|
|
93
105
|
end
|
|
94
106
|
|
|
95
107
|
def assignee_ids
|
|
@@ -102,7 +114,7 @@ module Gitlab
|
|
|
102
114
|
def resolve_label_ids(names, scope)
|
|
103
115
|
return [] if names.empty?
|
|
104
116
|
|
|
105
|
-
by_name =
|
|
117
|
+
by_name = labels_by_name(scope)
|
|
106
118
|
|
|
107
119
|
names.map do |name|
|
|
108
120
|
label = by_name[name]
|
|
@@ -112,24 +124,41 @@ module Gitlab
|
|
|
112
124
|
end
|
|
113
125
|
end
|
|
114
126
|
|
|
127
|
+
def labels_by_name(scope)
|
|
128
|
+
@labels_by_name_by_scope ||= {}
|
|
129
|
+
@labels_by_name_by_scope[scope] ||= network.query_api(labels_url(scope)).index_by { |label| label[:name] }
|
|
130
|
+
end
|
|
131
|
+
|
|
115
132
|
# The Work Items REST update endpoint can return success without
|
|
116
133
|
# persisting a change when the token lacks permission. The response
|
|
117
134
|
# echoes the resulting widgets, so confirm the requested ids landed and
|
|
118
135
|
# raise otherwise rather than reporting a silent no-op as success.
|
|
119
136
|
def verify_applied!(resource, response)
|
|
120
137
|
verify_labels_applied!(resource, response) if requested_labels.any?
|
|
138
|
+
verify_labels_removed!(resource, response) if requested_removed_labels.any?
|
|
121
139
|
verify_assignees_applied!(resource, response) if requested_assignees.any?
|
|
122
140
|
end
|
|
123
141
|
|
|
124
142
|
def verify_labels_applied!(resource, response)
|
|
125
|
-
|
|
126
|
-
missing = label_ids_to_add(resource) - applied_ids
|
|
143
|
+
missing = label_ids_to_add(resource) - applied_label_ids(response)
|
|
127
144
|
return if missing.empty?
|
|
128
145
|
|
|
129
146
|
raise UpdateNotAppliedError,
|
|
130
147
|
"Labels were not applied to #{resource[:web_url]}; the token may lack permission."
|
|
131
148
|
end
|
|
132
149
|
|
|
150
|
+
def verify_labels_removed!(resource, response)
|
|
151
|
+
still_present = label_ids_to_remove(resource) & applied_label_ids(response)
|
|
152
|
+
return if still_present.empty?
|
|
153
|
+
|
|
154
|
+
raise UpdateNotAppliedError,
|
|
155
|
+
"Labels were not removed from #{resource[:web_url]}; the token may lack permission."
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def applied_label_ids(response)
|
|
159
|
+
[*response.dig(:features, :labels, :labels)].pluck(:id)
|
|
160
|
+
end
|
|
161
|
+
|
|
133
162
|
def verify_assignees_applied!(resource, response)
|
|
134
163
|
applied_ids = [*response.dig(:features, :assignees)].pluck(:id)
|
|
135
164
|
missing = assignee_ids - applied_ids
|
data/lib/gitlab/triage/engine.rb
CHANGED
|
@@ -730,7 +730,7 @@ module Gitlab
|
|
|
730
730
|
end
|
|
731
731
|
|
|
732
732
|
def draft_condition_builder(draft_condittion)
|
|
733
|
-
# Issues API only accepts 'yes' and 'no' as strings: https://docs.gitlab.com/
|
|
733
|
+
# Issues API only accepts 'yes' and 'no' as strings: https://docs.gitlab.com/api/merge_requests/
|
|
734
734
|
wip =
|
|
735
735
|
case draft_condittion
|
|
736
736
|
when true
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'active_support/core_ext/object/blank'
|
|
4
|
+
|
|
3
5
|
require_relative '../version'
|
|
4
6
|
|
|
5
7
|
module Gitlab
|
|
@@ -14,6 +16,21 @@ module Gitlab
|
|
|
14
16
|
@options = options
|
|
15
17
|
@tracker = tracker
|
|
16
18
|
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
# When the rate-limit headers are absent (e.g. a server that doesn't send
|
|
23
|
+
# them), treat the limit as not-a-concern rather than coercing a missing
|
|
24
|
+
# header to 0/1970, which would otherwise trip rate_limit_wait.
|
|
25
|
+
def rate_limit_remaining(headers)
|
|
26
|
+
value = headers['ratelimit-remaining']
|
|
27
|
+
value.present? ? value.to_i : Float::INFINITY
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def rate_limit_reset_at(headers)
|
|
31
|
+
value = headers['ratelimit-reset']
|
|
32
|
+
Time.at(value.to_i) if value.present?
|
|
33
|
+
end
|
|
17
34
|
end
|
|
18
35
|
end
|
|
19
36
|
end
|
|
@@ -58,14 +58,9 @@ module Gitlab
|
|
|
58
58
|
|
|
59
59
|
raise_on_error!(response)
|
|
60
60
|
|
|
61
|
-
parsed_response = response.data
|
|
62
61
|
headers = response.extensions.fetch('headers', {})
|
|
63
62
|
|
|
64
|
-
|
|
65
|
-
ratelimit_remaining: headers['ratelimit-remaining'].to_i,
|
|
66
|
-
ratelimit_reset_at: Time.at(headers['ratelimit-reset'].to_i),
|
|
67
|
-
results: parsed_response.to_h
|
|
68
|
-
}
|
|
63
|
+
build_graphql_response(response.data.to_h, headers)
|
|
69
64
|
end
|
|
70
65
|
|
|
71
66
|
delegate :parse, to: :client
|
|
@@ -96,19 +91,6 @@ module Gitlab
|
|
|
96
91
|
end
|
|
97
92
|
end
|
|
98
93
|
|
|
99
|
-
# When the rate-limit headers are absent (e.g. a server that doesn't send
|
|
100
|
-
# them), treat the limit as not-a-concern rather than coercing a missing
|
|
101
|
-
# header to 0/1970, which would otherwise trip rate_limit_wait.
|
|
102
|
-
def rate_limit_remaining(headers)
|
|
103
|
-
value = headers['ratelimit-remaining']
|
|
104
|
-
value.present? ? value.to_i : Float::INFINITY
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
def rate_limit_reset_at(headers)
|
|
108
|
-
value = headers['ratelimit-reset']
|
|
109
|
-
Time.at(value.to_i) if value.present?
|
|
110
|
-
end
|
|
111
|
-
|
|
112
94
|
# Converts a parsed GraphQL::Client response node into plain Ruby so the
|
|
113
95
|
# shared builder only ever deals with Hash/Array, never client objects.
|
|
114
96
|
def plain_node(parsed_response)
|
|
@@ -28,8 +28,8 @@ module Gitlab
|
|
|
28
28
|
more_pages: (response.headers["x-next-page"].to_s != ""),
|
|
29
29
|
next_page_url: next_page_url(url, response),
|
|
30
30
|
results: response.parsed_response,
|
|
31
|
-
ratelimit_remaining: response.headers
|
|
32
|
-
ratelimit_reset_at:
|
|
31
|
+
ratelimit_remaining: rate_limit_remaining(response.headers),
|
|
32
|
+
ratelimit_reset_at: rate_limit_reset_at(response.headers)
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
resource_count = result[:results].is_a?(Array) ? result[:results].size : 0
|
|
@@ -57,8 +57,8 @@ module Gitlab
|
|
|
57
57
|
|
|
58
58
|
{
|
|
59
59
|
results: response.parsed_response,
|
|
60
|
-
ratelimit_remaining: response.headers
|
|
61
|
-
ratelimit_reset_at:
|
|
60
|
+
ratelimit_remaining: rate_limit_remaining(response.headers),
|
|
61
|
+
ratelimit_reset_at: rate_limit_reset_at(response.headers)
|
|
62
62
|
}
|
|
63
63
|
end
|
|
64
64
|
|
|
@@ -81,8 +81,8 @@ module Gitlab
|
|
|
81
81
|
|
|
82
82
|
{
|
|
83
83
|
results: response.parsed_response,
|
|
84
|
-
ratelimit_remaining: response.headers
|
|
85
|
-
ratelimit_reset_at:
|
|
84
|
+
ratelimit_remaining: rate_limit_remaining(response.headers),
|
|
85
|
+
ratelimit_reset_at: rate_limit_reset_at(response.headers)
|
|
86
86
|
}
|
|
87
87
|
end
|
|
88
88
|
|
|
@@ -105,8 +105,8 @@ module Gitlab
|
|
|
105
105
|
|
|
106
106
|
{
|
|
107
107
|
results: response.parsed_response,
|
|
108
|
-
ratelimit_remaining: response.headers
|
|
109
|
-
ratelimit_reset_at:
|
|
108
|
+
ratelimit_remaining: rate_limit_remaining(response.headers),
|
|
109
|
+
ratelimit_reset_at: rate_limit_reset_at(response.headers)
|
|
110
110
|
}
|
|
111
111
|
end
|
|
112
112
|
|
|
@@ -127,8 +127,8 @@ module Gitlab
|
|
|
127
127
|
|
|
128
128
|
{
|
|
129
129
|
results: response.parsed_response,
|
|
130
|
-
ratelimit_remaining: response.headers
|
|
131
|
-
ratelimit_reset_at:
|
|
130
|
+
ratelimit_remaining: rate_limit_remaining(response.headers),
|
|
131
|
+
ratelimit_reset_at: rate_limit_reset_at(response.headers)
|
|
132
132
|
}
|
|
133
133
|
end
|
|
134
134
|
|
|
@@ -74,10 +74,10 @@ module Gitlab
|
|
|
74
74
|
end
|
|
75
75
|
|
|
76
76
|
opts.on('--cache-dir [string]', String,
|
|
77
|
-
'Directory containing pre-fetched API responses.
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
77
|
+
'Directory containing pre-fetched API responses.',
|
|
78
|
+
'Files must follow the naming convention: <source>_<id>_<resource>.json',
|
|
79
|
+
'(e.g. groups_9970_members.json).',
|
|
80
|
+
'Can also be set via the GITLAB_TRIAGE_CACHE_DIR environment variable.') do |value|
|
|
81
81
|
options.cache_dir = value
|
|
82
82
|
end
|
|
83
83
|
|
|
@@ -210,9 +210,12 @@ module Gitlab
|
|
|
210
210
|
def rate_limit_wait(response)
|
|
211
211
|
return unless response.delete(:ratelimit_remaining) < MINIMUM_RATE_LIMIT
|
|
212
212
|
|
|
213
|
-
|
|
213
|
+
reset_at = response[:ratelimit_reset_at]
|
|
214
|
+
return unless reset_at
|
|
215
|
+
|
|
216
|
+
puts Gitlab::Triage::UI.debug "Rate limit almost exceeded, sleeping for #{reset_at - Time.now} seconds" if options.debug
|
|
214
217
|
@adapter.tracker&.record_rate_limit_wait
|
|
215
|
-
sleep(1) until Time.now >=
|
|
218
|
+
sleep(1) until Time.now >= reset_at
|
|
216
219
|
end
|
|
217
220
|
|
|
218
221
|
def raise_unexpected_response(results)
|
|
@@ -10,6 +10,7 @@ module Gitlab
|
|
|
10
10
|
# where nobody sees it).
|
|
11
11
|
class PolicyValidator
|
|
12
12
|
UnsupportedActionError = Class.new(StandardError)
|
|
13
|
+
ConflictingActionError = Class.new(StandardError)
|
|
13
14
|
|
|
14
15
|
COMMENT_NOT_SUPPORTED_MESSAGE =
|
|
15
16
|
'Commenting on work_items is not yet supported by the Work Items REST API ' \
|
|
@@ -18,7 +19,7 @@ module Gitlab
|
|
|
18
19
|
|
|
19
20
|
# Action keys Action.actions_for dispatches for work_items policies.
|
|
20
21
|
# Anything outside this list would silently do nothing, so reject it up front.
|
|
21
|
-
SUPPORTED_WORK_ITEM_ACTIONS = %i[labels assignees summarize comment_on_summary].freeze
|
|
22
|
+
SUPPORTED_WORK_ITEM_ACTIONS = %i[labels remove_labels assignees summarize comment_on_summary].freeze
|
|
22
23
|
|
|
23
24
|
def initialize(resource_rules)
|
|
24
25
|
@resource_rules = resource_rules || {}
|
|
@@ -38,6 +39,16 @@ module Gitlab
|
|
|
38
39
|
action_sets_from(policy_definition).each do |actions|
|
|
39
40
|
raise UnsupportedActionError, COMMENT_NOT_SUPPORTED_MESSAGE if actions.key?(:comment)
|
|
40
41
|
|
|
42
|
+
# GitLab applies remove_label_ids after add_label_ids, so a label in
|
|
43
|
+
# both lists is always removed. Catch the typo here rather than let the
|
|
44
|
+
# run fail later with a misleading "token may lack permission" error.
|
|
45
|
+
conflicting = Array(actions[:labels]).map(&:to_s) & Array(actions[:remove_labels]).map(&:to_s)
|
|
46
|
+
unless conflicting.none?
|
|
47
|
+
raise ConflictingActionError,
|
|
48
|
+
"Label(s) in both labels and remove_labels for a work_items rule: #{conflicting.join(', ')}. " \
|
|
49
|
+
"A label cannot be added and removed in the same rule."
|
|
50
|
+
end
|
|
51
|
+
|
|
41
52
|
unsupported = actions.keys.map(&:to_sym) - SUPPORTED_WORK_ITEM_ACTIONS
|
|
42
53
|
next if unsupported.none?
|
|
43
54
|
|
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.57.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- GitLab
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-10-
|
|
11
|
+
date: 2026-10-19 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activesupport
|
|
@@ -50,28 +50,34 @@ dependencies:
|
|
|
50
50
|
requirements:
|
|
51
51
|
- - "~>"
|
|
52
52
|
- !ruby/object:Gem::Version
|
|
53
|
-
version: '0.
|
|
53
|
+
version: '0.23'
|
|
54
54
|
type: :runtime
|
|
55
55
|
prerelease: false
|
|
56
56
|
version_requirements: !ruby/object:Gem::Requirement
|
|
57
57
|
requirements:
|
|
58
58
|
- - "~>"
|
|
59
59
|
- !ruby/object:Gem::Version
|
|
60
|
-
version: '0.
|
|
60
|
+
version: '0.23'
|
|
61
61
|
- !ruby/object:Gem::Dependency
|
|
62
62
|
name: graphql
|
|
63
63
|
requirement: !ruby/object:Gem::Requirement
|
|
64
64
|
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: 2.4.13
|
|
65
68
|
- - "<"
|
|
66
69
|
- !ruby/object:Gem::Version
|
|
67
|
-
version: 2.
|
|
70
|
+
version: 2.7.0
|
|
68
71
|
type: :runtime
|
|
69
72
|
prerelease: false
|
|
70
73
|
version_requirements: !ruby/object:Gem::Requirement
|
|
71
74
|
requirements:
|
|
75
|
+
- - ">="
|
|
76
|
+
- !ruby/object:Gem::Version
|
|
77
|
+
version: 2.4.13
|
|
72
78
|
- - "<"
|
|
73
79
|
- !ruby/object:Gem::Version
|
|
74
|
-
version: 2.
|
|
80
|
+
version: 2.7.0
|
|
75
81
|
- !ruby/object:Gem::Dependency
|
|
76
82
|
name: httparty
|
|
77
83
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -212,7 +218,6 @@ executables:
|
|
|
212
218
|
extensions: []
|
|
213
219
|
extra_rdoc_files: []
|
|
214
220
|
files:
|
|
215
|
-
- ".codeclimate.yml"
|
|
216
221
|
- ".git-blame-ignore-revs"
|
|
217
222
|
- ".gitignore"
|
|
218
223
|
- ".gitlab-ci.yml"
|
data/.codeclimate.yml
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
engines:
|
|
3
|
-
bundler-audit:
|
|
4
|
-
enabled: true
|
|
5
|
-
duplication:
|
|
6
|
-
enabled: true
|
|
7
|
-
config:
|
|
8
|
-
languages:
|
|
9
|
-
- ruby
|
|
10
|
-
rubocop-gitlab:
|
|
11
|
-
enabled: true
|
|
12
|
-
ratings:
|
|
13
|
-
paths:
|
|
14
|
-
- Gemfile.lock
|
|
15
|
-
- "**.rb"
|
|
16
|
-
exclude_paths:
|
|
17
|
-
- spec/
|
|
18
|
-
- vendor/
|
|
19
|
-
- tmp/
|