dri 0.8.0 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitlab/merge_request_templates/Release.md +28 -0
- data/.gitlab-ci.yml +1 -1
- data/.ruby-version +1 -1
- data/.tool-versions +1 -1
- data/Gemfile.lock +50 -38
- data/README.md +23 -0
- data/dri.gemspec +7 -5
- data/faq.yaml +21 -0
- data/lib/dri/api_client.rb +41 -7
- data/lib/dri/cli.rb +17 -0
- data/lib/dri/command.rb +4 -0
- data/lib/dri/commands/faq.rb +33 -0
- data/lib/dri/commands/fetch/failures.rb +35 -21
- data/lib/dri/commands/fetch/pipelines.rb +0 -3
- data/lib/dri/commands/fetch/runbooks.rb +62 -0
- data/lib/dri/commands/fetch/testcases.rb +0 -1
- data/lib/dri/commands/fetch.rb +19 -1
- data/lib/dri/commands/profile.rb +8 -3
- data/lib/dri/report.rb +0 -1
- data/lib/dri/utils/constants.rb +2 -7
- data/lib/dri/version.rb +1 -1
- metadata +44 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b5791162eb6afd6c20228dc94f5c247233a1f9703743fb671df15f0cef3258c2
|
4
|
+
data.tar.gz: 2629b72f72f18669b362b7f904bce44a4abc63b8fa421a4dbb515c1dd17ee8cb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6ffc4003b8ea2cc342c88a3f457f3ea9701f9dfe0e7ca0ff7a5f54e2ec1a8ccc896dea9e16bb4ea1655d3e9d1eadadaa0ef2d01652ffb4740fb94fcc01690397
|
7
|
+
data.tar.gz: f302fd36aa8cc2385e6c83e6de915481b3515097be2d5e7137ea63af3ef70e7462ad2a4f64a575777cd734b1320699b01c4efd99fd60941d59b0b8b65a4b005a
|
@@ -0,0 +1,28 @@
|
|
1
|
+
## Diff
|
2
|
+
<!-- Replace `v0.9.0` with the previous DRI release, and `55ddcace1c166fa2323d4e556262eba6b274230a` with the latest commit from https://gitlab.com/gitlab-org/quality/dri/-/commits/master that will be included in the release -->
|
3
|
+
https://gitlab.com/gitlab-org/quality/dri/-/compare/v0.9.0...55ddcace1c166fa2323d4e556262eba6b274230a
|
4
|
+
|
5
|
+
## Release Notes
|
6
|
+
|
7
|
+
```markdown
|
8
|
+
### Features
|
9
|
+
- !aaa <Title of the aaa MR>
|
10
|
+
|
11
|
+
### Bug Fixes
|
12
|
+
- !bbb <Title of the bbb MR>
|
13
|
+
|
14
|
+
### Maintenance
|
15
|
+
- !ccc <Title of the ccc MR>
|
16
|
+
```
|
17
|
+
|
18
|
+
## Checklists
|
19
|
+
|
20
|
+
### Before merging:
|
21
|
+
- [ ] MR title is set to `Bump version to <version>`, where `<version>` is the new version, according to [SemVer](https://semver.org/).
|
22
|
+
- [ ] Diff link is up-to-date.
|
23
|
+
- [ ] Release notes are accurate.
|
24
|
+
- [ ] `lib/dri/version.rb` is updated with the new version number.
|
25
|
+
- [ ] `bundle update` was run to update `Gemfile.lock` with the new version number.
|
26
|
+
|
27
|
+
### After merging:
|
28
|
+
- [ ] Update the release notes for the [newly created tag](https://gitlab.com/gitlab-org/quality/dri/-/tags) with the release notes from this MR.
|
data/.gitlab-ci.yml
CHANGED
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
3.0.
|
1
|
+
3.0.5
|
data/.tool-versions
CHANGED
@@ -1 +1 @@
|
|
1
|
-
ruby 3.0.
|
1
|
+
ruby 3.0.5
|
data/Gemfile.lock
CHANGED
@@ -1,19 +1,21 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
dri (0.
|
4
|
+
dri (0.10.0)
|
5
5
|
amatch (~> 0.4.1)
|
6
|
-
gitlab (~> 4.
|
7
|
-
httparty (~> 0.
|
6
|
+
gitlab (~> 4.19)
|
7
|
+
httparty (~> 0.21.0)
|
8
8
|
json (~> 2.6.1)
|
9
|
+
launchy (~> 2.4)
|
9
10
|
markdown-tables (~> 1.1.1)
|
10
11
|
pastel (~> 0.8.0)
|
11
|
-
thor (~> 1.0
|
12
|
+
thor (~> 1.2.0)
|
12
13
|
tty-box (~> 0.7.0)
|
13
|
-
tty-config (~> 0.
|
14
|
-
tty-editor (~> 0.
|
14
|
+
tty-config (~> 0.6.0)
|
15
|
+
tty-editor (~> 0.7)
|
15
16
|
tty-font (~> 0.5)
|
16
17
|
tty-logger (~> 0.6.0)
|
18
|
+
tty-markdown (~> 0.7.0)
|
17
19
|
tty-prompt (~> 0.23.1)
|
18
20
|
tty-spinner (~> 0.9)
|
19
21
|
tty-table (~> 0.12.0)
|
@@ -21,19 +23,19 @@ PATH
|
|
21
23
|
GEM
|
22
24
|
remote: https://rubygems.org/
|
23
25
|
specs:
|
24
|
-
activesupport (7.0.
|
26
|
+
activesupport (7.0.5)
|
25
27
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
26
28
|
i18n (>= 1.6, < 2)
|
27
29
|
minitest (>= 5.1)
|
28
30
|
tzinfo (~> 2.0)
|
29
|
-
addressable (2.8.
|
30
|
-
public_suffix (>= 2.0.2, <
|
31
|
+
addressable (2.8.4)
|
32
|
+
public_suffix (>= 2.0.2, < 6.0)
|
31
33
|
amatch (0.4.1)
|
32
34
|
mize
|
33
35
|
tins (~> 1.0)
|
34
36
|
ast (2.4.2)
|
35
37
|
coderay (1.1.3)
|
36
|
-
concurrent-ruby (1.
|
38
|
+
concurrent-ruby (1.2.2)
|
37
39
|
crack (0.4.5)
|
38
40
|
rexml
|
39
41
|
diff-lcs (1.5.0)
|
@@ -48,37 +50,40 @@ GEM
|
|
48
50
|
rubocop-rails (~> 2.9)
|
49
51
|
rubocop-rspec (~> 1.44)
|
50
52
|
hashdiff (1.0.1)
|
51
|
-
httparty (0.
|
52
|
-
|
53
|
+
httparty (0.21.0)
|
54
|
+
mini_mime (>= 1.0.0)
|
53
55
|
multi_xml (>= 0.5.2)
|
54
|
-
i18n (1.
|
56
|
+
i18n (1.13.0)
|
55
57
|
concurrent-ruby (~> 1.0)
|
56
|
-
json (2.6.
|
58
|
+
json (2.6.3)
|
59
|
+
kramdown (2.4.0)
|
60
|
+
rexml
|
61
|
+
launchy (2.5.2)
|
62
|
+
addressable (~> 2.8)
|
57
63
|
markdown-tables (1.1.1)
|
58
64
|
method_source (1.0.0)
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
minitest (5.15.0)
|
63
|
-
mize (0.4.0)
|
65
|
+
mini_mime (1.1.2)
|
66
|
+
minitest (5.18.0)
|
67
|
+
mize (0.4.1)
|
64
68
|
protocol (~> 2.0)
|
65
69
|
multi_xml (0.6.0)
|
66
|
-
parallel (1.
|
67
|
-
parser (3.
|
70
|
+
parallel (1.23.0)
|
71
|
+
parser (3.2.2.1)
|
68
72
|
ast (~> 2.4.1)
|
69
73
|
pastel (0.8.0)
|
70
74
|
tty-color (~> 0.5)
|
71
75
|
protocol (2.0.0)
|
72
76
|
ruby_parser (~> 3.0)
|
73
|
-
pry (0.14.
|
77
|
+
pry (0.14.2)
|
74
78
|
coderay (~> 1.1)
|
75
79
|
method_source (~> 1.0)
|
76
|
-
public_suffix (
|
77
|
-
rack (
|
80
|
+
public_suffix (5.0.1)
|
81
|
+
rack (3.0.7)
|
78
82
|
rainbow (3.1.1)
|
79
83
|
rake (13.0.6)
|
80
|
-
regexp_parser (2.
|
84
|
+
regexp_parser (2.8.0)
|
81
85
|
rexml (3.2.5)
|
86
|
+
rouge (4.1.1)
|
82
87
|
rspec (3.10.0)
|
83
88
|
rspec-core (~> 3.10.0)
|
84
89
|
rspec-expectations (~> 3.10.0)
|
@@ -101,11 +106,11 @@ GEM
|
|
101
106
|
rubocop-ast (>= 0.6.0)
|
102
107
|
ruby-progressbar (~> 1.7)
|
103
108
|
unicode-display_width (>= 1.4.0, < 2.0)
|
104
|
-
rubocop-ast (1.
|
105
|
-
parser (>= 3.
|
109
|
+
rubocop-ast (1.28.1)
|
110
|
+
parser (>= 3.2.1.0)
|
106
111
|
rubocop-gitlab-security (0.1.1)
|
107
112
|
rubocop (>= 0.51)
|
108
|
-
rubocop-graphql (0.
|
113
|
+
rubocop-graphql (0.19.0)
|
109
114
|
rubocop (>= 0.87, < 2)
|
110
115
|
rubocop-performance (1.9.2)
|
111
116
|
rubocop (>= 0.90.0, < 2.0)
|
@@ -117,10 +122,10 @@ GEM
|
|
117
122
|
rubocop-rspec (1.44.1)
|
118
123
|
rubocop (~> 0.87)
|
119
124
|
rubocop-ast (>= 0.7.1)
|
120
|
-
ruby-progressbar (1.
|
121
|
-
ruby_parser (3.
|
125
|
+
ruby-progressbar (1.13.0)
|
126
|
+
ruby_parser (3.20.1)
|
122
127
|
sexp_processor (~> 4.16)
|
123
|
-
sexp_processor (4.
|
128
|
+
sexp_processor (4.17.0)
|
124
129
|
strings (0.2.1)
|
125
130
|
strings-ansi (~> 0.2)
|
126
131
|
unicode-display_width (>= 1.5, < 3.0)
|
@@ -129,22 +134,29 @@ GEM
|
|
129
134
|
sync (0.5.0)
|
130
135
|
terminal-table (3.0.2)
|
131
136
|
unicode-display_width (>= 1.1.1, < 3)
|
132
|
-
thor (1.
|
133
|
-
timecop (0.9.
|
134
|
-
tins (1.
|
137
|
+
thor (1.2.2)
|
138
|
+
timecop (0.9.6)
|
139
|
+
tins (1.32.1)
|
135
140
|
sync
|
136
141
|
tty-box (0.7.0)
|
137
142
|
pastel (~> 0.8)
|
138
143
|
strings (~> 0.2.0)
|
139
144
|
tty-cursor (~> 0.7)
|
140
145
|
tty-color (0.6.0)
|
141
|
-
tty-config (0.
|
146
|
+
tty-config (0.6.0)
|
142
147
|
tty-cursor (0.7.1)
|
143
148
|
tty-editor (0.7.0)
|
144
149
|
tty-prompt (~> 0.22)
|
145
150
|
tty-font (0.5.0)
|
146
151
|
tty-logger (0.6.0)
|
147
152
|
pastel (~> 0.8)
|
153
|
+
tty-markdown (0.7.2)
|
154
|
+
kramdown (>= 1.16.2, < 3.0)
|
155
|
+
pastel (~> 0.8)
|
156
|
+
rouge (>= 3.14, < 5.0)
|
157
|
+
strings (~> 0.2.0)
|
158
|
+
tty-color (~> 0.5)
|
159
|
+
tty-screen (~> 0.8)
|
148
160
|
tty-prompt (0.23.1)
|
149
161
|
pastel (~> 0.8)
|
150
162
|
tty-reader (~> 0.8)
|
@@ -159,11 +171,11 @@ GEM
|
|
159
171
|
pastel (~> 0.8)
|
160
172
|
strings (~> 0.2.0)
|
161
173
|
tty-screen (~> 0.8)
|
162
|
-
tzinfo (2.0.
|
174
|
+
tzinfo (2.0.6)
|
163
175
|
concurrent-ruby (~> 1.0)
|
164
176
|
unicode-display_width (1.8.0)
|
165
177
|
unicode_utils (1.4.0)
|
166
|
-
webmock (3.
|
178
|
+
webmock (3.18.1)
|
167
179
|
addressable (>= 2.8.0)
|
168
180
|
crack (>= 0.3.2)
|
169
181
|
hashdiff (>= 0.4.0, < 2.0.0)
|
@@ -182,4 +194,4 @@ DEPENDENCIES
|
|
182
194
|
webmock (~> 3.5)
|
183
195
|
|
184
196
|
BUNDLED WITH
|
185
|
-
2.
|
197
|
+
2.4.13
|
data/README.md
CHANGED
@@ -59,6 +59,7 @@ $ dri profile
|
|
59
59
|
- quarantines
|
60
60
|
- dequarantines
|
61
61
|
- featureflags
|
62
|
+
- runbooks
|
62
63
|
- [4. publish](#4-publish)
|
63
64
|
- report
|
64
65
|
- [5. rm](#5-rm)
|
@@ -69,6 +70,7 @@ $ dri profile
|
|
69
70
|
- [7. analyze](#7-analyze)
|
70
71
|
- stacktraces
|
71
72
|
- [8. version](#8-version)
|
73
|
+
- [9. faq](#8-faq)
|
72
74
|
|
73
75
|
#### 1. init
|
74
76
|
|
@@ -96,6 +98,11 @@ $ dri fetch failures
|
|
96
98
|
Fetches today's opened failures and lists them according to their triage status.
|
97
99
|
Helpful to understand if there are missing recent failures to be reviewed.
|
98
100
|
|
101
|
+
There is the possibility to customise the timeframe for such failures by passing a
|
102
|
+
`--start_date=DD-MM-YYYY` and `--end_date=DD-MM-YYYY`. It is also possible to pass
|
103
|
+
a `--cutoff=HH:MM` to just show failures after a certain period of the day. The cutoff
|
104
|
+
time will be converted to UTC.
|
105
|
+
|
99
106
|
To surface most urgent issues pass the `--urgent` flag to see issues that are both
|
100
107
|
in `canary` and `staging-canary` pipelines just during today's timespan.
|
101
108
|
|
@@ -139,6 +146,13 @@ $ dri fetch pipelines
|
|
139
146
|
Fetches a table containing last executed pipeline and its test report link for all monitored pipelines.
|
140
147
|
The timestamps are in UTC
|
141
148
|
|
149
|
+
```shell
|
150
|
+
$ dri fetch runbooks [runbook]
|
151
|
+
```
|
152
|
+
|
153
|
+
Fetches a runbook from the [runbooks](https://gitlab.com/gitlab-org/quality/runbooks) project.
|
154
|
+
If `[runbook]` is omitted, the command will prompt the user to select from a list of available runbooks.
|
155
|
+
|
142
156
|
#### 4. publish
|
143
157
|
|
144
158
|
```shell
|
@@ -220,6 +234,15 @@ $ dri version
|
|
220
234
|
|
221
235
|
`dri` gem version.
|
222
236
|
|
237
|
+
#### 9. faq
|
238
|
+
|
239
|
+
```shell
|
240
|
+
$ dri faq
|
241
|
+
```
|
242
|
+
|
243
|
+
Returns a list of questions and answers related to on-call operations. This leverages
|
244
|
+
existing pages of documentation, redirecting directly to the relevant section to help
|
245
|
+
with a problem.
|
223
246
|
## Copyright
|
224
247
|
|
225
248
|
Copyright (c) 2022 GitLab, Inc. See [MIT License](LICENSE.txt) for further details.
|
data/dri.gemspec
CHANGED
@@ -23,17 +23,19 @@ Gem::Specification.new do |spec|
|
|
23
23
|
spec.require_paths = ['lib']
|
24
24
|
|
25
25
|
spec.add_dependency 'amatch', '~> 0.4.1'
|
26
|
-
spec.add_dependency "gitlab", "~> 4.
|
27
|
-
spec.add_dependency 'httparty', '~> 0.
|
26
|
+
spec.add_dependency "gitlab", "~> 4.19"
|
27
|
+
spec.add_dependency 'httparty', '~> 0.21.0'
|
28
28
|
spec.add_dependency 'json', '~> 2.6.1'
|
29
|
+
spec.add_dependency 'launchy', '~> 2.4'
|
29
30
|
spec.add_dependency 'markdown-tables', '~> 1.1.1'
|
30
31
|
spec.add_dependency 'pastel', '~> 0.8.0'
|
31
|
-
spec.add_dependency 'thor', '~> 1.0
|
32
|
+
spec.add_dependency 'thor', '~> 1.2.0'
|
32
33
|
spec.add_dependency 'tty-box', '~> 0.7.0'
|
33
|
-
spec.add_dependency 'tty-config', '~> 0.
|
34
|
-
spec.add_dependency 'tty-editor', '~> 0.
|
34
|
+
spec.add_dependency 'tty-config', '~> 0.6.0'
|
35
|
+
spec.add_dependency 'tty-editor', '~> 0.7'
|
35
36
|
spec.add_dependency 'tty-font', '~> 0.5'
|
36
37
|
spec.add_dependency 'tty-logger', '~> 0.6.0'
|
38
|
+
spec.add_dependency 'tty-markdown', '~> 0.7.0'
|
37
39
|
spec.add_dependency 'tty-prompt', '~> 0.23.1'
|
38
40
|
spec.add_dependency 'tty-spinner', '~> 0.9'
|
39
41
|
spec.add_dependency 'tty-table', '~> 0.12.0'
|
data/faq.yaml
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
---
|
2
|
+
- question: "Which pipelines we currently have and what is their running cadence?"
|
3
|
+
link: "https://about.gitlab.com/handbook/engineering/quality/quality-engineering/debugging-qa-test-failures/#qa-test-pipelines"
|
4
|
+
- question: "Where can I find the rotation schedule?"
|
5
|
+
link: "https://gitlab.com/gitlab-org/quality/pipeline-triage#dri-weekly-rotation-schedule"
|
6
|
+
- question: "What is the process to quarantine a test?"
|
7
|
+
link: "https://about.gitlab.com/handbook/engineering/quality/quality-engineering/debugging-qa-test-failures/#quarantining-tests"
|
8
|
+
- question: "What is the process to dequarantine a test?"
|
9
|
+
link: "https://about.gitlab.com/handbook/engineering/quality/quality-engineering/debugging-qa-test-failures/#dequarantining-tests"
|
10
|
+
- question: "What are the failure classification labels?"
|
11
|
+
link: "https://about.gitlab.com/handbook/engineering/quality/quality-engineering/debugging-qa-test-failures/#classify-and-triage-the-test-failure"
|
12
|
+
- question: "I have a correlation ID. How to find logs from various GitLab components?"
|
13
|
+
link: "https://about.gitlab.com/handbook/engineering/quality/quality-engineering/debugging-qa-test-failures/#kibana-correlation-dashboards"
|
14
|
+
- question: "Where do I find the MR that introduced a feature flag?"
|
15
|
+
link: "https://samdbeckham.gitlab.io/feature-flags/#%5B%7B%22type%22:%22filtered-search-term%22,%22value%22:%7B%22data%22:%22%22%7D%7D%5D"
|
16
|
+
- question: "What to do when a failure needs escalation?"
|
17
|
+
link: "https://about.gitlab.com/handbook/engineering/quality/quality-engineering/debugging-qa-test-failures/#failure-needs-escalation"
|
18
|
+
- question: "I have an MR to fix the failure. How I make sure it is merged as soon as possible?"
|
19
|
+
link: "https://about.gitlab.com/handbook/engineering/quality/quality-engineering/debugging-qa-test-failures/#fixing-the-test"
|
20
|
+
|
21
|
+
|
data/lib/dri/api_client.rb
CHANGED
@@ -160,15 +160,24 @@ module Dri
|
|
160
160
|
|
161
161
|
# Fetch all new failures
|
162
162
|
#
|
163
|
-
# @param [String]
|
163
|
+
# @param [String] start_date
|
164
|
+
# @param [String] end_date
|
164
165
|
# @param [String] state
|
165
166
|
# @return [Array<Gitlab::ObjectifiedHash>]
|
166
|
-
def fetch_all_new_failures(
|
167
|
+
def fetch_all_new_failures(start_date:, end_date:, state:)
|
167
168
|
project_ids = [GITLAB_PROJECT_ID, CUSTOMERSDOT_PROJECT_ID]
|
168
169
|
failures = []
|
169
170
|
|
171
|
+
start_date_iso = start_date.strftime('%Y-%m-%dT00:00:00Z')
|
172
|
+
end_date_iso = end_date.strftime('%Y-%m-%dT23:59:59Z')
|
173
|
+
|
170
174
|
project_ids.each do |project_id|
|
171
|
-
failures += fetch_new_failures(
|
175
|
+
failures += fetch_new_failures(
|
176
|
+
project_id: project_id,
|
177
|
+
start_date: start_date_iso,
|
178
|
+
end_date: end_date_iso,
|
179
|
+
state: state
|
180
|
+
)
|
172
181
|
end
|
173
182
|
|
174
183
|
failures
|
@@ -177,17 +186,20 @@ module Dri
|
|
177
186
|
# Fetch new failures for a given project
|
178
187
|
#
|
179
188
|
# @param [Integer] project_id
|
180
|
-
# @param [String]
|
189
|
+
# @param [String] start_date
|
190
|
+
# @param [String] end_date
|
181
191
|
# @param [String] state
|
182
192
|
# @return [Array<Gitlab::ObjectifiedHash>]
|
183
|
-
def fetch_new_failures(project_id:,
|
193
|
+
def fetch_new_failures(project_id:, start_date:, end_date:, state:)
|
184
194
|
gitlab.issues(
|
185
195
|
project_id,
|
186
196
|
labels: FAILURE_NEW,
|
187
|
-
order_by: "
|
197
|
+
order_by: "created_at",
|
198
|
+
sort: 'desc',
|
188
199
|
state: state,
|
189
200
|
scope: "all",
|
190
|
-
created_after:
|
201
|
+
created_after: start_date,
|
202
|
+
created_before: end_date,
|
191
203
|
per_page: 100
|
192
204
|
)
|
193
205
|
end
|
@@ -279,6 +291,28 @@ module Dri
|
|
279
291
|
gitlab.pipeline_jobs(project_id, pipeline_id, options).auto_paginate
|
280
292
|
end
|
281
293
|
|
294
|
+
# Fetch runbooks from the runbooks project
|
295
|
+
#
|
296
|
+
# @param [Integer] project_id
|
297
|
+
# @return [Array<Gitlab::ObjectifedHash>]
|
298
|
+
def list_runbooks(project_id = RUNBOOKS_PROJECT_ID)
|
299
|
+
tree = gitlab.tree(project_id, { recursive: true, ref: 'main' }).auto_paginate
|
300
|
+
|
301
|
+
tree.select do |node|
|
302
|
+
node.type == 'tree' && !node.name.start_with?('_')
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
# Fetches file contents at <path>
|
307
|
+
#
|
308
|
+
# @param [String] path
|
309
|
+
# @param [String] ref
|
310
|
+
# @param [Integer] project_id
|
311
|
+
# @return [Gitlab::ObjectifiedHash]
|
312
|
+
def get_file(path, ref:, project_id:)
|
313
|
+
gitlab.get_file(project_id, path, ref)
|
314
|
+
end
|
315
|
+
|
282
316
|
private
|
283
317
|
|
284
318
|
attr_reader :token, :ops_token
|
data/lib/dri/cli.rb
CHANGED
@@ -77,6 +77,23 @@ module Dri
|
|
77
77
|
end
|
78
78
|
end
|
79
79
|
|
80
|
+
desc 'faq', 'Consult frequently ask questions while on-call'
|
81
|
+
method_option :help, aliases: '-h', type: :boolean,
|
82
|
+
desc: 'Display usage information'
|
83
|
+
|
84
|
+
def faq(*)
|
85
|
+
if options[:help]
|
86
|
+
invoke :help, ['faq']
|
87
|
+
else
|
88
|
+
begin
|
89
|
+
require_relative 'commands/faq'
|
90
|
+
Dri::Commands::FAQ.new(options).execute
|
91
|
+
rescue Dri::Commands::FAQ::ExitCommand
|
92
|
+
puts "Exiting faq command..."
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
80
97
|
require_relative 'commands/fetch'
|
81
98
|
register Dri::Commands::Fetch, 'fetch', 'fetch [SUBCOMMAND]', 'Fetch failures & testcases'
|
82
99
|
|
data/lib/dri/command.rb
CHANGED
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../command'
|
4
|
+
require 'tty-prompt'
|
5
|
+
require 'launchy'
|
6
|
+
|
7
|
+
module Dri
|
8
|
+
module Commands
|
9
|
+
class FAQ < Dri::Command
|
10
|
+
ExitCommand = Class.new(StandardError)
|
11
|
+
|
12
|
+
def execute(input: $stdin, output: $stdout)
|
13
|
+
root_dir = File.expand_path('../../..', __dir__)
|
14
|
+
faq_file = File.join(root_dir, 'faq.yaml')
|
15
|
+
faq_data = YAML.load_file(faq_file)
|
16
|
+
|
17
|
+
prompt = TTY::Prompt.new
|
18
|
+
|
19
|
+
choices = faq_data.map { |entry| { name: entry['question'], value: entry['link'] } }
|
20
|
+
|
21
|
+
url = prompt.select("Select a question:", choices)
|
22
|
+
raise ExitCommand if url.nil? || url.strip.empty?
|
23
|
+
|
24
|
+
begin
|
25
|
+
Launchy.open(url)
|
26
|
+
rescue Launchy::Error => e
|
27
|
+
puts "Failed to open URL: #{e.message}"
|
28
|
+
raise ExitCommand
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -12,17 +12,11 @@ module Dri
|
|
12
12
|
include Dri::Utils::Constants::Triage::Labels
|
13
13
|
using Refinements
|
14
14
|
|
15
|
-
SORT_BY_OPTIONS = {
|
16
|
-
title: 0,
|
17
|
-
triaged: 1,
|
18
|
-
environment: 2,
|
19
|
-
author: 3,
|
20
|
-
url: 4
|
21
|
-
}.freeze
|
22
|
-
|
23
15
|
def initialize(options)
|
24
16
|
@options = options
|
25
|
-
@
|
17
|
+
@start_date = @options[:start_date] ? Date.parse(@options[:start_date]) : Date.today
|
18
|
+
@end_date = @options[:end_date] ? Date.parse(@options[:end_date]) : Date.today
|
19
|
+
@cutoff_time = @options[:cutoff] ? Time.parse(options[:cutoff]).utc : nil
|
26
20
|
end
|
27
21
|
|
28
22
|
def execute(input: $stdin, output: $stdout) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength
|
@@ -35,26 +29,40 @@ module Dri
|
|
35
29
|
environment = add_color('Environment', :bright_yellow)
|
36
30
|
author = add_color('Author', :bright_yellow)
|
37
31
|
url = add_color('URL', :bright_yellow)
|
32
|
+
updated_at = add_color('Updated at', :bright_yellow)
|
38
33
|
|
39
34
|
sorted_failures = []
|
40
|
-
labels =
|
35
|
+
labels = { title: title, triaged: triaged, environment: environment, author: author, url: url,
|
36
|
+
updated_at: updated_at }
|
41
37
|
triaged_counter = 0
|
42
38
|
|
43
|
-
|
39
|
+
if @cutoff_time
|
40
|
+
@start_date = Time.new(
|
41
|
+
@start_date.year,
|
42
|
+
@start_date.month,
|
43
|
+
@start_date.day,
|
44
|
+
@cutoff_time.hour,
|
45
|
+
@cutoff_time.min, 0,
|
46
|
+
"+00:00"
|
47
|
+
)
|
48
|
+
end
|
49
|
+
|
50
|
+
logger.info "Fetching failures from #{@start_date} (UTC) to #{@end_date} (UTC)..."
|
44
51
|
|
45
52
|
spinner.run do # rubocop:disable Metrics/BlockLength
|
46
|
-
failures = api_client.fetch_all_new_failures(
|
53
|
+
failures = api_client.fetch_all_new_failures(start_date: @start_date, end_date: @end_date, state: 'opened')
|
47
54
|
|
48
55
|
if failures.empty?
|
49
|
-
logger.info
|
56
|
+
logger.info "Life is great, there are no new failures between #{@start_date} and #{@end_date}!"
|
50
57
|
exit 0
|
51
58
|
end
|
52
59
|
|
53
60
|
failures.each do |failure|
|
54
61
|
project_id = failure.project_id
|
55
62
|
title = failure.title.truncate(60)
|
56
|
-
author = failure.to_h.dig('author', 'username')
|
63
|
+
author = failure.to_h.dig('author', 'username').truncate(25)
|
57
64
|
url = failure.web_url
|
65
|
+
updated_at = failure.updated_at
|
58
66
|
triaged = add_color('x', :red)
|
59
67
|
envs = failure.labels.select { |l| l.include?(FOUND) }.map do |l|
|
60
68
|
env = l.split(':').last.gsub('.gitlab.com', '')
|
@@ -63,6 +71,8 @@ module Dri
|
|
63
71
|
end
|
64
72
|
urgent = urgent_environments.all? { |env| envs.include?(env) }
|
65
73
|
|
74
|
+
next if @options[:urgent] && !urgent
|
75
|
+
|
66
76
|
emoji_awards = api_client.fetch_awarded_emojis(failure.iid, project_id: project_id).find do |e|
|
67
77
|
e.name == emoji && e.to_h.dig('user', 'username') == username
|
68
78
|
end
|
@@ -72,14 +82,11 @@ module Dri
|
|
72
82
|
triaged_counter += 1
|
73
83
|
end
|
74
84
|
|
75
|
-
|
76
|
-
|
77
|
-
else
|
78
|
-
sorted_failures << [title, triaged, envs.first, author, url]
|
79
|
-
end
|
85
|
+
sorted_failures << { title: title, triaged: triaged, environment: envs.first, author: author, url: url,
|
86
|
+
updated_at: updated_at }
|
80
87
|
end
|
81
88
|
|
82
|
-
sorted_failures.sort_by! { |failure| failure[
|
89
|
+
sorted_failures.sort_by! { |failure| failure[@options[:sort_by]&.to_sym || :environment] }
|
83
90
|
end
|
84
91
|
|
85
92
|
msg = if @options[:urgent]
|
@@ -92,7 +99,14 @@ module Dri
|
|
92
99
|
MSG
|
93
100
|
end
|
94
101
|
|
95
|
-
|
102
|
+
terminal_width = TTY::Screen.width
|
103
|
+
|
104
|
+
if terminal_width <= 210 # adjust the desired minimum width
|
105
|
+
labels.delete(:updated_at)
|
106
|
+
sorted_failures.map { |failure| failure.delete(:updated_at) }
|
107
|
+
end
|
108
|
+
|
109
|
+
print_table(labels.values, sorted_failures.map(&:values), alignments: [:left, :center, :center, :left, :left])
|
96
110
|
output.puts(msg)
|
97
111
|
end
|
98
112
|
end
|
@@ -120,7 +120,6 @@ module Dri
|
|
120
120
|
tests_exceed_threshold?(project_id: project_id, pipeline_id: pipeline_id, ops: ops)
|
121
121
|
else
|
122
122
|
contains_stage?(jobs, "qa") || contains_stage?(jobs, "test")
|
123
|
-
# Nightly pipeline does not execute full E2E suite if sanity fails so can't check tests count
|
124
123
|
end
|
125
124
|
end
|
126
125
|
|
@@ -161,8 +160,6 @@ module Dri
|
|
161
160
|
case pipeline_name
|
162
161
|
when "master"
|
163
162
|
"e2e-package-and-test"
|
164
|
-
when "nightly"
|
165
|
-
pipeline_name
|
166
163
|
when "pre_prod"
|
167
164
|
"preprod-#{run_type(sanity)}"
|
168
165
|
else
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../../command'
|
4
|
+
require_relative '../../utils/constants'
|
5
|
+
require 'tty-markdown'
|
6
|
+
require 'tty-prompt'
|
7
|
+
require 'base64'
|
8
|
+
|
9
|
+
module Dri
|
10
|
+
module Commands
|
11
|
+
class Fetch
|
12
|
+
class Runbooks < Dri::Command
|
13
|
+
def initialize(options)
|
14
|
+
@options = options
|
15
|
+
end
|
16
|
+
|
17
|
+
def execute(folder: nil, input: $stdin, output: $stdout)
|
18
|
+
return fetch_summary(output: output) unless folder
|
19
|
+
|
20
|
+
fetch_runbook("#{folder}/index.md", output: output)
|
21
|
+
end
|
22
|
+
|
23
|
+
def fetch_summary(output:)
|
24
|
+
logger.info 'Fetching runbooks'
|
25
|
+
prompt = TTY::Prompt.new
|
26
|
+
runbooks = api_client.list_runbooks
|
27
|
+
|
28
|
+
return output.puts 'No runbooks could be found' if runbooks.empty?
|
29
|
+
|
30
|
+
choices = runbooks.map(&:path)
|
31
|
+
path = prompt.select('Which runbook would you like to look at?', choices)
|
32
|
+
|
33
|
+
fetch_runbook("#{path}/index.md", output: output)
|
34
|
+
end
|
35
|
+
|
36
|
+
def fetch_runbook(path, output:)
|
37
|
+
runbook = nil
|
38
|
+
spinner.run do
|
39
|
+
runbook = api_client.get_file(
|
40
|
+
path,
|
41
|
+
ref: 'main',
|
42
|
+
project_id: Utils::Constants::ProjectIDs::RUNBOOKS_PROJECT_ID
|
43
|
+
)
|
44
|
+
rescue Gitlab::Error::NotFound
|
45
|
+
logger.error "The runbook #{path} could not be found."
|
46
|
+
end
|
47
|
+
|
48
|
+
return unless runbook
|
49
|
+
|
50
|
+
content = Base64.decode64(runbook.content)
|
51
|
+
markdown(content, output)
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def markdown(str, io)
|
57
|
+
io.puts TTY::Markdown.parse(str, symbols: :ascii)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/lib/dri/commands/fetch.rb
CHANGED
@@ -45,13 +45,20 @@ module Dri
|
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
|
-
desc 'failures', 'Display failures opened
|
48
|
+
desc 'failures', 'Display failures opened on a given period.'
|
49
49
|
method_option :help, aliases: '-h', type: :boolean,
|
50
50
|
desc: 'Display usage information'
|
51
51
|
method_option :urgent, type: :boolean,
|
52
52
|
desc: 'Shows failures that quickly escalated'
|
53
53
|
method_option :sort_by, type: :string,
|
54
54
|
desc: 'Shows failures in specified order'
|
55
|
+
method_option :start_date, type: :string,
|
56
|
+
desc: 'Fetch failures created after this date (YYYY-MM-DD)'
|
57
|
+
method_option :end_date, type: :string,
|
58
|
+
desc: 'Fetch failures created before this date (YYYY-MM-DD)'
|
59
|
+
method_option :cutoff, type: :string,
|
60
|
+
desc: 'Fetch failures created after this time (HH:MM)'
|
61
|
+
|
55
62
|
def failures(*)
|
56
63
|
if options[:help]
|
57
64
|
invoke :help, ['failures']
|
@@ -94,6 +101,17 @@ module Dri
|
|
94
101
|
require_relative 'fetch/pipelines'
|
95
102
|
Dri::Commands::Fetch::Pipelines.new(options).execute
|
96
103
|
end
|
104
|
+
|
105
|
+
desc 'runbooks', 'Fetch runbooks'
|
106
|
+
method_option :help, aliases: '-h', type: :boolean,
|
107
|
+
desc: 'Display usage information'
|
108
|
+
|
109
|
+
def runbooks(*args)
|
110
|
+
return invoke :help, %w[runbooks] if options[:help]
|
111
|
+
|
112
|
+
require_relative 'fetch/runbooks'
|
113
|
+
Dri::Commands::Fetch::Runbooks.new(options).execute(folder: args.first)
|
114
|
+
end
|
97
115
|
end
|
98
116
|
end
|
99
117
|
end
|
data/lib/dri/commands/profile.rb
CHANGED
@@ -20,10 +20,12 @@ module Dri
|
|
20
20
|
logger.info "🔎 Looking for profiles...\n"
|
21
21
|
|
22
22
|
if config.exist?
|
23
|
+
width = handover_report_path.nil? ? 30 : 75
|
24
|
+
|
23
25
|
frame_args = {
|
24
|
-
width:
|
26
|
+
width: width,
|
25
27
|
height: 10,
|
26
|
-
align: :
|
28
|
+
align: :left,
|
27
29
|
padding: 1,
|
28
30
|
border: :thick,
|
29
31
|
title: { top_left: add_color('PROFILE:', :bright_cyan) }
|
@@ -37,10 +39,13 @@ module Dri
|
|
37
39
|
|
38
40
|
def pretty_print_profile
|
39
41
|
<<~PROFILE
|
40
|
-
#{add_color('User:', :bright_cyan)} #{username}
|
42
|
+
#{add_color('User:', :bright_cyan)} #{username}
|
43
|
+
#{add_color('Token:', :bright_cyan)} #{token}
|
41
44
|
#{add_color('OpsToken:', :bright_cyan)} #{ops_token}
|
42
45
|
#{add_color('Timezone:', :bright_cyan)} #{timezone}
|
43
46
|
#{add_color('Emoji:', :bright_cyan)} #{emoji}
|
47
|
+
#{add_color('Report Path:', :bright_cyan)} #{handover_report_path}
|
48
|
+
|
44
49
|
PROFILE
|
45
50
|
end
|
46
51
|
end
|
data/lib/dri/report.rb
CHANGED
@@ -83,7 +83,6 @@ module Dri
|
|
83
83
|
'canary.staging.gitlab.com' => '/quality/staging-canary',
|
84
84
|
'main' => '/gitlab-org/gitlab-qa-mirror',
|
85
85
|
'master' => '/gitlab-org/gitlab-qa-mirror',
|
86
|
-
'nightly' => '/quality/nightly',
|
87
86
|
'pre.gitlab.com' => '/quality/preprod',
|
88
87
|
'staging-ref' => '/quality/staging-ref',
|
89
88
|
'staging.gitlab.com' => '/quality/staging',
|
data/lib/dri/utils/constants.rb
CHANGED
@@ -10,6 +10,7 @@ module Dri
|
|
10
10
|
CUSTOMERSDOT_PROJECT_ID = 2670515
|
11
11
|
FEATURE_FLAG_LOG_PROJECT_ID = 15208716
|
12
12
|
INFRA_TEAM_PROD_PROJECT_ID = 7444821
|
13
|
+
RUNBOOKS_PROJECT_ID = 41045213
|
13
14
|
end
|
14
15
|
|
15
16
|
module Triage
|
@@ -62,12 +63,6 @@ module Dri
|
|
62
63
|
project_id: "547",
|
63
64
|
search_hours_ago: 12
|
64
65
|
},
|
65
|
-
nightly: {
|
66
|
-
name: "nightly",
|
67
|
-
url: "https://gitlab.com/gitlab-org/quality/nightly",
|
68
|
-
project_id: "7523614",
|
69
|
-
search_hours_ago: 24
|
70
|
-
},
|
71
66
|
pre_prod: {
|
72
67
|
name: "pre.gitlab.com",
|
73
68
|
url: "https://ops.gitlab.net/gitlab-org/quality/preprod",
|
@@ -84,7 +79,7 @@ module Dri
|
|
84
79
|
name: "master",
|
85
80
|
url: "https://gitlab.com/gitlab-org/gitlab",
|
86
81
|
project_id: "278964",
|
87
|
-
search_hours_ago:
|
82
|
+
search_hours_ago: 24
|
88
83
|
}
|
89
84
|
}.freeze
|
90
85
|
end
|
data/lib/dri/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dri
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.10.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- GitLab Quality
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-05-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: amatch
|
@@ -30,28 +30,28 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '4.
|
33
|
+
version: '4.19'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '4.
|
40
|
+
version: '4.19'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: httparty
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: 0.
|
47
|
+
version: 0.21.0
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: 0.
|
54
|
+
version: 0.21.0
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: json
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -66,6 +66,20 @@ dependencies:
|
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: 2.6.1
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: launchy
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '2.4'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '2.4'
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
84
|
name: markdown-tables
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -100,14 +114,14 @@ dependencies:
|
|
100
114
|
requirements:
|
101
115
|
- - "~>"
|
102
116
|
- !ruby/object:Gem::Version
|
103
|
-
version: 1.0
|
117
|
+
version: 1.2.0
|
104
118
|
type: :runtime
|
105
119
|
prerelease: false
|
106
120
|
version_requirements: !ruby/object:Gem::Requirement
|
107
121
|
requirements:
|
108
122
|
- - "~>"
|
109
123
|
- !ruby/object:Gem::Version
|
110
|
-
version: 1.0
|
124
|
+
version: 1.2.0
|
111
125
|
- !ruby/object:Gem::Dependency
|
112
126
|
name: tty-box
|
113
127
|
requirement: !ruby/object:Gem::Requirement
|
@@ -128,28 +142,28 @@ dependencies:
|
|
128
142
|
requirements:
|
129
143
|
- - "~>"
|
130
144
|
- !ruby/object:Gem::Version
|
131
|
-
version: 0.
|
145
|
+
version: 0.6.0
|
132
146
|
type: :runtime
|
133
147
|
prerelease: false
|
134
148
|
version_requirements: !ruby/object:Gem::Requirement
|
135
149
|
requirements:
|
136
150
|
- - "~>"
|
137
151
|
- !ruby/object:Gem::Version
|
138
|
-
version: 0.
|
152
|
+
version: 0.6.0
|
139
153
|
- !ruby/object:Gem::Dependency
|
140
154
|
name: tty-editor
|
141
155
|
requirement: !ruby/object:Gem::Requirement
|
142
156
|
requirements:
|
143
157
|
- - "~>"
|
144
158
|
- !ruby/object:Gem::Version
|
145
|
-
version: '0.
|
159
|
+
version: '0.7'
|
146
160
|
type: :runtime
|
147
161
|
prerelease: false
|
148
162
|
version_requirements: !ruby/object:Gem::Requirement
|
149
163
|
requirements:
|
150
164
|
- - "~>"
|
151
165
|
- !ruby/object:Gem::Version
|
152
|
-
version: '0.
|
166
|
+
version: '0.7'
|
153
167
|
- !ruby/object:Gem::Dependency
|
154
168
|
name: tty-font
|
155
169
|
requirement: !ruby/object:Gem::Requirement
|
@@ -178,6 +192,20 @@ dependencies:
|
|
178
192
|
- - "~>"
|
179
193
|
- !ruby/object:Gem::Version
|
180
194
|
version: 0.6.0
|
195
|
+
- !ruby/object:Gem::Dependency
|
196
|
+
name: tty-markdown
|
197
|
+
requirement: !ruby/object:Gem::Requirement
|
198
|
+
requirements:
|
199
|
+
- - "~>"
|
200
|
+
- !ruby/object:Gem::Version
|
201
|
+
version: 0.7.0
|
202
|
+
type: :runtime
|
203
|
+
prerelease: false
|
204
|
+
version_requirements: !ruby/object:Gem::Requirement
|
205
|
+
requirements:
|
206
|
+
- - "~>"
|
207
|
+
- !ruby/object:Gem::Version
|
208
|
+
version: 0.7.0
|
181
209
|
- !ruby/object:Gem::Dependency
|
182
210
|
name: tty-prompt
|
183
211
|
requirement: !ruby/object:Gem::Requirement
|
@@ -315,6 +343,7 @@ files:
|
|
315
343
|
- ".editorconfig"
|
316
344
|
- ".gitignore"
|
317
345
|
- ".gitlab-ci.yml"
|
346
|
+
- ".gitlab/merge_request_templates/Release.md"
|
318
347
|
- ".rspec"
|
319
348
|
- ".rubocop.yml"
|
320
349
|
- ".ruby-version"
|
@@ -328,17 +357,20 @@ files:
|
|
328
357
|
- bin/setup
|
329
358
|
- dri.gemspec
|
330
359
|
- exe/dri
|
360
|
+
- faq.yaml
|
331
361
|
- lib/dri.rb
|
332
362
|
- lib/dri/api_client.rb
|
333
363
|
- lib/dri/cli.rb
|
334
364
|
- lib/dri/command.rb
|
335
365
|
- lib/dri/commands/analyze.rb
|
336
366
|
- lib/dri/commands/analyze/stack_traces.rb
|
367
|
+
- lib/dri/commands/faq.rb
|
337
368
|
- lib/dri/commands/fetch.rb
|
338
369
|
- lib/dri/commands/fetch/failures.rb
|
339
370
|
- lib/dri/commands/fetch/featureflags.rb
|
340
371
|
- lib/dri/commands/fetch/pipelines.rb
|
341
372
|
- lib/dri/commands/fetch/quarantines.rb
|
373
|
+
- lib/dri/commands/fetch/runbooks.rb
|
342
374
|
- lib/dri/commands/fetch/testcases.rb
|
343
375
|
- lib/dri/commands/fetch/triaged.rb
|
344
376
|
- lib/dri/commands/incidents.rb
|