dri 0.1.0 → 0.1.3
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/.gitignore +3 -1
- data/.gitlab-ci.yml +25 -13
- data/.tool-versions +1 -0
- data/Gemfile +0 -3
- data/Gemfile.lock +15 -15
- data/README.md +42 -2
- data/dri.gemspec +2 -2
- data/lib/dri/api_client.rb +23 -4
- data/lib/dri/command.rb +2 -0
- data/lib/dri/commands/fetch/failures.rb +36 -20
- data/lib/dri/commands/fetch/quarantines.rb +46 -0
- data/lib/dri/commands/fetch/testcases.rb +4 -0
- data/lib/dri/commands/fetch/triaged.rb +55 -0
- data/lib/dri/commands/fetch.rb +26 -0
- data/lib/dri/commands/init.rb +3 -2
- data/lib/dri/commands/publish/report.rb +9 -2
- data/lib/dri/commands/publish.rb +2 -0
- data/lib/dri/commands/rm/profile.rb +33 -0
- data/lib/dri/commands/rm/reports.rb +20 -0
- data/lib/dri/commands/rm.rb +24 -0
- data/lib/dri/refinements/truncate.rb +15 -0
- data/lib/dri/report.rb +65 -18
- data/lib/dri/version.rb +1 -1
- metadata +13 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c1c589c770547e0142cb5b2942316e41af19cad293ed2e141d95e54a7379f741
|
|
4
|
+
data.tar.gz: b1cc881757273745c40485c469b34abb642a8edcda18ae1baf22bfe80ed8bacd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0fa73e3af14a31bcf96f919c1b37465695a52a7db5d0fcaedf8ee36e51c817bf4c789c3fe24bc2169de0b385b83d0ec92ae716f8da6fbb55cb134c6219a0b26a
|
|
7
|
+
data.tar.gz: 880ae4a81c350dcfe7b49c9db00c3760d7f98b56383b7516d0c890dae24034fc3d8ebb86f37592c8d65655c3cbe8671bddf3c26327d49a6006b0e9f5f0313afa
|
data/.gitignore
CHANGED
data/.gitlab-ci.yml
CHANGED
|
@@ -1,28 +1,40 @@
|
|
|
1
|
-
|
|
1
|
+
.job_base:
|
|
2
|
+
image: ruby:3.1
|
|
3
|
+
variables:
|
|
4
|
+
BUNDLE_PATH: vendor/bundle
|
|
5
|
+
BUNDLE_SUPPRESS_INSTALL_USING_MESSAGES: "true"
|
|
6
|
+
before_script:
|
|
7
|
+
- bundle install
|
|
8
|
+
cache:
|
|
9
|
+
key:
|
|
10
|
+
files:
|
|
11
|
+
- dri.gemspec
|
|
12
|
+
- Gemfile.lock
|
|
13
|
+
paths:
|
|
14
|
+
- vendor/bundle
|
|
15
|
+
rules:
|
|
16
|
+
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
|
|
17
|
+
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
|
18
|
+
|
|
19
|
+
include:
|
|
20
|
+
- project: gitlab-org/quality/pipeline-common
|
|
21
|
+
ref: 0.3.4
|
|
22
|
+
file:
|
|
23
|
+
- /ci/gem-release.yml
|
|
2
24
|
|
|
3
25
|
stages:
|
|
4
26
|
- build
|
|
5
27
|
- test
|
|
6
28
|
- deploy
|
|
7
29
|
|
|
8
|
-
before_script:
|
|
9
|
-
- gem install bundler:2.2.22
|
|
10
|
-
- bundle install
|
|
11
|
-
|
|
12
30
|
build_gem:
|
|
13
31
|
stage: build
|
|
32
|
+
extends: .job_base
|
|
14
33
|
script:
|
|
15
34
|
- gem build
|
|
16
35
|
|
|
17
36
|
rspec:
|
|
18
37
|
stage: test
|
|
38
|
+
extends: .job_base
|
|
19
39
|
script:
|
|
20
40
|
- bundle exec rspec
|
|
21
|
-
|
|
22
|
-
deploy:
|
|
23
|
-
stage: deploy
|
|
24
|
-
script:
|
|
25
|
-
- gem push dri*.gem
|
|
26
|
-
rules:
|
|
27
|
-
- if: '$CI_COMMIT_TAG'
|
|
28
|
-
when: always
|
data/.tool-versions
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ruby 3.0.2
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
dri (0.1.
|
|
4
|
+
dri (0.1.2)
|
|
5
5
|
httparty (~> 0.20.0)
|
|
6
6
|
json (~> 2.6.1)
|
|
7
7
|
markdown-tables (~> 1.1.1)
|
|
@@ -39,19 +39,19 @@ GEM
|
|
|
39
39
|
public_suffix (4.0.6)
|
|
40
40
|
rake (12.3.3)
|
|
41
41
|
rexml (3.2.5)
|
|
42
|
-
rspec (3.
|
|
43
|
-
rspec-core (~> 3.
|
|
44
|
-
rspec-expectations (~> 3.
|
|
45
|
-
rspec-mocks (~> 3.
|
|
46
|
-
rspec-core (3.
|
|
47
|
-
rspec-support (~> 3.
|
|
48
|
-
rspec-expectations (3.
|
|
42
|
+
rspec (3.10.0)
|
|
43
|
+
rspec-core (~> 3.10.0)
|
|
44
|
+
rspec-expectations (~> 3.10.0)
|
|
45
|
+
rspec-mocks (~> 3.10.0)
|
|
46
|
+
rspec-core (3.10.1)
|
|
47
|
+
rspec-support (~> 3.10.0)
|
|
48
|
+
rspec-expectations (3.10.1)
|
|
49
49
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
50
|
-
rspec-support (~> 3.
|
|
51
|
-
rspec-mocks (3.
|
|
50
|
+
rspec-support (~> 3.10.0)
|
|
51
|
+
rspec-mocks (3.10.2)
|
|
52
52
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
53
|
-
rspec-support (~> 3.
|
|
54
|
-
rspec-support (3.
|
|
53
|
+
rspec-support (~> 3.10.0)
|
|
54
|
+
rspec-support (3.10.2)
|
|
55
55
|
strings (0.2.1)
|
|
56
56
|
strings-ansi (~> 0.2)
|
|
57
57
|
unicode-display_width (>= 1.5, < 3.0)
|
|
@@ -98,10 +98,10 @@ PLATFORMS
|
|
|
98
98
|
|
|
99
99
|
DEPENDENCIES
|
|
100
100
|
dri!
|
|
101
|
-
rake
|
|
102
|
-
rspec (~> 3.0)
|
|
101
|
+
rake
|
|
102
|
+
rspec (~> 3.10.0)
|
|
103
103
|
timecop (~> 0.9.1)
|
|
104
104
|
webmock (~> 3.5)
|
|
105
105
|
|
|
106
106
|
BUNDLED WITH
|
|
107
|
-
2.
|
|
107
|
+
2.3.9
|
data/README.md
CHANGED
|
@@ -7,9 +7,9 @@ Welcome to your new gem! DRI is a CLI ready to help:
|
|
|
7
7
|
- Fetching failing test cases by pipeline
|
|
8
8
|
- Reporting at the end of the timezone shift
|
|
9
9
|
- View today's newest and untriaged failures
|
|
10
|
-
-
|
|
10
|
+
- Mark actions on failures
|
|
11
11
|
|
|
12
|
-
... and more
|
|
12
|
+
... and more
|
|
13
13
|
|
|
14
14
|
## Installation
|
|
15
15
|
|
|
@@ -55,10 +55,13 @@ $ dri profile
|
|
|
55
55
|
- [3. fetch](#3-fetch)
|
|
56
56
|
- failures
|
|
57
57
|
- testcases
|
|
58
|
+
- triaged
|
|
58
59
|
- [4. publish](#4-publish)
|
|
59
60
|
- report
|
|
60
61
|
- [5. rm](#5-rm)
|
|
61
62
|
- emoji
|
|
63
|
+
- profile
|
|
64
|
+
- reports
|
|
62
65
|
- [6. version](#6-version)
|
|
63
66
|
|
|
64
67
|
#### 1. init
|
|
@@ -87,6 +90,9 @@ $ dri fetch failures
|
|
|
87
90
|
Fetches today's opened failures and lists them according to their triage status.
|
|
88
91
|
Helpful to understand if there are missing recent failures to be reviewed.
|
|
89
92
|
|
|
93
|
+
To surface most urgent issues pass the `--urgent` flag to see issues that are both
|
|
94
|
+
in `canary` and `staging-canary` pipelines just during today's timespan.
|
|
95
|
+
|
|
90
96
|
```shell
|
|
91
97
|
$ dri fetch testcases
|
|
92
98
|
```
|
|
@@ -95,6 +101,12 @@ Fetches test cases that failing currently and groups them by pipeline.
|
|
|
95
101
|
To filter the pipelines, pass the `--filter-pipelines` options to multi-select the pipelines you wish to consult.
|
|
96
102
|
(Use `Space` to select an option)
|
|
97
103
|
|
|
104
|
+
```shell
|
|
105
|
+
$ dri fetch triaged
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Fetches triaged failures which use the triage emoji specified in `dri profile`.
|
|
109
|
+
|
|
98
110
|
#### 4. publish
|
|
99
111
|
|
|
100
112
|
```shell
|
|
@@ -109,8 +121,22 @@ Publishes a handover report on the latest triage issue, in the [pipeline-triage]
|
|
|
109
121
|
$ dri publish report --format=list # formats the report in a list
|
|
110
122
|
$ dri publish report --format=table # formats the report in a table (default)
|
|
111
123
|
$ dri publish report --dry-run # the report is only generated locally
|
|
124
|
+
$ dri publish report --actions # activate the actions prompt for each failure
|
|
112
125
|
```
|
|
113
126
|
|
|
127
|
+
**Note:** These options above can be combined like:
|
|
128
|
+
|
|
129
|
+
```shell
|
|
130
|
+
$ dri publish report --format=list --dry-run --actions
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
**Actions**
|
|
134
|
+
|
|
135
|
+
When using `--actions` a set of actions are prompt on each failure. Use `Space` to
|
|
136
|
+
mark several actions: `pinged SET`, `quarantined`, `reproduced`, `transient`.
|
|
137
|
+
Hitting `Enter` without a selected action will skip marking the actions for a given
|
|
138
|
+
failure.
|
|
139
|
+
|
|
114
140
|
#### 5. rm
|
|
115
141
|
|
|
116
142
|
```shell
|
|
@@ -119,6 +145,20 @@ $ dri rm emoji
|
|
|
119
145
|
|
|
120
146
|
Removes the triage emoji from all triaged issues.
|
|
121
147
|
|
|
148
|
+
```shell
|
|
149
|
+
$ dri rm reports
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
When using `$ dri publish report --dry-run` to download the reports locally instead
|
|
153
|
+
of uploading to the latest Pipeline Triage Issue, an `/handover_reports/` folder
|
|
154
|
+
created to store these reports. This command removes this folder and subsequent reports stored in it.
|
|
155
|
+
|
|
156
|
+
```shell
|
|
157
|
+
$ dri rm profile
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Removes the profile currently in use.
|
|
161
|
+
|
|
122
162
|
#### 6. version
|
|
123
163
|
|
|
124
164
|
```shell
|
data/dri.gemspec
CHANGED
|
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
|
|
|
9
9
|
|
|
10
10
|
spec.summary = %q{CLI app to help triage GitLab QA pipelines}
|
|
11
11
|
spec.homepage = 'https://gitlab.com/gitlab-org/quality/dri'
|
|
12
|
-
spec.required_ruby_version = Gem::Requirement.new(">= 2.
|
|
12
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 2.7.0")
|
|
13
13
|
|
|
14
14
|
# Specify which files should be added to the gem when it is released.
|
|
15
15
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
|
@@ -30,7 +30,7 @@ Gem::Specification.new do |spec|
|
|
|
30
30
|
spec.add_dependency "tty-editor", "~> 0.6"
|
|
31
31
|
spec.add_dependency "pastel", "~> 0.8.0"
|
|
32
32
|
spec.add_dependency "thor", "~> 1.0.1"
|
|
33
|
-
spec.add_dependency "markdown-tables", "~> 1.1.1"
|
|
33
|
+
spec.add_dependency "markdown-tables", "~> 1.1.1"
|
|
34
34
|
spec.add_dependency 'json', '~> 2.6.1'
|
|
35
35
|
spec.add_dependency 'httparty', '~> 0.20.0'
|
|
36
36
|
|
data/lib/dri/api_client.rb
CHANGED
|
@@ -3,13 +3,14 @@
|
|
|
3
3
|
require "httparty"
|
|
4
4
|
require "json"
|
|
5
5
|
require "tty-config"
|
|
6
|
+
require 'cgi'
|
|
6
7
|
|
|
7
8
|
module Dri
|
|
8
9
|
class ApiClient
|
|
9
|
-
|
|
10
|
+
|
|
10
11
|
API_URL = 'https://gitlab.com/api/v4'
|
|
11
12
|
TESTCASES_PROJECT_ID = '11229385'
|
|
12
|
-
TRIAGE_PROJECT_ID = '
|
|
13
|
+
TRIAGE_PROJECT_ID = '15291320'
|
|
13
14
|
GITLAB_PROJECT_ID = '278964'
|
|
14
15
|
|
|
15
16
|
def initialize(config)
|
|
@@ -50,6 +51,24 @@ module Dri
|
|
|
50
51
|
fetch_json(url.join)
|
|
51
52
|
end
|
|
52
53
|
|
|
54
|
+
# Fetch MRs
|
|
55
|
+
#
|
|
56
|
+
# @see https://docs.gitlab.com/ee/api/merge_requests.html for all passable options
|
|
57
|
+
#
|
|
58
|
+
# @param [Hash<String>] options
|
|
59
|
+
# @option options [String] state
|
|
60
|
+
# @option options [String] order_by
|
|
61
|
+
# @option options [String] sort
|
|
62
|
+
# @option options [String] milestone
|
|
63
|
+
# @option options [String] labels
|
|
64
|
+
def fetch_mrs(project_id:, **options)
|
|
65
|
+
uri = URI(API_URL)
|
|
66
|
+
uri.query = HTTParty::HashConversions.to_params(options)
|
|
67
|
+
uri.path = File.join(uri.path, 'projects', CGI.escape(project_id), 'merge_requests') # CGI.escape('gitlab-org/gitlab') => 'gitlab-org%2Fgitlab'
|
|
68
|
+
|
|
69
|
+
fetch_json(uri.to_s)
|
|
70
|
+
end
|
|
71
|
+
|
|
53
72
|
def fetch_current_triage_issue
|
|
54
73
|
url = ["#{API_URL}/projects/"]
|
|
55
74
|
url << "#{TRIAGE_PROJECT_ID}/issues?state=opened"
|
|
@@ -97,7 +116,7 @@ module Dri
|
|
|
97
116
|
}
|
|
98
117
|
}
|
|
99
118
|
|
|
100
|
-
response = HTTParty.post(url, options)
|
|
119
|
+
response = HTTParty.post(url, options)
|
|
101
120
|
handle_response(response)
|
|
102
121
|
end
|
|
103
122
|
|
|
@@ -122,4 +141,4 @@ module Dri
|
|
|
122
141
|
handle_response(JSON.parse(response.body))
|
|
123
142
|
end
|
|
124
143
|
end
|
|
125
|
-
end
|
|
144
|
+
end
|
data/lib/dri/command.rb
CHANGED
|
@@ -8,6 +8,8 @@ module Dri
|
|
|
8
8
|
module Commands
|
|
9
9
|
class Fetch
|
|
10
10
|
class Failures < Dri::Command
|
|
11
|
+
using Refinements
|
|
12
|
+
|
|
11
13
|
def initialize(options)
|
|
12
14
|
@options = options
|
|
13
15
|
@today_iso_format = Time.now.strftime('%Y-%m-%dT00:00:00Z')
|
|
@@ -22,46 +24,60 @@ module Dri
|
|
|
22
24
|
url = add_color('URL', :bright_yellow)
|
|
23
25
|
|
|
24
26
|
failures = []
|
|
27
|
+
urgent = []
|
|
25
28
|
labels = [ title, triaged, author, url ]
|
|
26
29
|
triaged_counter = 0
|
|
27
30
|
|
|
28
|
-
logger.info "Fetching today
|
|
29
|
-
|
|
30
|
-
spinner.run do
|
|
31
|
+
logger.info "Fetching today's failures..."
|
|
32
|
+
|
|
33
|
+
spinner.run do
|
|
31
34
|
|
|
32
35
|
response = api_client.fetch_failures(date: @today_iso_format, state: 'opened')
|
|
33
36
|
|
|
34
37
|
if response.nil?
|
|
35
|
-
logger.info
|
|
38
|
+
logger.info 'Life is great, there are no new failures today!'
|
|
36
39
|
exit 0
|
|
37
40
|
end
|
|
38
41
|
|
|
39
42
|
response.each do |failure|
|
|
40
|
-
title =
|
|
41
|
-
author = failure[
|
|
42
|
-
url = failure[
|
|
43
|
-
award_emoji_url = failure[
|
|
43
|
+
title = failure['title'].truncate(60)
|
|
44
|
+
author = failure['author']['username']
|
|
45
|
+
url = failure['web_url']
|
|
46
|
+
award_emoji_url = failure['_links']['award_emoji']
|
|
44
47
|
triaged = add_color('x', :red)
|
|
45
48
|
|
|
46
|
-
emoji_awards = api_client.fetch_awarded_emojis(award_emoji_url)
|
|
49
|
+
emoji_awards = api_client.fetch_awarded_emojis(award_emoji_url).find do |e|
|
|
50
|
+
e['name'] == emoji && e['user']['username'] == username
|
|
51
|
+
end
|
|
47
52
|
|
|
48
|
-
|
|
49
|
-
|
|
53
|
+
if emoji_awards
|
|
54
|
+
triaged = add_color('✓', :green)
|
|
55
|
+
triaged_counter += 1
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
if @options[:urgent]
|
|
59
|
+
labels = failure['labels']
|
|
60
|
+
|
|
61
|
+
labels.each do |label|
|
|
62
|
+
if label.include? ('found:canary.gitlab.com' && 'found:canary.staging.gitlab.com')
|
|
63
|
+
urgent << [title, triaged, author, url]
|
|
64
|
+
end
|
|
65
|
+
end
|
|
50
66
|
end
|
|
51
67
|
|
|
52
68
|
failures << [title, triaged, author, url]
|
|
53
69
|
end
|
|
54
70
|
end
|
|
55
71
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
72
|
+
if @options[:urgent]
|
|
73
|
+
table = TTY::Table.new(labels, urgent)
|
|
74
|
+
puts table.render(:ascii, resize: true, alignments: [:center, :center, :center, :center])
|
|
75
|
+
output.puts "\nFound: #{urgent.size} urgent failures, occurring in both canary.gitlab.com and canary.staging.gitlab.com."
|
|
76
|
+
else
|
|
77
|
+
table = TTY::Table.new(labels, failures)
|
|
78
|
+
puts table.render(:ascii, resize: true, alignments: [:center, :center, :center, :center])
|
|
79
|
+
output.puts "\nFound: #{failures.size} failures, of these #{triaged_counter} have been triaged with a #{emoji}."
|
|
80
|
+
end
|
|
65
81
|
end
|
|
66
82
|
end
|
|
67
83
|
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../../command'
|
|
4
|
+
|
|
5
|
+
require 'tty-table'
|
|
6
|
+
|
|
7
|
+
module Dri
|
|
8
|
+
module Commands
|
|
9
|
+
class Fetch
|
|
10
|
+
class Quarantines < Dri::Command
|
|
11
|
+
using Refinements
|
|
12
|
+
|
|
13
|
+
def initialize(options)
|
|
14
|
+
@options = options
|
|
15
|
+
@today_iso_format = Time.now.strftime('%Y-%m-%dT00:00:00Z')
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def execute(input: $stdin, output: $stdout)
|
|
19
|
+
verify_config_exists
|
|
20
|
+
|
|
21
|
+
title = add_color('Example name', :bright_yellow)
|
|
22
|
+
url = add_color('URL', :bright_yellow)
|
|
23
|
+
|
|
24
|
+
headers = [title, url]
|
|
25
|
+
mrs = []
|
|
26
|
+
|
|
27
|
+
logger.info 'Fetching Quarantine MRs...'
|
|
28
|
+
|
|
29
|
+
spinner.run do
|
|
30
|
+
response = api_client.fetch_mrs(project_id: 'gitlab-org/gitlab', labels: 'QA,Quality', search: '[QUARANTINE]', in: :title, state: :opened)
|
|
31
|
+
|
|
32
|
+
mrs = response.each_with_object([]) do |mr, found_mrs|
|
|
33
|
+
title = mr['title'][13..] # remove the "[QUARANTINE] " prefix
|
|
34
|
+
url = mr['web_url']
|
|
35
|
+
|
|
36
|
+
found_mrs << [title, url]
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
puts TTY::Table.new(headers, mrs).render(:ascii, resize: true, alignments: %i[left center])
|
|
41
|
+
output.puts "Found #{mrs.size} open Quarantine MRs"
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -31,6 +31,10 @@ module Dri
|
|
|
31
31
|
|
|
32
32
|
pipelines = @options[:filter_pipelines] ? filtered_pipelines : @available_pipelines
|
|
33
33
|
|
|
34
|
+
if pipelines.empty?
|
|
35
|
+
logger.error "No pipelines selected to continue to fetch testcases."
|
|
36
|
+
end
|
|
37
|
+
|
|
34
38
|
pipelines.each do |pipeline|
|
|
35
39
|
logger.info "Fetching failing testcases in #{pipeline}\n"
|
|
36
40
|
response = api_client.fetch_failing_testcases(pipeline, state: 'opened')
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
require_relative '../../command'
|
|
2
|
+
require 'tty-table'
|
|
3
|
+
|
|
4
|
+
module Dri
|
|
5
|
+
module Commands
|
|
6
|
+
class Fetch
|
|
7
|
+
class Triaged < Dri::Command
|
|
8
|
+
def initialize(options)
|
|
9
|
+
@options = options
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def execute(input: $stdin, output: $stdout)
|
|
13
|
+
verify_config_exists
|
|
14
|
+
|
|
15
|
+
title = add_color('Title', :magenta)
|
|
16
|
+
url = add_color('URL', :magenta)
|
|
17
|
+
type_label = add_color('Type', :magenta)
|
|
18
|
+
|
|
19
|
+
table_labels = [ title, url, type_label ]
|
|
20
|
+
failures_triaged = []
|
|
21
|
+
|
|
22
|
+
logger.info "Fetching your triaged failures..."
|
|
23
|
+
spinner.start
|
|
24
|
+
|
|
25
|
+
response = api_client.fetch_triaged_failures(emoji: emoji, state: 'opened')
|
|
26
|
+
|
|
27
|
+
if response.empty?
|
|
28
|
+
logger.info "There are no failures triaged yet with #{add_color(emoji, :black, :on_white)}."
|
|
29
|
+
exit 0
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
response.each do |triaged|
|
|
33
|
+
title = triaged["title"].truncate(70)
|
|
34
|
+
url = triaged["web_url"]
|
|
35
|
+
labels = triaged["labels"]
|
|
36
|
+
type = ""
|
|
37
|
+
|
|
38
|
+
labels.each do |label|
|
|
39
|
+
type = label.gsub!('failure::', ' ').to_s if label.include? "failure::"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
labels = triaged["labels"]
|
|
43
|
+
|
|
44
|
+
failures_triaged << [title, url, type]
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
spinner.stop
|
|
48
|
+
|
|
49
|
+
table = TTY::Table.new(table_labels,failures_triaged)
|
|
50
|
+
puts table.render(:ascii, resize: true, alignments: [:center, :center, :center])
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
data/lib/dri/commands/fetch.rb
CHANGED
|
@@ -7,6 +7,18 @@ module Dri
|
|
|
7
7
|
class Fetch < Thor
|
|
8
8
|
namespace :fetch
|
|
9
9
|
|
|
10
|
+
desc 'triaged', 'Command description...'
|
|
11
|
+
method_option :help, aliases: '-h', type: :boolean,
|
|
12
|
+
desc: 'Display usage information'
|
|
13
|
+
def triaged(*)
|
|
14
|
+
if options[:help]
|
|
15
|
+
invoke :help, ['triaged']
|
|
16
|
+
else
|
|
17
|
+
require_relative 'fetch/triaged'
|
|
18
|
+
Dri::Commands::Fetch::Triaged.new(options).execute
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
10
22
|
desc 'testcases', 'Display failing testcases'
|
|
11
23
|
method_option :help, aliases: '-h', type: :boolean,
|
|
12
24
|
desc: 'Display usage information'
|
|
@@ -24,6 +36,8 @@ module Dri
|
|
|
24
36
|
desc 'failures', 'Display failures opened today'
|
|
25
37
|
method_option :help, aliases: '-h', type: :boolean,
|
|
26
38
|
desc: 'Display usage information'
|
|
39
|
+
method_option :urgent, type: :boolean,
|
|
40
|
+
desc: 'Shows failures that quickly escalated'
|
|
27
41
|
def failures(*)
|
|
28
42
|
if options[:help]
|
|
29
43
|
invoke :help, ['failures']
|
|
@@ -32,6 +46,18 @@ module Dri
|
|
|
32
46
|
Dri::Commands::Fetch::Failures.new(options).execute
|
|
33
47
|
end
|
|
34
48
|
end
|
|
49
|
+
|
|
50
|
+
desc 'quarantines', 'Display open quarantine MRs'
|
|
51
|
+
method_option :help, aliases: '-h', type: :boolean,
|
|
52
|
+
desc: 'Display usage information'
|
|
53
|
+
def quarantines(*)
|
|
54
|
+
if options[:help]
|
|
55
|
+
invoke :help, ['quarantines']
|
|
56
|
+
else
|
|
57
|
+
require_relative 'fetch/quarantines'
|
|
58
|
+
Dri::Commands::Fetch::Quarantines.new(options).execute
|
|
59
|
+
end
|
|
60
|
+
end
|
|
35
61
|
end
|
|
36
62
|
end
|
|
37
63
|
end
|
data/lib/dri/commands/init.rb
CHANGED
|
@@ -3,12 +3,13 @@
|
|
|
3
3
|
require_relative '../command'
|
|
4
4
|
|
|
5
5
|
require "tty-font"
|
|
6
|
-
require "pastel"
|
|
7
6
|
|
|
8
7
|
module Dri
|
|
9
8
|
module Commands
|
|
10
9
|
class Init < Dri::Command
|
|
11
10
|
def initialize(options)
|
|
11
|
+
@options = options
|
|
12
|
+
|
|
12
13
|
font = TTY::Font.new(:doom)
|
|
13
14
|
puts pastel.yellow(font.write("DRI"))
|
|
14
15
|
end
|
|
@@ -21,7 +22,7 @@ module Dri
|
|
|
21
22
|
if config.exist?
|
|
22
23
|
overwrite = prompt.yes?("There is already a configuration initialized. Would you like to overwrite it?")
|
|
23
24
|
unless overwrite
|
|
24
|
-
output.puts "Using existing configuration. To view configuration in use try
|
|
25
|
+
output.puts "Using existing configuration. To view configuration in use try #{add_color('dri profile', :yellow)}."
|
|
25
26
|
exit 0
|
|
26
27
|
end
|
|
27
28
|
end
|
|
@@ -34,9 +34,16 @@ module Dri
|
|
|
34
34
|
|
|
35
35
|
logger.info "Assembling the report... "
|
|
36
36
|
# sets each failure on the table
|
|
37
|
+
action_options = ["pinged SET", "reproduced", "transient", "quarantined"]
|
|
38
|
+
|
|
37
39
|
spinner.start
|
|
38
40
|
issues.each do |issue|
|
|
39
|
-
|
|
41
|
+
actions = []
|
|
42
|
+
|
|
43
|
+
if @options[:actions]
|
|
44
|
+
actions = prompt.multi_select("Please mark the actions on #{add_color(issue['title'], :yellow)}: ", action_options)
|
|
45
|
+
end
|
|
46
|
+
report.add_failure(issue, actions)
|
|
40
47
|
end
|
|
41
48
|
|
|
42
49
|
if @options[:format] == 'list'
|
|
@@ -78,7 +85,7 @@ module Dri
|
|
|
78
85
|
response = api_client.post_triage_report_note(iid: current_issue_iid, body: note)
|
|
79
86
|
|
|
80
87
|
output.puts "Done! ✅\n"
|
|
81
|
-
logger.success "Thanks @#{username}, your report was posted at https://gitlab.com/gitlab-org/quality/
|
|
88
|
+
logger.success "Thanks @#{username}, your report was posted at https://gitlab.com/gitlab-org/quality/pipeline-triage/-/issues/#{current_issue_iid} 🎉"
|
|
82
89
|
end
|
|
83
90
|
end
|
|
84
91
|
end
|
data/lib/dri/commands/publish.rb
CHANGED
|
@@ -13,6 +13,8 @@ module Dri
|
|
|
13
13
|
desc: 'Generates a report locally'
|
|
14
14
|
method_option :format, aliases: '-f', type: :string, :default => "table",
|
|
15
15
|
desc: 'Formats the report'
|
|
16
|
+
method_option :actions, type: :boolean,
|
|
17
|
+
desc: 'Updates actions on failures'
|
|
16
18
|
def report(*)
|
|
17
19
|
if options[:help]
|
|
18
20
|
invoke :help, ['report']
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../../command'
|
|
4
|
+
require 'fileutils'
|
|
5
|
+
|
|
6
|
+
module Dri
|
|
7
|
+
module Commands
|
|
8
|
+
class Rm
|
|
9
|
+
class Profile < Dri::Command
|
|
10
|
+
def initialize(options)
|
|
11
|
+
@options = options
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def execute(input: $stdin, output: $stdout)
|
|
15
|
+
verify_config_exists
|
|
16
|
+
|
|
17
|
+
remove = prompt.yes? "Are you sure you want to remove existing profile?"
|
|
18
|
+
|
|
19
|
+
unless remove
|
|
20
|
+
logger.info "Profile kept in place 👍"
|
|
21
|
+
exit 0
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
logger.info "Removing profile..."
|
|
25
|
+
|
|
26
|
+
FileUtils.rm("#{Dir.pwd}/.dri_profile.yml")
|
|
27
|
+
|
|
28
|
+
logger.success "Done ✅"
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../../command'
|
|
4
|
+
require 'fileutils'
|
|
5
|
+
|
|
6
|
+
module Dri
|
|
7
|
+
module Commands
|
|
8
|
+
class Rm
|
|
9
|
+
class Reports < Dri::Command
|
|
10
|
+
def initialize(options)
|
|
11
|
+
@options = options
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def execute(input: $stdin, output: $stdout)
|
|
15
|
+
FileUtils.rm_rf("#{Dir.pwd}/handover_reports")
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
data/lib/dri/commands/rm.rb
CHANGED
|
@@ -8,6 +8,30 @@ module Dri
|
|
|
8
8
|
|
|
9
9
|
namespace :rm
|
|
10
10
|
|
|
11
|
+
desc 'profile', 'Command description...'
|
|
12
|
+
method_option :help, aliases: '-h', type: :boolean,
|
|
13
|
+
desc: 'Display usage information'
|
|
14
|
+
def profile(*)
|
|
15
|
+
if options[:help]
|
|
16
|
+
invoke :help, ['profile']
|
|
17
|
+
else
|
|
18
|
+
require_relative 'rm/profile'
|
|
19
|
+
Dri::Commands::Rm::Profile.new(options).execute
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
desc 'reports', 'Command description...'
|
|
24
|
+
method_option :help, aliases: '-h', type: :boolean,
|
|
25
|
+
desc: 'Display usage information'
|
|
26
|
+
def reports(*)
|
|
27
|
+
if options[:help]
|
|
28
|
+
invoke :help, ['reports']
|
|
29
|
+
else
|
|
30
|
+
require_relative 'rm/reports'
|
|
31
|
+
Dri::Commands::Rm::Reports.new(options).execute
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
11
35
|
desc 'emoji', 'Remove triage emoji from all failures'
|
|
12
36
|
method_option :help, aliases: '-h', type: :boolean,
|
|
13
37
|
desc: 'Display usage information'
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Refinements
|
|
4
|
+
refine String do
|
|
5
|
+
# Truncate string to limit of _n_ characters
|
|
6
|
+
# @param [Integer] limit the limit of characters
|
|
7
|
+
# @return [String] the resulting string after truncation
|
|
8
|
+
def truncate(limit = 50)
|
|
9
|
+
return self.freeze if self.size <= limit
|
|
10
|
+
|
|
11
|
+
# limit - 3 for the ellipses
|
|
12
|
+
self[0...(limit - 3)] << '...'
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
data/lib/dri/report.rb
CHANGED
|
@@ -17,7 +17,7 @@ module Dri
|
|
|
17
17
|
@header = "# #{timezone}, #{@weekday} - #{@date}\n posted by: @#{username}"
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
-
def add_failure(failure)
|
|
20
|
+
def add_failure(failure, actions_opts = [])
|
|
21
21
|
iid = failure["iid"]
|
|
22
22
|
title = failure["title"]
|
|
23
23
|
link = failure["web_url"]
|
|
@@ -38,10 +38,10 @@ module Dri
|
|
|
38
38
|
assigned_status = assigned?(assignees)
|
|
39
39
|
pipelines = filter_pipeline_labels(labels)
|
|
40
40
|
|
|
41
|
-
linked_pipelines = link_pipelines(iid, pipelines)
|
|
41
|
+
linked_pipelines = link_pipelines(iid, pipelines, description)
|
|
42
42
|
|
|
43
43
|
actions = ""
|
|
44
|
-
actions.concat actions_status_template(failure_type, assigned_status)
|
|
44
|
+
actions.concat actions_status_template(failure_type, assigned_status, actions_opts)
|
|
45
45
|
actions.concat actions_fixes_template(related_mrs)
|
|
46
46
|
|
|
47
47
|
@failures << [title, emojified_link, linked_pipelines, stack_trace, actions]
|
|
@@ -49,28 +49,75 @@ module Dri
|
|
|
49
49
|
|
|
50
50
|
private
|
|
51
51
|
|
|
52
|
-
def link_pipelines(iid, pipelines)
|
|
52
|
+
def link_pipelines(iid, pipelines, description)
|
|
53
|
+
linked = []
|
|
54
|
+
label_pipeline_map = {
|
|
55
|
+
'gitlab.com' => '/quality/production',
|
|
56
|
+
'canary.gitlab.com' => '/quality/canary',
|
|
57
|
+
# 'canary.staging.gitlab.com' => '',
|
|
58
|
+
'main' => '/gitlab-org/gitlab-qa-mirror',
|
|
59
|
+
'master' => '/gitlab-org/gitlab-qa-mirror',
|
|
60
|
+
'nightly' => '/quality/nightly',
|
|
61
|
+
'pre.gitlab.com' => '/quality/preprod',
|
|
62
|
+
'staging-ref' => '/quality/staging-ref',
|
|
63
|
+
'staging.gitlab.com' => '/quality/staging',
|
|
64
|
+
'release' => '/quality/release'
|
|
65
|
+
}
|
|
66
|
+
|
|
53
67
|
failure_notes = @api_client.fetch_failure_notes(issue_iid: iid)
|
|
54
68
|
|
|
55
|
-
|
|
69
|
+
return if pipelines.empty?
|
|
56
70
|
|
|
57
71
|
pipelines.each do |pipeline|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
72
|
+
next if !label_pipeline_map.has_key? pipeline
|
|
73
|
+
|
|
74
|
+
pipeline_in_notes_found = false
|
|
75
|
+
pipeline_link = ''
|
|
76
|
+
pipeline_link_sanitized = ''
|
|
77
|
+
pipeline_markdown = ''
|
|
78
|
+
|
|
79
|
+
failure_notes.each do |note|
|
|
80
|
+
if note["body"].include? label_pipeline_map.fetch(pipeline)
|
|
81
|
+
pipeline_in_notes_found = true
|
|
82
|
+
pipeline_link = URI.extract(note["body"], %w(https))
|
|
83
|
+
break
|
|
65
84
|
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
unless pipeline_in_notes_found
|
|
88
|
+
links_description = URI.extract(description, %w(https))
|
|
89
|
+
pipeline_link = links_description.select { |link| link.include? label_pipeline_map.fetch(pipeline) }
|
|
90
|
+
end
|
|
66
91
|
|
|
67
|
-
|
|
92
|
+
if !pipeline_link.empty?
|
|
93
|
+
pipeline_link_sanitized = pipeline_link.join.strip.chop
|
|
94
|
+
pipeline_markdown = "[#{pipeline.gsub(/.gitlab.com/, '')}](#{pipeline_link_sanitized})"
|
|
95
|
+
linked << pipeline_markdown
|
|
96
|
+
end
|
|
68
97
|
end
|
|
69
|
-
linked
|
|
98
|
+
linked.join(', ')
|
|
70
99
|
end
|
|
71
100
|
|
|
72
|
-
def actions_status_template(failure_type, assigned_status)
|
|
73
|
-
|
|
101
|
+
def actions_status_template(failure_type, assigned_status, actions_opts)
|
|
102
|
+
notified_set = ''
|
|
103
|
+
quarantined = ''
|
|
104
|
+
reproduced = ''
|
|
105
|
+
transient = ''
|
|
106
|
+
|
|
107
|
+
notified_set = '<li>[x] notified SET</li>' if actions_opts.include? 'pinged SET'
|
|
108
|
+
quarantined = '<li>[x] quarantined</li>' if actions_opts.include? 'quarantined'
|
|
109
|
+
reproduced = '<li>[x] reproduced</li>' if actions_opts.include? 'reproduced'
|
|
110
|
+
transient = '<li>[x] transient</li>' if actions_opts.include? 'transient'
|
|
111
|
+
|
|
112
|
+
action_status = "<i>Status:</i><ul>"
|
|
113
|
+
action_status << "<li>#{failure_type}</li>"
|
|
114
|
+
action_status << "</li><li>#{assigned_status}</li>"
|
|
115
|
+
action_status << notified_set
|
|
116
|
+
action_status << quarantined
|
|
117
|
+
action_status << reproduced
|
|
118
|
+
action_status << transient
|
|
119
|
+
|
|
120
|
+
action_status
|
|
74
121
|
end
|
|
75
122
|
|
|
76
123
|
def actions_fixes_template(related_mrs)
|
|
@@ -90,10 +137,10 @@ module Dri
|
|
|
90
137
|
pipelines = []
|
|
91
138
|
|
|
92
139
|
labels.each do |label|
|
|
93
|
-
matchers = { 'found:' => ' '
|
|
140
|
+
matchers = { 'found:' => ' '}
|
|
94
141
|
|
|
95
142
|
if label.include? "found:"
|
|
96
|
-
pipeline = label.gsub(/found
|
|
143
|
+
pipeline = label.gsub(/found:/) { |match| matchers[match] }
|
|
97
144
|
pipelines << pipeline.strip
|
|
98
145
|
end
|
|
99
146
|
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.1.
|
|
4
|
+
version: 0.1.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- GitLab Quality
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2022-03-
|
|
11
|
+
date: 2022-03-29 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: tty-config
|
|
@@ -248,7 +248,7 @@ dependencies:
|
|
|
248
248
|
- - "~>"
|
|
249
249
|
- !ruby/object:Gem::Version
|
|
250
250
|
version: 0.9.1
|
|
251
|
-
description:
|
|
251
|
+
description:
|
|
252
252
|
email:
|
|
253
253
|
- quality+dri@gitlab.com
|
|
254
254
|
executables:
|
|
@@ -260,6 +260,7 @@ files:
|
|
|
260
260
|
- ".gitignore"
|
|
261
261
|
- ".gitlab-ci.yml"
|
|
262
262
|
- ".rspec"
|
|
263
|
+
- ".tool-versions"
|
|
263
264
|
- Gemfile
|
|
264
265
|
- Gemfile.lock
|
|
265
266
|
- LICENSE.txt
|
|
@@ -275,13 +276,18 @@ files:
|
|
|
275
276
|
- lib/dri/command.rb
|
|
276
277
|
- lib/dri/commands/fetch.rb
|
|
277
278
|
- lib/dri/commands/fetch/failures.rb
|
|
279
|
+
- lib/dri/commands/fetch/quarantines.rb
|
|
278
280
|
- lib/dri/commands/fetch/testcases.rb
|
|
281
|
+
- lib/dri/commands/fetch/triaged.rb
|
|
279
282
|
- lib/dri/commands/init.rb
|
|
280
283
|
- lib/dri/commands/profile.rb
|
|
281
284
|
- lib/dri/commands/publish.rb
|
|
282
285
|
- lib/dri/commands/publish/report.rb
|
|
283
286
|
- lib/dri/commands/rm.rb
|
|
284
287
|
- lib/dri/commands/rm/emoji.rb
|
|
288
|
+
- lib/dri/commands/rm/profile.rb
|
|
289
|
+
- lib/dri/commands/rm/reports.rb
|
|
290
|
+
- lib/dri/refinements/truncate.rb
|
|
285
291
|
- lib/dri/report.rb
|
|
286
292
|
- lib/dri/utils/markdown_lists.rb
|
|
287
293
|
- lib/dri/version.rb
|
|
@@ -289,7 +295,7 @@ homepage: https://gitlab.com/gitlab-org/quality/dri
|
|
|
289
295
|
licenses:
|
|
290
296
|
- MIT
|
|
291
297
|
metadata: {}
|
|
292
|
-
post_install_message:
|
|
298
|
+
post_install_message:
|
|
293
299
|
rdoc_options: []
|
|
294
300
|
require_paths:
|
|
295
301
|
- lib
|
|
@@ -297,7 +303,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
297
303
|
requirements:
|
|
298
304
|
- - ">="
|
|
299
305
|
- !ruby/object:Gem::Version
|
|
300
|
-
version: 2.
|
|
306
|
+
version: 2.7.0
|
|
301
307
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
302
308
|
requirements:
|
|
303
309
|
- - ">="
|
|
@@ -305,7 +311,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
305
311
|
version: '0'
|
|
306
312
|
requirements: []
|
|
307
313
|
rubygems_version: 3.1.6
|
|
308
|
-
signing_key:
|
|
314
|
+
signing_key:
|
|
309
315
|
specification_version: 4
|
|
310
316
|
summary: CLI app to help triage GitLab QA pipelines
|
|
311
317
|
test_files: []
|