dri 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitlab-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
|