dri 0.7.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitlab-ci.yml +2 -2
- data/Gemfile.lock +1 -1
- data/README.md +2 -1
- data/lib/dri/api_client.rb +66 -20
- data/lib/dri/commands/analyze/stack_traces.rb +4 -2
- data/lib/dri/commands/fetch/failures.rb +15 -12
- data/lib/dri/commands/fetch/featureflags.rb +2 -2
- data/lib/dri/commands/fetch/pipelines.rb +2 -1
- data/lib/dri/commands/fetch/quarantines.rb +3 -1
- data/lib/dri/commands/fetch/triaged.rb +6 -4
- data/lib/dri/commands/incidents.rb +4 -2
- data/lib/dri/commands/publish/report.rb +28 -10
- data/lib/dri/commands/publish.rb +3 -0
- data/lib/dri/commands/rm/emoji.rb +1 -1
- data/lib/dri/feature_flag_report.rb +2 -2
- data/lib/dri/report.rb +16 -14
- data/lib/dri/utils/constants.rb +33 -0
- data/lib/dri/version.rb +1 -1
- metadata +2 -3
- data/lib/dri/utils/feature_flag_consts.rb +0 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3b4a86b6e658b2426bfce682d92f34f1441a9a1812f0cc805088b8c250991d4d
|
4
|
+
data.tar.gz: 3cf2b62c9aa5b1ef8de5cd51f8fdd927fa5335c9a1299b4947ab162b8afe2526
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4d79cdc2db7e1f959fee5b80988516677455a1d18e151a300923b2d19a58f0bb4fc4342db5e3785c34b5ef4475655b37cb10ad8850e488429ab12cfd8ad03fb8
|
7
|
+
data.tar.gz: 5120e934a2e8a45a51b4328bf5a1ae3af1a55b19e42023e538f65c958bfad0a8810f89844b7f2ef40608d0c7233d64f652ba9f101cff37f203a900d940084771
|
data/.gitlab-ci.yml
CHANGED
@@ -15,12 +15,12 @@
|
|
15
15
|
- vendor/bundle
|
16
16
|
policy: pull
|
17
17
|
rules:
|
18
|
-
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
|
18
|
+
- if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
|
19
19
|
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
20
20
|
|
21
21
|
include:
|
22
22
|
- project: gitlab-org/quality/pipeline-common
|
23
|
-
ref:
|
23
|
+
ref: 1.2.3
|
24
24
|
file:
|
25
25
|
- /ci/gem-release.yml
|
26
26
|
- /ci/ref-update.gitlab-ci.yml
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -156,12 +156,13 @@ $ dri publish report --format=table # formats the report in a table (default)
|
|
156
156
|
$ dri publish report --dry-run # the report is only generated locally
|
157
157
|
$ dri publish report --actions # activate the actions prompt for each failure
|
158
158
|
$ dri publish report --feature-flags # includes a summary of the feature flag changes on each environment
|
159
|
+
$ dri publish report --update # update the report note if has already been posted
|
159
160
|
```
|
160
161
|
|
161
162
|
**Note:** These options above can be combined like:
|
162
163
|
|
163
164
|
```shell
|
164
|
-
$ dri publish report --format=list --dry-run --actions
|
165
|
+
$ dri publish report --format=list --dry-run --actions --update
|
165
166
|
```
|
166
167
|
|
167
168
|
**Actions**
|
data/lib/dri/api_client.rb
CHANGED
@@ -6,16 +6,16 @@ require "tty-config"
|
|
6
6
|
require "cgi"
|
7
7
|
require "gitlab"
|
8
8
|
|
9
|
+
require_relative './utils/constants'
|
10
|
+
|
9
11
|
module Dri
|
10
12
|
TokenNotProvidedError = Class.new(StandardError)
|
11
13
|
class ApiClient # rubocop:disable Metrics/ClassLength
|
14
|
+
include Dri::Utils::Constants::ProjectIDs
|
15
|
+
include Dri::Utils::Constants::Triage::Labels
|
16
|
+
|
12
17
|
API_URL = "https://gitlab.com/api/v4"
|
13
18
|
OPS_API_URL = "https://ops.gitlab.net/api/v4"
|
14
|
-
TESTCASES_PROJECT_ID = 11229385
|
15
|
-
TRIAGE_PROJECT_ID = 15291320
|
16
|
-
GITLAB_PROJECT_ID = 278964
|
17
|
-
FEATURE_FLAG_LOG_PROJECT_ID = 15208716
|
18
|
-
INFRA_TEAM_PROD_PROJECT_ID = 7444821
|
19
19
|
|
20
20
|
def initialize(config, ops = false)
|
21
21
|
@token = config.read.dig("settings", "token")
|
@@ -28,14 +28,31 @@ module Dri
|
|
28
28
|
@ops_instance = ops
|
29
29
|
end
|
30
30
|
|
31
|
-
# Fetch triaged failures
|
31
|
+
# Fetch all triaged failures
|
32
32
|
#
|
33
33
|
# @param [String] emoji
|
34
34
|
# @param [String] state
|
35
|
-
# @return [Gitlab::ObjectifiedHash]
|
36
|
-
def
|
35
|
+
# @return [Array<Gitlab::ObjectifiedHash>]
|
36
|
+
def fetch_all_triaged_failures(emoji:, state:)
|
37
|
+
project_ids = [GITLAB_PROJECT_ID, CUSTOMERSDOT_PROJECT_ID]
|
38
|
+
failures = []
|
39
|
+
|
40
|
+
project_ids.each do |project_id|
|
41
|
+
failures += fetch_triaged_failures(project_id: project_id, emoji: emoji, state: state)
|
42
|
+
end
|
43
|
+
|
44
|
+
failures
|
45
|
+
end
|
46
|
+
|
47
|
+
# Fetch triaged failures for a given project
|
48
|
+
#
|
49
|
+
# @param [Integer] project_id
|
50
|
+
# @param [String] emoji
|
51
|
+
# @param [String] state
|
52
|
+
# @return [Array<Gitlab::ObjectifiedHash>]
|
53
|
+
def fetch_triaged_failures(project_id:, emoji:, state:)
|
37
54
|
gitlab.issues(
|
38
|
-
|
55
|
+
project_id,
|
39
56
|
order_by: "updated_at",
|
40
57
|
my_reaction_emoji: emoji,
|
41
58
|
scope: "all",
|
@@ -61,7 +78,7 @@ module Dri
|
|
61
78
|
# @param [Integer] issue_iid
|
62
79
|
# @param [Integer] project_id
|
63
80
|
# @return [Array<Gitlab::ObjectifiedHash>]
|
64
|
-
def fetch_awarded_emojis(issue_iid, project_id:
|
81
|
+
def fetch_awarded_emojis(issue_iid, project_id:)
|
65
82
|
gitlab.award_emojis(project_id, issue_iid, "issue")
|
66
83
|
end
|
67
84
|
|
@@ -76,14 +93,14 @@ module Dri
|
|
76
93
|
labels: "#{pipeline}::failed",
|
77
94
|
state: state,
|
78
95
|
scope: "all",
|
79
|
-
"not[labels]":
|
96
|
+
"not[labels]": QUARANTINE
|
80
97
|
).auto_paginate
|
81
98
|
end
|
82
99
|
|
83
100
|
# Fetch issues related to failing test cases
|
84
101
|
#
|
85
102
|
# @return [Array<Gitlab::ObjectifiedHash>]
|
86
|
-
def fetch_test_failure_issues(labels:
|
103
|
+
def fetch_test_failure_issues(labels: FAILURE_NEW)
|
87
104
|
gitlab.issues(
|
88
105
|
GITLAB_PROJECT_ID,
|
89
106
|
labels: labels,
|
@@ -94,10 +111,11 @@ module Dri
|
|
94
111
|
|
95
112
|
# Fetch related issue mrs
|
96
113
|
#
|
114
|
+
# @param [Integer] project_id
|
97
115
|
# @param [Integer] issue_iid
|
98
116
|
# @return [Array<Gitlab::ObjectifiedHash>]
|
99
|
-
def fetch_related_mrs(issue_iid)
|
100
|
-
gitlab.related_issue_merge_requests(
|
117
|
+
def fetch_related_mrs(project_id, issue_iid)
|
118
|
+
gitlab.related_issue_merge_requests(project_id, issue_iid)
|
101
119
|
end
|
102
120
|
|
103
121
|
# Fetch MRs
|
@@ -130,15 +148,42 @@ module Dri
|
|
130
148
|
gitlab.create_issue_note(TRIAGE_PROJECT_ID, iid, body)
|
131
149
|
end
|
132
150
|
|
133
|
-
#
|
151
|
+
# Update triage report note
|
152
|
+
#
|
153
|
+
# @param [Integer] iid
|
154
|
+
# @param [Integer] note_id
|
155
|
+
# @param [String] body
|
156
|
+
# @return [Gitlab::ObjectifiedHash]
|
157
|
+
def update_triage_report_note(iid:, note_id:, body:)
|
158
|
+
gitlab.edit_issue_note(TRIAGE_PROJECT_ID, iid, note_id, body)
|
159
|
+
end
|
160
|
+
|
161
|
+
# Fetch all new failures
|
162
|
+
#
|
163
|
+
# @param [String] date
|
164
|
+
# @param [String] state
|
165
|
+
# @return [Array<Gitlab::ObjectifiedHash>]
|
166
|
+
def fetch_all_new_failures(date:, state:)
|
167
|
+
project_ids = [GITLAB_PROJECT_ID, CUSTOMERSDOT_PROJECT_ID]
|
168
|
+
failures = []
|
169
|
+
|
170
|
+
project_ids.each do |project_id|
|
171
|
+
failures += fetch_new_failures(project_id: project_id, date: date, state: state)
|
172
|
+
end
|
173
|
+
|
174
|
+
failures
|
175
|
+
end
|
176
|
+
|
177
|
+
# Fetch new failures for a given project
|
134
178
|
#
|
179
|
+
# @param [Integer] project_id
|
135
180
|
# @param [String] date
|
136
181
|
# @param [String] state
|
137
182
|
# @return [Array<Gitlab::ObjectifiedHash>]
|
138
|
-
def
|
183
|
+
def fetch_new_failures(project_id:, date:, state:)
|
139
184
|
gitlab.issues(
|
140
|
-
|
141
|
-
labels:
|
185
|
+
project_id,
|
186
|
+
labels: FAILURE_NEW,
|
142
187
|
order_by: "updated_at",
|
143
188
|
state: state,
|
144
189
|
scope: "all",
|
@@ -149,10 +194,11 @@ module Dri
|
|
149
194
|
|
150
195
|
# Fetch failure notes
|
151
196
|
#
|
197
|
+
# @param [Integer] project_id
|
152
198
|
# @param [Integer] issue_iid
|
153
199
|
# @return [Array<Gitlab::ObjectifiedHash>]
|
154
|
-
def fetch_failure_notes(issue_iid)
|
155
|
-
gitlab.issue_notes(
|
200
|
+
def fetch_failure_notes(project_id, issue_iid)
|
201
|
+
gitlab.issue_notes(project_id, issue_iid, per_page: 100).auto_paginate
|
156
202
|
end
|
157
203
|
|
158
204
|
# Delete award emoji
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require_relative '../../command'
|
4
4
|
require_relative '../../utils/table'
|
5
|
+
require_relative '../../utils/constants'
|
5
6
|
require 'amatch'
|
6
7
|
require 'fileutils'
|
7
8
|
|
@@ -11,10 +12,11 @@ module Dri
|
|
11
12
|
class StackTraces < Dri::Command
|
12
13
|
include Amatch
|
13
14
|
include Dri::Utils::Table
|
15
|
+
include Dri::Utils::Constants::Triage::Labels
|
14
16
|
|
15
17
|
def initialize(options)
|
16
18
|
@options = options
|
17
|
-
@labels = options[:labels] ||
|
19
|
+
@labels = options[:labels] || FAILURE_NEW
|
18
20
|
@similarity_score_threshold = options[:similarity_score_threshold] || 0.9
|
19
21
|
end
|
20
22
|
|
@@ -29,7 +31,7 @@ module Dri
|
|
29
31
|
logger.info "#{Time.now.utc} API response completed"
|
30
32
|
|
31
33
|
if response.empty?
|
32
|
-
logger.info
|
34
|
+
logger.info "There are no #{FAILURE_NEW} issues identified!"
|
33
35
|
exit 0
|
34
36
|
end
|
35
37
|
|
@@ -2,12 +2,14 @@
|
|
2
2
|
|
3
3
|
require_relative '../../command'
|
4
4
|
require_relative '../../utils/table'
|
5
|
+
require_relative '../../utils/constants'
|
5
6
|
|
6
7
|
module Dri
|
7
8
|
module Commands
|
8
9
|
class Fetch
|
9
10
|
class Failures < Dri::Command
|
10
11
|
include Dri::Utils::Table
|
12
|
+
include Dri::Utils::Constants::Triage::Labels
|
11
13
|
using Refinements
|
12
14
|
|
13
15
|
SORT_BY_OPTIONS = {
|
@@ -34,33 +36,34 @@ module Dri
|
|
34
36
|
author = add_color('Author', :bright_yellow)
|
35
37
|
url = add_color('URL', :bright_yellow)
|
36
38
|
|
37
|
-
|
39
|
+
sorted_failures = []
|
38
40
|
labels = [title, triaged, environment, author, url]
|
39
41
|
triaged_counter = 0
|
40
42
|
|
41
43
|
logger.info "Fetching today's failures..."
|
42
44
|
|
43
45
|
spinner.run do # rubocop:disable Metrics/BlockLength
|
44
|
-
|
46
|
+
failures = api_client.fetch_all_new_failures(date: @today_iso_format, state: 'opened')
|
45
47
|
|
46
|
-
if
|
48
|
+
if failures.empty?
|
47
49
|
logger.info 'Life is great, there are no new failures today!'
|
48
50
|
exit 0
|
49
51
|
end
|
50
52
|
|
51
|
-
|
53
|
+
failures.each do |failure|
|
54
|
+
project_id = failure.project_id
|
52
55
|
title = failure.title.truncate(60)
|
53
56
|
author = failure.to_h.dig('author', 'username')
|
54
57
|
url = failure.web_url
|
55
58
|
triaged = add_color('x', :red)
|
56
|
-
envs = failure.labels.select { |l| l.include?(
|
59
|
+
envs = failure.labels.select { |l| l.include?(FOUND) }.map do |l|
|
57
60
|
env = l.split(':').last.gsub('.gitlab.com', '')
|
58
61
|
|
59
62
|
env == 'gitlab.com' ? 'production' : env
|
60
63
|
end
|
61
64
|
urgent = urgent_environments.all? { |env| envs.include?(env) }
|
62
65
|
|
63
|
-
emoji_awards = api_client.fetch_awarded_emojis(failure.iid).find do |e|
|
66
|
+
emoji_awards = api_client.fetch_awarded_emojis(failure.iid, project_id: project_id).find do |e|
|
64
67
|
e.name == emoji && e.to_h.dig('user', 'username') == username
|
65
68
|
end
|
66
69
|
|
@@ -70,26 +73,26 @@ module Dri
|
|
70
73
|
end
|
71
74
|
|
72
75
|
if @options[:urgent]
|
73
|
-
|
76
|
+
sorted_failures << [title, triaged, envs.first, author, url] if urgent
|
74
77
|
else
|
75
|
-
|
78
|
+
sorted_failures << [title, triaged, envs.first, author, url]
|
76
79
|
end
|
77
80
|
end
|
78
81
|
|
79
|
-
|
82
|
+
sorted_failures.sort_by! { |failure| failure[SORT_BY_OPTIONS[@options[:sort_by]&.to_sym || :environment]] }
|
80
83
|
end
|
81
84
|
|
82
85
|
msg = if @options[:urgent]
|
83
86
|
<<~MSG
|
84
|
-
Found: #{
|
87
|
+
Found: #{sorted_failures.size} urgent failures, occurring in both canary.gitlab.com and canary.staging.gitlab.com.
|
85
88
|
MSG
|
86
89
|
else
|
87
90
|
<<~MSG
|
88
|
-
Found: #{
|
91
|
+
Found: #{sorted_failures.size} failures, of these #{triaged_counter} have been triaged with a #{emoji}.
|
89
92
|
MSG
|
90
93
|
end
|
91
94
|
|
92
|
-
print_table(labels,
|
95
|
+
print_table(labels, sorted_failures, alignments: [:left, :center, :center, :left])
|
93
96
|
output.puts(msg)
|
94
97
|
end
|
95
98
|
end
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
require_relative '../../command'
|
4
4
|
require_relative '../../utils/table'
|
5
|
-
require_relative '../../utils/
|
5
|
+
require_relative '../../utils/constants'
|
6
6
|
require_relative '../../feature_flag_report'
|
7
7
|
|
8
8
|
module Dri
|
@@ -10,7 +10,7 @@ module Dri
|
|
10
10
|
class Fetch
|
11
11
|
class FeatureFlags < Dri::Command
|
12
12
|
include Dri::Utils::Table
|
13
|
-
include Dri::Utils::
|
13
|
+
include Dri::Utils::Constants::FeatureFlag
|
14
14
|
|
15
15
|
def initialize(options)
|
16
16
|
@options = options
|
@@ -10,6 +10,7 @@ module Dri
|
|
10
10
|
class Fetch
|
11
11
|
class Pipelines < Dri::Command # rubocop:disable Metrics/ClassLength
|
12
12
|
include Dri::Utils::Table
|
13
|
+
include Dri::Utils::Constants
|
13
14
|
using Refinements
|
14
15
|
|
15
16
|
NUM_OF_TESTS_LIVE_ENV = 1000
|
@@ -27,7 +28,7 @@ module Dri
|
|
27
28
|
table_labels = define_table_labels
|
28
29
|
|
29
30
|
spinner.run do
|
30
|
-
|
31
|
+
PIPELINE_ENVIRONMENTS.each do |environment, details|
|
31
32
|
logger.info "Fetching last executed #{environment} pipeline"
|
32
33
|
pipelines << fetch_pipeline(pipeline_name: environment.to_s, details: details)
|
33
34
|
logger.info "Fetching complete for #{environment} ✓"
|
@@ -2,12 +2,14 @@
|
|
2
2
|
|
3
3
|
require_relative '../../command'
|
4
4
|
require_relative '../../utils/table'
|
5
|
+
require_relative '../../utils/constants'
|
5
6
|
|
6
7
|
module Dri
|
7
8
|
module Commands
|
8
9
|
class Fetch
|
9
10
|
class Quarantines < Dri::Command
|
10
11
|
include Dri::Utils::Table
|
12
|
+
include Dri::Utils::Constants::Triage::Labels
|
11
13
|
using Refinements
|
12
14
|
|
13
15
|
def initialize(options, search: '[QUARANTINE]')
|
@@ -30,7 +32,7 @@ module Dri
|
|
30
32
|
spinner.run do
|
31
33
|
response = api_client.fetch_mrs(
|
32
34
|
project_id: 'gitlab-org/gitlab',
|
33
|
-
labels:
|
35
|
+
labels: "#{QA},#{QUALITY}",
|
34
36
|
search: @search,
|
35
37
|
in: :title,
|
36
38
|
state: :opened
|
@@ -2,12 +2,14 @@
|
|
2
2
|
|
3
3
|
require_relative '../../command'
|
4
4
|
require_relative '../../utils/table'
|
5
|
+
require_relative '../../utils/constants'
|
5
6
|
|
6
7
|
module Dri
|
7
8
|
module Commands
|
8
9
|
class Fetch
|
9
10
|
class Triaged < Dri::Command
|
10
11
|
include Dri::Utils::Table
|
12
|
+
include Dri::Utils::Constants::Triage::Labels
|
11
13
|
using Refinements
|
12
14
|
|
13
15
|
def initialize(options)
|
@@ -27,21 +29,21 @@ module Dri
|
|
27
29
|
logger.info "Fetching your triaged failures..."
|
28
30
|
spinner.start
|
29
31
|
|
30
|
-
|
32
|
+
failures = api_client.fetch_all_triaged_failures(emoji: emoji, state: 'opened')
|
31
33
|
|
32
|
-
if
|
34
|
+
if failures.empty?
|
33
35
|
logger.info "There are no failures triaged yet with #{add_color(emoji, :black, :on_white)}."
|
34
36
|
exit 0
|
35
37
|
end
|
36
38
|
|
37
|
-
|
39
|
+
failures.each do |triaged|
|
38
40
|
title = triaged["title"].truncate(70)
|
39
41
|
url = triaged["web_url"]
|
40
42
|
labels = triaged["labels"]
|
41
43
|
type = ""
|
42
44
|
|
43
45
|
labels.each do |label|
|
44
|
-
type = label.gsub!(
|
46
|
+
type = label.gsub!(FAILURE, ' ').to_s if label.include? FAILURE
|
45
47
|
end
|
46
48
|
|
47
49
|
failures_triaged << [title, url, type]
|
@@ -2,11 +2,13 @@
|
|
2
2
|
|
3
3
|
require_relative '../command'
|
4
4
|
require_relative '../utils/table'
|
5
|
+
require_relative '../utils/constants'
|
5
6
|
|
6
7
|
module Dri
|
7
8
|
module Commands
|
8
9
|
class Incidents < Dri::Command
|
9
10
|
include Dri::Utils::Table
|
11
|
+
include Dri::Utils::Constants::Triage::Labels
|
10
12
|
using Refinements
|
11
13
|
|
12
14
|
def initialize(options)
|
@@ -42,8 +44,8 @@ module Dri
|
|
42
44
|
service = "N/A"
|
43
45
|
|
44
46
|
labels.each do |label|
|
45
|
-
status = label.gsub!(
|
46
|
-
service = label.gsub!(
|
47
|
+
status = label.gsub!(INCIDENT, ' ').to_s if label.include? INCIDENT
|
48
|
+
service = label.gsub!(SERVICE, ' ').to_s if label.include? SERVICE
|
47
49
|
end
|
48
50
|
|
49
51
|
incidents << [title, service, status, url]
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
require_relative '../../command'
|
4
4
|
require_relative '../../utils/markdown_lists'
|
5
|
-
require_relative '../../utils/
|
5
|
+
require_relative '../../utils/constants'
|
6
6
|
require_relative '../../report'
|
7
7
|
require_relative '../../feature_flag_report'
|
8
8
|
|
@@ -14,7 +14,7 @@ module Dri
|
|
14
14
|
module Commands
|
15
15
|
class Publish
|
16
16
|
class Report < Dri::Command # rubocop:disable Metrics/ClassLength
|
17
|
-
include Dri::Utils::
|
17
|
+
include Dri::Utils::Constants::FeatureFlag
|
18
18
|
|
19
19
|
def initialize(options)
|
20
20
|
@options = options
|
@@ -32,13 +32,13 @@ module Dri
|
|
32
32
|
|
33
33
|
spinner.start
|
34
34
|
|
35
|
-
|
35
|
+
failures = api_client.fetch_all_triaged_failures(emoji: emoji, state: 'opened')
|
36
36
|
incidents = api_client.fetch_triaged_incidents(emoji: emoji)
|
37
37
|
|
38
38
|
spinner.stop
|
39
39
|
|
40
|
-
if
|
41
|
-
logger.warn "Found no
|
40
|
+
if failures.empty? && incidents.empty?
|
41
|
+
logger.warn "Found no failures nor incidents associated with \"#{emoji}\" emoji. Will exit. Bye 👋"
|
42
42
|
exit 1
|
43
43
|
end
|
44
44
|
|
@@ -58,18 +58,18 @@ module Dri
|
|
58
58
|
|
59
59
|
spinner.start
|
60
60
|
|
61
|
-
|
61
|
+
failures.each do |failure|
|
62
62
|
actions = []
|
63
63
|
|
64
64
|
if @options[:actions]
|
65
65
|
actions = prompt.multi_select(
|
66
|
-
"Please mark the actions on #{add_color(
|
66
|
+
"Please mark the actions on #{add_color(failure.title, :yellow)}: ",
|
67
67
|
action_options,
|
68
68
|
per_page: 9
|
69
69
|
)
|
70
70
|
end
|
71
71
|
|
72
|
-
report.add_failure(
|
72
|
+
report.add_failure(failure, actions)
|
73
73
|
end
|
74
74
|
|
75
75
|
unless incidents.empty?
|
@@ -180,11 +180,29 @@ module Dri
|
|
180
180
|
issues = api_client.fetch_current_triage_issue
|
181
181
|
current_issue_iid = issues.first.iid
|
182
182
|
|
183
|
-
|
183
|
+
note_action = 'posted'
|
184
|
+
posted_note = nil
|
185
|
+
if @options[:update]
|
186
|
+
report_file = File.read("handover_reports/.tmp/report-#{@date}.json")
|
187
|
+
report_json = JSON.parse(report_file) if report_file
|
188
|
+
posted_note = api_client.update_triage_report_note(
|
189
|
+
iid: current_issue_iid, note_id: report_json['id'], body: note
|
190
|
+
)
|
191
|
+
note_action = 'updated'
|
192
|
+
else
|
193
|
+
posted_note = api_client.post_triage_report_note(iid: current_issue_iid, body: note)
|
194
|
+
|
195
|
+
FileUtils.mkdir_p("#{Dir.pwd}/handover_reports/.tmp")
|
196
|
+
report_path = "handover_reports/.tmp/report-#{@date}.json"
|
197
|
+
|
198
|
+
File.open(report_path, 'w') do |out_file|
|
199
|
+
out_file.write(posted_note.to_h.to_json)
|
200
|
+
end
|
201
|
+
end
|
184
202
|
|
185
203
|
output.puts "Done! ✅\n"
|
186
204
|
logger.success(<<~MSG)
|
187
|
-
Thanks @#{username}, your report was
|
205
|
+
Thanks @#{username}, your report was #{note_action} at https://gitlab.com/gitlab-org/quality/pipeline-triage/-/issues/#{current_issue_iid}#note_#{posted_note.id} 🎉
|
188
206
|
MSG
|
189
207
|
end
|
190
208
|
|
data/lib/dri/commands/publish.rb
CHANGED
@@ -16,6 +16,9 @@ module Dri
|
|
16
16
|
desc: 'Updates actions on failures'
|
17
17
|
method_option :feature_flags, type: :boolean,
|
18
18
|
desc: 'Adds summary of feature flag changes'
|
19
|
+
method_option :update, aliases: '-u', type: :boolean,
|
20
|
+
desc: 'Updates an existing report'
|
21
|
+
|
19
22
|
def report(*)
|
20
23
|
if options[:help]
|
21
24
|
invoke :help, ['report']
|
@@ -24,7 +24,7 @@ module Dri
|
|
24
24
|
|
25
25
|
spinner.start
|
26
26
|
|
27
|
-
failures_with_award_emoji = api_client.
|
27
|
+
failures_with_award_emoji = api_client.fetch_all_triaged_failures(emoji: emoji, state: 'opened')
|
28
28
|
incidents_with_award_emoji = api_client.fetch_triaged_incidents(emoji: emoji)
|
29
29
|
|
30
30
|
issues_with_award_emoji = failures_with_award_emoji + incidents_with_award_emoji
|
@@ -1,10 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative './utils/
|
3
|
+
require_relative './utils/constants'
|
4
4
|
|
5
5
|
module Dri
|
6
6
|
class FeatureFlagReport
|
7
|
-
include Dri::Utils::
|
7
|
+
include Dri::Utils::Constants::FeatureFlag::Labels
|
8
8
|
|
9
9
|
attr_reader :header, :labels, :prod, :staging, :staging_ref, :preprod
|
10
10
|
|
data/lib/dri/report.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative './utils/constants'
|
4
|
+
|
3
5
|
module Dri
|
4
6
|
class Report # rubocop:disable Metrics/ClassLength
|
7
|
+
include Dri::Utils::Constants::Triage::Labels
|
5
8
|
using Refinements
|
6
9
|
|
7
10
|
attr_reader :header, :failures, :labels, :labels_incidents, :incidents
|
@@ -31,24 +34,23 @@ module Dri
|
|
31
34
|
service = 'N/A'
|
32
35
|
|
33
36
|
labels.each do |label|
|
34
|
-
status = label.gsub!(
|
35
|
-
service = label.gsub!(
|
37
|
+
status = label.gsub!(INCIDENT, ' ').to_s if label.include? INCIDENT
|
38
|
+
service = label.gsub!(SERVICE, ' ').to_s if label.include? SERVICE
|
36
39
|
end
|
37
40
|
|
38
41
|
@incidents << [title, service, status, url]
|
39
42
|
end
|
40
43
|
|
41
44
|
def add_failure(failure, actions_opts = [])
|
45
|
+
project_id = failure["project_id"]
|
42
46
|
iid = failure["iid"]
|
43
47
|
title = format_title(failure["title"])
|
44
48
|
link = failure["web_url"]
|
45
49
|
labels = failure["labels"]
|
46
|
-
created_at = failure["created_at"]
|
47
|
-
assignees = failure["assignees"]
|
48
50
|
description = failure["description"]
|
49
51
|
|
50
|
-
related_mrs = @api_client.fetch_related_mrs(iid)
|
51
|
-
emoji = classify_failure_emoji(created_at)
|
52
|
+
related_mrs = @api_client.fetch_related_mrs(project_id, iid)
|
53
|
+
emoji = classify_failure_emoji(failure["created_at"])
|
52
54
|
emojified_link = "#{emoji} #{link}"
|
53
55
|
|
54
56
|
stack_blob = if description.empty?
|
@@ -60,10 +62,10 @@ module Dri
|
|
60
62
|
stack_trace = ":link:[`#{stack_blob}...`](#{link}#stack-trace)"
|
61
63
|
|
62
64
|
failure_type = filter_failure_type_labels(labels)
|
63
|
-
assigned_status = assigned?(assignees)
|
65
|
+
assigned_status = assigned?(failure["assignees"])
|
64
66
|
pipelines = filter_pipeline_labels(labels)
|
65
67
|
|
66
|
-
linked_pipelines = link_pipelines(iid, pipelines, description)
|
68
|
+
linked_pipelines = link_pipelines(project_id, iid, pipelines, description)
|
67
69
|
|
68
70
|
actions_status = actions_status_template(failure_type, assigned_status, actions_opts)
|
69
71
|
actions_fixes = actions_fixes_template(related_mrs)
|
@@ -73,7 +75,7 @@ module Dri
|
|
73
75
|
|
74
76
|
private
|
75
77
|
|
76
|
-
def link_pipelines(iid, pipelines, description) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength
|
78
|
+
def link_pipelines(project_id, iid, pipelines, description) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength
|
77
79
|
linked = []
|
78
80
|
label_pipeline_map = {
|
79
81
|
'gitlab.com' => '/quality/production',
|
@@ -88,7 +90,7 @@ module Dri
|
|
88
90
|
'release' => '/quality/release'
|
89
91
|
}
|
90
92
|
|
91
|
-
failure_notes = @api_client.fetch_failure_notes(iid)
|
93
|
+
failure_notes = @api_client.fetch_failure_notes(project_id, iid)
|
92
94
|
|
93
95
|
return if pipelines.empty?
|
94
96
|
|
@@ -173,10 +175,10 @@ module Dri
|
|
173
175
|
pipelines = []
|
174
176
|
|
175
177
|
labels.each do |label|
|
176
|
-
matchers = {
|
178
|
+
matchers = { FOUND => ' ' }
|
177
179
|
|
178
|
-
if label.include?
|
179
|
-
pipeline = label.gsub(/
|
180
|
+
if label.include? FOUND
|
181
|
+
pipeline = label.gsub(/#{FOUND}/o) { |match| matchers[match] }
|
180
182
|
pipelines << pipeline.strip
|
181
183
|
end
|
182
184
|
end
|
@@ -185,7 +187,7 @@ module Dri
|
|
185
187
|
|
186
188
|
def filter_failure_type_labels(labels)
|
187
189
|
labels.each do |label|
|
188
|
-
@type = label.gsub!(
|
190
|
+
@type = label.gsub!(FAILURE, ' ').to_s if label.include? FAILURE
|
189
191
|
end
|
190
192
|
@type
|
191
193
|
end
|
data/lib/dri/utils/constants.rb
CHANGED
@@ -3,6 +3,39 @@
|
|
3
3
|
module Dri
|
4
4
|
module Utils
|
5
5
|
module Constants
|
6
|
+
module ProjectIDs
|
7
|
+
TESTCASES_PROJECT_ID = 11229385
|
8
|
+
TRIAGE_PROJECT_ID = 15291320
|
9
|
+
GITLAB_PROJECT_ID = 278964
|
10
|
+
CUSTOMERSDOT_PROJECT_ID = 2670515
|
11
|
+
FEATURE_FLAG_LOG_PROJECT_ID = 15208716
|
12
|
+
INFRA_TEAM_PROD_PROJECT_ID = 7444821
|
13
|
+
end
|
14
|
+
|
15
|
+
module Triage
|
16
|
+
module Labels
|
17
|
+
FOUND = 'found:'
|
18
|
+
FAILURE = 'failure::'
|
19
|
+
FAILURE_NEW = "#{FAILURE}new"
|
20
|
+
INCIDENT = 'Incident::'
|
21
|
+
SERVICE = 'Service::'
|
22
|
+
QA = 'QA'
|
23
|
+
QUALITY = 'Quality'
|
24
|
+
QUARANTINE = 'quarantine'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
module FeatureFlag
|
29
|
+
module Labels
|
30
|
+
PRODUCTION = 'host::gitlab.com'
|
31
|
+
STAGING = 'host::staging.gitlab.com'
|
32
|
+
STAGING_REF = 'host::staging-ref.gitlab.com'
|
33
|
+
PREPROD = 'host::pre.gitlab.com'
|
34
|
+
end
|
35
|
+
|
36
|
+
TITLE_SUBSTRINGS = ["set to \"true\"", "set to \"false\""].freeze
|
37
|
+
end
|
38
|
+
|
6
39
|
PIPELINE_ENVIRONMENTS =
|
7
40
|
{
|
8
41
|
production: {
|
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.8.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: 2022-
|
11
|
+
date: 2022-11-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: amatch
|
@@ -355,7 +355,6 @@ files:
|
|
355
355
|
- lib/dri/refinements/truncate.rb
|
356
356
|
- lib/dri/report.rb
|
357
357
|
- lib/dri/utils/constants.rb
|
358
|
-
- lib/dri/utils/feature_flag_consts.rb
|
359
358
|
- lib/dri/utils/markdown_lists.rb
|
360
359
|
- lib/dri/utils/table.rb
|
361
360
|
- lib/dri/version.rb
|
@@ -1,13 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Dri
|
4
|
-
module Utils
|
5
|
-
module FeatureFlagConsts
|
6
|
-
PRODUCTION = 'host::gitlab.com'
|
7
|
-
STAGING = 'host::staging.gitlab.com'
|
8
|
-
STAGING_REF = 'host::staging-ref.gitlab.com'
|
9
|
-
PREPROD = 'host::pre.gitlab.com'
|
10
|
-
TITLE_SUBSTRINGS = ["set to \"true\"", "set to \"false\""].freeze
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|