gitlab_quality-test_tooling 2.4.0 → 2.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/Gemfile.lock +1 -1
- data/lib/gitlab_quality/test_tooling/gitlab_client/gitlab_client.rb +4 -3
- data/lib/gitlab_quality/test_tooling/gitlab_client/issues_client.rb +39 -0
- data/lib/gitlab_quality/test_tooling/job_trace_analyzer.rb +2 -1
- data/lib/gitlab_quality/test_tooling/report/concerns/group_and_category_labels.rb +5 -11
- data/lib/gitlab_quality/test_tooling/report/concerns/issue_reports.rb +2 -2
- data/lib/gitlab_quality/test_tooling/report/concerns/utils.rb +4 -0
- data/lib/gitlab_quality/test_tooling/report/failed_test_issue.rb +9 -5
- data/lib/gitlab_quality/test_tooling/report/flaky_test_issue.rb +15 -7
- data/lib/gitlab_quality/test_tooling/report/health_problem_reporter.rb +18 -7
- data/lib/gitlab_quality/test_tooling/report/knapsack_report_issue.rb +4 -0
- data/lib/gitlab_quality/test_tooling/report/relate_failure_issue.rb +170 -9
- data/lib/gitlab_quality/test_tooling/report/report_as_issue.rb +3 -0
- data/lib/gitlab_quality/test_tooling/report/slow_test_issue.rb +11 -3
- data/lib/gitlab_quality/test_tooling/runtime/env.rb +3 -1
- data/lib/gitlab_quality/test_tooling/test_result/base_test_result.rb +9 -1
- data/lib/gitlab_quality/test_tooling/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8547ed9407707737f7d776f263543f3681e19706d7705591968f2e0ec348ed88
|
|
4
|
+
data.tar.gz: 84f1fb8007ce5c5dd96a1782cfa7b75207d456df2b9c163ddde5900f3efe6ad2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 43e26da079892ae22a44025b4c96a076b65499e1ab1a5574da130132e9c70358ed12ae9ba123bb645b757b8e177bb59c11fd036f5e8d9d14e22620c5c0616958
|
|
7
|
+
data.tar.gz: f236ba27bc98161951cc4d98e4ff22321eeee476b6e305c0b67e2d58b09f6329a0361c9d834c20ff3efa86b41f5763733eb278c755fffa1e5fa49d25c556c1a9
|
data/Gemfile.lock
CHANGED
|
@@ -9,10 +9,11 @@ module GitlabQuality
|
|
|
9
9
|
RETRY_BACK_OFF_DELAY = 60
|
|
10
10
|
MAX_RETRY_ATTEMPTS = 3
|
|
11
11
|
|
|
12
|
-
def initialize(token:, project:, **_kwargs)
|
|
12
|
+
def initialize(token:, project:, endpoint: nil, **_kwargs)
|
|
13
13
|
@token = token
|
|
14
14
|
@project = project
|
|
15
15
|
@retry_backoff = 0
|
|
16
|
+
@endpoint = endpoint
|
|
16
17
|
end
|
|
17
18
|
|
|
18
19
|
def handle_gitlab_client_exceptions
|
|
@@ -76,11 +77,11 @@ module GitlabQuality
|
|
|
76
77
|
|
|
77
78
|
private
|
|
78
79
|
|
|
79
|
-
attr_reader :project, :token
|
|
80
|
+
attr_reader :project, :token, :endpoint
|
|
80
81
|
|
|
81
82
|
def client
|
|
82
83
|
@client ||= Gitlab.client(
|
|
83
|
-
endpoint: Runtime::Env.gitlab_api_base,
|
|
84
|
+
endpoint: endpoint || Runtime::Env.gitlab_api_base,
|
|
84
85
|
private_token: token
|
|
85
86
|
)
|
|
86
87
|
end
|
|
@@ -122,6 +122,45 @@ module GitlabQuality
|
|
|
122
122
|
end
|
|
123
123
|
end
|
|
124
124
|
|
|
125
|
+
def find_pipeline(project, pipeline_id)
|
|
126
|
+
handle_gitlab_client_exceptions do
|
|
127
|
+
client.pipeline(project, pipeline_id)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def find_commit(project, sha)
|
|
132
|
+
handle_gitlab_client_exceptions do
|
|
133
|
+
client.commit(project, sha)
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def find_commit_parent(project, sha)
|
|
138
|
+
handle_gitlab_client_exceptions do
|
|
139
|
+
# In a merged results commit, the first parent is the one from
|
|
140
|
+
# the main branch, and the second parent is from the branch
|
|
141
|
+
# itself (more likely to have caused the issue)
|
|
142
|
+
client.commit(project, sha).parent_ids.last
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def find_commit_diff(project, sha)
|
|
147
|
+
handle_gitlab_client_exceptions do
|
|
148
|
+
client.commit_diff(project, sha)
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def find_deployments(project, environment:, status:, order_by: 'id', sort: 'desc')
|
|
153
|
+
handle_gitlab_client_exceptions do
|
|
154
|
+
client.deployments(
|
|
155
|
+
project,
|
|
156
|
+
environment: environment,
|
|
157
|
+
status: status,
|
|
158
|
+
order_by: order_by,
|
|
159
|
+
sort: sort
|
|
160
|
+
)
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
125
164
|
private
|
|
126
165
|
|
|
127
166
|
attr_reader :token, :project
|
|
@@ -67,7 +67,8 @@ module GitlabQuality
|
|
|
67
67
|
'segmentation fault',
|
|
68
68
|
'no space left on device',
|
|
69
69
|
'Check free disk space',
|
|
70
|
-
'CLUSTERDOWN'
|
|
70
|
+
'CLUSTERDOWN',
|
|
71
|
+
'Redis client could not fetch cluster information: Connection refused'
|
|
71
72
|
],
|
|
72
73
|
flaky_test: [
|
|
73
74
|
"We have detected a PG::QueryCanceled error in the specs, so we're failing early"
|
|
@@ -5,20 +5,14 @@ module GitlabQuality
|
|
|
5
5
|
module Report
|
|
6
6
|
module Concerns
|
|
7
7
|
module GroupAndCategoryLabels
|
|
8
|
-
def
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def new_issue_labels(test)
|
|
13
|
-
debug_line = ' => [DEBUG] '
|
|
14
|
-
debug_line += "product_group: #{test&.product_group}; " if test.respond_to?(:product_group)
|
|
15
|
-
debug_line += "feature_category: #{test&.feature_category}" if test.respond_to?(:feature_category)
|
|
16
|
-
puts debug_line
|
|
8
|
+
def group_and_category_labels_for_test(test)
|
|
9
|
+
labels_inference = GitlabQuality::TestTooling::LabelsInference.new
|
|
10
|
+
new_labels = Set.new
|
|
17
11
|
|
|
18
|
-
new_labels = self.class::NEW_ISSUE_LABELS
|
|
19
12
|
new_labels += labels_inference.infer_labels_from_product_group(test.product_group) if test.respond_to?(:product_group)
|
|
20
13
|
new_labels += labels_inference.infer_labels_from_feature_category(test.feature_category) if test.respond_to?(:feature_category)
|
|
21
|
-
|
|
14
|
+
|
|
15
|
+
new_labels
|
|
22
16
|
end
|
|
23
17
|
end
|
|
24
18
|
end
|
|
@@ -113,11 +113,11 @@ module GitlabQuality
|
|
|
113
113
|
end
|
|
114
114
|
end
|
|
115
115
|
|
|
116
|
-
def initial_reports_section(test)
|
|
116
|
+
def initial_reports_section(test, item_extra_content: nil)
|
|
117
117
|
<<~REPORTS
|
|
118
118
|
### Reports (1)
|
|
119
119
|
|
|
120
|
-
#{ReportsList.report_list_item(test)}
|
|
120
|
+
#{ReportsList.report_list_item(test, item_extra_content: item_extra_content)}
|
|
121
121
|
REPORTS
|
|
122
122
|
end
|
|
123
123
|
|
|
@@ -15,6 +15,10 @@ module GitlabQuality
|
|
|
15
15
|
"#{title[...MAX_TITLE_LENGTH - 3]}..."
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
+
def label_names_to_label_quick_action(label_names)
|
|
19
|
+
%(/label #{label_names.map { |label| %(~"#{label}") }.join(' ')})
|
|
20
|
+
end
|
|
21
|
+
|
|
18
22
|
def new_issue_title(test)
|
|
19
23
|
"[Test] #{partial_file_path(test.file)} | #{search_safe(test.name)}".strip
|
|
20
24
|
end
|
|
@@ -57,7 +57,7 @@ module GitlabQuality
|
|
|
57
57
|
current_reports_note = find_failure_discussion_note(issue: issue, test: test, reports_discussion: reports_discussion)
|
|
58
58
|
|
|
59
59
|
new_reports_list = new_reports_list(current_reports_note: current_reports_note, test: test)
|
|
60
|
-
note_body =
|
|
60
|
+
note_body = append_quick_actions_to_note(
|
|
61
61
|
new_reports_list: new_reports_list,
|
|
62
62
|
related_issues: related_issues,
|
|
63
63
|
options: {
|
|
@@ -105,6 +105,14 @@ module GitlabQuality
|
|
|
105
105
|
IDENTITY_LABELS
|
|
106
106
|
end
|
|
107
107
|
|
|
108
|
+
def new_issue_labels(test)
|
|
109
|
+
up_to_date_labels(test: test, new_labels: NEW_ISSUE_LABELS + group_and_category_labels_for_test(test))
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def up_to_date_labels(test:, issue: nil, new_labels: Set.new)
|
|
113
|
+
(base_issue_labels + super).to_a
|
|
114
|
+
end
|
|
115
|
+
|
|
108
116
|
def report_section_header
|
|
109
117
|
REPORT_SECTION_HEADER
|
|
110
118
|
end
|
|
@@ -139,10 +147,6 @@ module GitlabQuality
|
|
|
139
147
|
quick_actions.join("\n")
|
|
140
148
|
end
|
|
141
149
|
|
|
142
|
-
def up_to_date_labels(test:, issue: nil, new_labels: Set.new)
|
|
143
|
-
(base_issue_labels + super).to_a
|
|
144
|
-
end
|
|
145
|
-
|
|
146
150
|
def find_failure_discussion_note(issue:, test:, reports_discussion:)
|
|
147
151
|
return unless reports_discussion
|
|
148
152
|
|
|
@@ -44,6 +44,14 @@ module GitlabQuality
|
|
|
44
44
|
IDENTITY_LABELS
|
|
45
45
|
end
|
|
46
46
|
|
|
47
|
+
def new_issue_labels(test)
|
|
48
|
+
up_to_date_labels(test: test, new_labels: NEW_ISSUE_LABELS)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def up_to_date_labels(test:, issue: nil, new_labels: Set.new)
|
|
52
|
+
(base_issue_labels + super).to_a
|
|
53
|
+
end
|
|
54
|
+
|
|
47
55
|
def report_section_header
|
|
48
56
|
REPORT_SECTION_HEADER
|
|
49
57
|
end
|
|
@@ -52,22 +60,22 @@ module GitlabQuality
|
|
|
52
60
|
REPORTS_DOCUMENTATION
|
|
53
61
|
end
|
|
54
62
|
|
|
55
|
-
def health_problem_status_label_quick_action(reports_list,
|
|
63
|
+
def health_problem_status_label_quick_action(reports_list, options: {})
|
|
56
64
|
case reports_list.reports_count
|
|
57
65
|
when 399..Float::INFINITY
|
|
58
|
-
'
|
|
66
|
+
label_names = Set.new(['flakiness::1'])
|
|
67
|
+
label_names += group_and_category_labels_for_test(options[:test]) if options.key?(:test)
|
|
68
|
+
label_names_to_label_quick_action(label_names)
|
|
59
69
|
when 37..398
|
|
60
|
-
'
|
|
70
|
+
label_names = Set.new(['flakiness::2'])
|
|
71
|
+
label_names += group_and_category_labels_for_test(options[:test]) if options.key?(:test)
|
|
72
|
+
label_names_to_label_quick_action(label_names)
|
|
61
73
|
when 13..36
|
|
62
74
|
'/label ~"flakiness::3"'
|
|
63
75
|
else
|
|
64
76
|
'/label ~"flakiness::4"'
|
|
65
77
|
end
|
|
66
78
|
end
|
|
67
|
-
|
|
68
|
-
def up_to_date_labels(test:, issue: nil, new_labels: Set.new)
|
|
69
|
-
(base_issue_labels + super).to_a
|
|
70
|
-
end
|
|
71
79
|
end
|
|
72
80
|
end
|
|
73
81
|
end
|
|
@@ -45,6 +45,10 @@ module GitlabQuality
|
|
|
45
45
|
[]
|
|
46
46
|
end
|
|
47
47
|
|
|
48
|
+
def new_issue_labels(_test)
|
|
49
|
+
[]
|
|
50
|
+
end
|
|
51
|
+
|
|
48
52
|
def search_labels
|
|
49
53
|
BASE_SEARCH_LABELS
|
|
50
54
|
end
|
|
@@ -57,10 +61,6 @@ module GitlabQuality
|
|
|
57
61
|
''
|
|
58
62
|
end
|
|
59
63
|
|
|
60
|
-
def health_problem_status_label_quick_action(*)
|
|
61
|
-
''
|
|
62
|
-
end
|
|
63
|
-
|
|
64
64
|
def item_extra_content(_test)
|
|
65
65
|
found_label
|
|
66
66
|
end
|
|
@@ -115,7 +115,13 @@ module GitlabQuality
|
|
|
115
115
|
current_reports_note = existing_reports_note(issue_iid: issue.iid)
|
|
116
116
|
|
|
117
117
|
new_reports_list = new_reports_list(current_reports_note: current_reports_note, test: test)
|
|
118
|
-
note_body =
|
|
118
|
+
note_body = append_quick_actions_to_note(
|
|
119
|
+
new_reports_list: new_reports_list,
|
|
120
|
+
related_issues: related_issues,
|
|
121
|
+
options: {
|
|
122
|
+
test: test
|
|
123
|
+
}
|
|
124
|
+
)
|
|
119
125
|
|
|
120
126
|
if current_reports_note
|
|
121
127
|
gitlab.edit_issue_note(
|
|
@@ -138,7 +144,7 @@ module GitlabQuality
|
|
|
138
144
|
)
|
|
139
145
|
end
|
|
140
146
|
|
|
141
|
-
def
|
|
147
|
+
def append_quick_actions_to_note(new_reports_list:, related_issues:, options: {})
|
|
142
148
|
report = new_reports_list
|
|
143
149
|
|
|
144
150
|
quick_actions = [
|
|
@@ -164,10 +170,15 @@ module GitlabQuality
|
|
|
164
170
|
end
|
|
165
171
|
end
|
|
166
172
|
|
|
173
|
+
# Defined in subclasses
|
|
174
|
+
def health_problem_status_label_quick_action(*)
|
|
175
|
+
''
|
|
176
|
+
end
|
|
177
|
+
|
|
167
178
|
def identity_labels_quick_action
|
|
168
179
|
return if identity_labels.empty?
|
|
169
180
|
|
|
170
|
-
|
|
181
|
+
label_names_to_label_quick_action(identity_labels)
|
|
171
182
|
end
|
|
172
183
|
|
|
173
184
|
def relate_issues_quick_actions(issues)
|
|
@@ -39,6 +39,10 @@ module GitlabQuality
|
|
|
39
39
|
search_and_create_issue
|
|
40
40
|
end
|
|
41
41
|
|
|
42
|
+
def new_issue_labels(test)
|
|
43
|
+
up_to_date_labels(test: test, new_labels: NEW_ISSUE_LABELS + group_and_category_labels_for_test(test))
|
|
44
|
+
end
|
|
45
|
+
|
|
42
46
|
def search_and_create_issue
|
|
43
47
|
filtered_report = KnapsackReports::SpecRunTimeReport.new(
|
|
44
48
|
token: token,
|
|
@@ -26,6 +26,28 @@ module GitlabQuality
|
|
|
26
26
|
NEW_ISSUE_LABELS = Set.new(%w[test failure::new priority::2 automation:bot-authored]).freeze
|
|
27
27
|
SCREENSHOT_IGNORED_ERRORS = ['500 Internal Server Error', 'fabricate_via_api!', 'Error Code 500'].freeze
|
|
28
28
|
|
|
29
|
+
# Map commits to security fork (gitlab-org/security/gitlab) for gitlab-org/gitlab since security patches exist
|
|
30
|
+
# there before being released to the public repository
|
|
31
|
+
DIFF_PROJECT_MAPPINGS = {
|
|
32
|
+
'gitlab-org/gitlab' => 'gitlab-org/security/gitlab',
|
|
33
|
+
'gitlab-org/customers-gitlab-com' => 'gitlab-org/customers-gitlab-com'
|
|
34
|
+
}.freeze
|
|
35
|
+
|
|
36
|
+
# The project contains record of the deployments we use to determine the commit diff
|
|
37
|
+
OPS_RELEASES_METADATA_PROJECT = 'gitlab-org/release/metadata'
|
|
38
|
+
|
|
39
|
+
# Naming of the environments are different between `gitlab-org/release/metadata` and `gitlab-org/quality`
|
|
40
|
+
# This maps the gitlab-org/quality environment names to the equivalent in `gitlab-org/release/metadata`
|
|
41
|
+
ENVIRONMENT_MAPPING = {
|
|
42
|
+
'production' => 'gprd',
|
|
43
|
+
'canary' => 'gprd-cny',
|
|
44
|
+
'staging' => 'gstg',
|
|
45
|
+
'staging-canary' => 'gstg-cny',
|
|
46
|
+
'staging-ref' => 'gstg-ref',
|
|
47
|
+
'preprod' => 'pre',
|
|
48
|
+
'release' => 'release'
|
|
49
|
+
}.freeze
|
|
50
|
+
|
|
29
51
|
MultipleIssuesFound = Class.new(StandardError)
|
|
30
52
|
|
|
31
53
|
def initialize(
|
|
@@ -47,7 +69,7 @@ module GitlabQuality
|
|
|
47
69
|
|
|
48
70
|
private
|
|
49
71
|
|
|
50
|
-
attr_reader :max_diff_ratio, :system_logs, :base_issue_labels, :exclude_labels_for_search, :metrics_files
|
|
72
|
+
attr_reader :max_diff_ratio, :system_logs, :base_issue_labels, :exclude_labels_for_search, :metrics_files, :ops_gitlab_client
|
|
51
73
|
|
|
52
74
|
def run!
|
|
53
75
|
puts "Reporting test failures in `#{files.join(',')}` as issues in project `#{project}` via the API at `#{Runtime::Env.gitlab_api_base}`."
|
|
@@ -58,6 +80,14 @@ module GitlabQuality
|
|
|
58
80
|
end
|
|
59
81
|
end
|
|
60
82
|
|
|
83
|
+
def new_issue_labels(test)
|
|
84
|
+
up_to_date_labels(test: test, new_labels: NEW_ISSUE_LABELS + group_and_category_labels_for_test(test))
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def up_to_date_labels(test:, issue: nil, new_labels: Set.new)
|
|
88
|
+
(Set.new(base_issue_labels) + (super << pipeline_name_label)).to_a
|
|
89
|
+
end
|
|
90
|
+
|
|
61
91
|
def test_metric_collections
|
|
62
92
|
@test_metric_collections ||= Dir.glob(metrics_files).map do |path|
|
|
63
93
|
TestMetrics::JsonTestMetricCollection.new(path)
|
|
@@ -106,7 +136,7 @@ module GitlabQuality
|
|
|
106
136
|
begin
|
|
107
137
|
issue = find_issue_and_update_reports(test)
|
|
108
138
|
|
|
109
|
-
issue = create_issue(test) unless issue || test.quarantine?
|
|
139
|
+
issue = create_issue(test) unless issue || (test.quarantine? && !test.conditional_quarantine?)
|
|
110
140
|
|
|
111
141
|
issue
|
|
112
142
|
rescue MultipleIssuesFound => e
|
|
@@ -271,14 +301,142 @@ module GitlabQuality
|
|
|
271
301
|
|
|
272
302
|
def new_issue_description(test)
|
|
273
303
|
super + [
|
|
274
|
-
"\n
|
|
304
|
+
"\n#{commit_diff_section}",
|
|
305
|
+
"### Stack trace",
|
|
275
306
|
"```\n#{test.full_stacktrace}\n```",
|
|
276
307
|
screenshot_section(test),
|
|
277
308
|
system_log_errors_section(test),
|
|
278
|
-
initial_reports_section(test)
|
|
309
|
+
initial_reports_section(test, item_extra_content: screenshot_artifact_url(test))
|
|
279
310
|
].compact.join("\n\n")
|
|
280
311
|
end
|
|
281
312
|
|
|
313
|
+
def commit_diff_section
|
|
314
|
+
"### Commit diff\n#{generate_diff_link}"
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
def generate_diff_link
|
|
318
|
+
initialize_gitlab_ops_client
|
|
319
|
+
|
|
320
|
+
if Runtime::Env.ci_pipeline_url.include?('ops.gitlab.net')
|
|
321
|
+
pipeline = ops_gitlab_client.find_pipeline(project, Runtime::Env.ci_pipeline_id.to_i)
|
|
322
|
+
generate_ops_gitlab_diff(pipeline)
|
|
323
|
+
else
|
|
324
|
+
pipeline = gitlab.find_pipeline(project, Runtime::Env.ci_pipeline_id.to_i)
|
|
325
|
+
generate_gitlab_diff(pipeline)
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
def generate_ops_gitlab_diff(pipeline)
|
|
330
|
+
deployment_info = fetch_deployment_info(pipeline)
|
|
331
|
+
|
|
332
|
+
return deployment_info if deployment_info.is_a?(String)
|
|
333
|
+
|
|
334
|
+
source_gitlab_ee_sha = fetch_deployment_gitlab_ee_sha(ops_gitlab_client, deployment_info[:source].sha)
|
|
335
|
+
target_gitlab_ee_sha = fetch_deployment_gitlab_ee_sha(ops_gitlab_client, deployment_info[:target].sha)
|
|
336
|
+
|
|
337
|
+
if source_gitlab_ee_sha == target_gitlab_ee_sha
|
|
338
|
+
"No diff"
|
|
339
|
+
else
|
|
340
|
+
"https://gitlab.com/gitlab-org/security/gitlab/-/compare/#{target_gitlab_ee_sha}...#{source_gitlab_ee_sha}"
|
|
341
|
+
end
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
def fetch_deployment_info(pipeline)
|
|
345
|
+
pipeline_deploy_version = Runtime::Env.ci_pipeline_name.match(/(\d+\.\d+\.\d+)(?:-|$)/)&.captures&.first
|
|
346
|
+
deployments = fetch_deployments(ops_gitlab_client, pipeline)
|
|
347
|
+
found_deployment = find_matching_deployment(pipeline_deploy_version, deployments)
|
|
348
|
+
|
|
349
|
+
return 'No matching deployment found to generate a diff link.' unless found_deployment
|
|
350
|
+
|
|
351
|
+
{
|
|
352
|
+
source: found_deployment[:deployment],
|
|
353
|
+
target: deployments[found_deployment[:index] + 1]
|
|
354
|
+
}
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
def fetch_deployments(client, pipeline)
|
|
358
|
+
ops_environment = pipeline.web_url.match(%r{gitlab-org/quality/([^/-]+(?:-[^/-]+)*?)(?:/-)?/pipelines})[1]
|
|
359
|
+
|
|
360
|
+
raise "Environment '#{ops_environment}' is not supported" unless ENVIRONMENT_MAPPING.key?(ops_environment)
|
|
361
|
+
|
|
362
|
+
environment = ENVIRONMENT_MAPPING[ops_environment]
|
|
363
|
+
|
|
364
|
+
client.find_deployments(
|
|
365
|
+
OPS_RELEASES_METADATA_PROJECT,
|
|
366
|
+
environment: environment,
|
|
367
|
+
status: 'success'
|
|
368
|
+
)
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
def initialize_gitlab_ops_client
|
|
372
|
+
@ops_gitlab_client = GitlabClient::IssuesClient.new(
|
|
373
|
+
endpoint: Runtime::Env.ci_api_v4_url,
|
|
374
|
+
token: Runtime::Env.gitlab_ci_token,
|
|
375
|
+
project: OPS_RELEASES_METADATA_PROJECT
|
|
376
|
+
)
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
def find_matching_deployment(pipeline_deploy_version, deployments)
|
|
380
|
+
return nil unless pipeline_deploy_version
|
|
381
|
+
return nil unless deployments
|
|
382
|
+
|
|
383
|
+
target_timestamp = extract_timestamp(pipeline_deploy_version)
|
|
384
|
+
return nil unless target_timestamp
|
|
385
|
+
|
|
386
|
+
matching_deployment = nil
|
|
387
|
+
|
|
388
|
+
deployments.each_with_index do |deployment, index|
|
|
389
|
+
deployment_version = extract_deployment_version(ops_gitlab_client, deployment)
|
|
390
|
+
next unless deployment_version
|
|
391
|
+
|
|
392
|
+
deployment_timestamp = extract_timestamp(deployment_version)
|
|
393
|
+
next unless deployment_timestamp
|
|
394
|
+
|
|
395
|
+
# Stop searching if the deployment timestamp is older than the target timestamp
|
|
396
|
+
break if deployment_timestamp && deployment_timestamp < target_timestamp
|
|
397
|
+
|
|
398
|
+
if deployment_version == pipeline_deploy_version
|
|
399
|
+
matching_deployment = { deployment: deployment, index: index }
|
|
400
|
+
break
|
|
401
|
+
end
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
matching_deployment
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
def extract_timestamp(version)
|
|
408
|
+
version.match(/\d{12}$/)&.to_s
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
def extract_deployment_version(client, deployment)
|
|
412
|
+
commit = client.find_commit(OPS_RELEASES_METADATA_PROJECT, deployment.sha)
|
|
413
|
+
version_match = commit.message&.match(/Product-Version: ([\d.]+)/)
|
|
414
|
+
version_match&.[](1)
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
def fetch_deployment_gitlab_ee_sha(client, deployment_sha)
|
|
418
|
+
commit_diff = client.find_commit_diff(OPS_RELEASES_METADATA_PROJECT, deployment_sha).first
|
|
419
|
+
diffs_content = commit_diff.diff.lines.select { |line| line.start_with?('+') }.map { |line| line[1..] }.join
|
|
420
|
+
|
|
421
|
+
begin
|
|
422
|
+
JSON.parse(diffs_content).dig('releases', 'gitlab-ee', 'sha')
|
|
423
|
+
rescue JSON::ParserError
|
|
424
|
+
raise "Failed to parse the diffs content"
|
|
425
|
+
end
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
def generate_gitlab_diff(pipeline)
|
|
429
|
+
pipeline_sha = pipeline.sha
|
|
430
|
+
parent_sha = gitlab.find_commit_parent(project, pipeline_sha)
|
|
431
|
+
diff_project = if DIFF_PROJECT_MAPPINGS.key?(project)
|
|
432
|
+
DIFF_PROJECT_MAPPINGS[project]
|
|
433
|
+
else
|
|
434
|
+
raise "Project #{project} is not supported for commit diff links"
|
|
435
|
+
end
|
|
436
|
+
|
|
437
|
+
"https://gitlab.com/#{diff_project}/-/compare/#{parent_sha}...#{pipeline_sha}"
|
|
438
|
+
end
|
|
439
|
+
|
|
282
440
|
def system_log_errors_section(test)
|
|
283
441
|
correlation_id = test.failures.first['correlation_id']
|
|
284
442
|
section = ''
|
|
@@ -298,10 +456,6 @@ module GitlabQuality
|
|
|
298
456
|
section
|
|
299
457
|
end
|
|
300
458
|
|
|
301
|
-
def up_to_date_labels(test:, issue: nil, new_labels: Set.new)
|
|
302
|
-
(Set.new(base_issue_labels) + (super << pipeline_name_label)).to_a
|
|
303
|
-
end
|
|
304
|
-
|
|
305
459
|
def new_issue_assignee_id(test)
|
|
306
460
|
return unless test.product_group?
|
|
307
461
|
|
|
@@ -322,7 +476,7 @@ module GitlabQuality
|
|
|
322
476
|
state_event = issue.state == 'closed' ? 'reopen' : nil
|
|
323
477
|
|
|
324
478
|
issue_attrs = {
|
|
325
|
-
description: increment_reports(current_reports_content: issue.description, test: test).to_s,
|
|
479
|
+
description: increment_reports(current_reports_content: issue.description, test: test, item_extra_content: screenshot_artifact_url(test)).to_s,
|
|
326
480
|
labels: up_to_date_labels(test: test, issue: issue)
|
|
327
481
|
}
|
|
328
482
|
issue_attrs[:state_event] = state_event if state_event
|
|
@@ -386,6 +540,13 @@ module GitlabQuality
|
|
|
386
540
|
.filter_map { |e| failure_to_ignore.find { |m| e['message'].include?(m) } }
|
|
387
541
|
.compact
|
|
388
542
|
end
|
|
543
|
+
|
|
544
|
+
def screenshot_artifact_url(test)
|
|
545
|
+
ci_job_url = test.ci_job_url
|
|
546
|
+
screenshot_path = test.screenshot_image[%r{qa/.*$}]
|
|
547
|
+
|
|
548
|
+
"|| [Screenshot](#{ci_job_url}/artifacts/file/#{screenshot_path})"
|
|
549
|
+
end
|
|
389
550
|
end
|
|
390
551
|
end
|
|
391
552
|
end
|
|
@@ -144,6 +144,9 @@ module GitlabQuality
|
|
|
144
144
|
gitlab.edit_issue(iid: issue.iid, options: { labels: labels.to_a })
|
|
145
145
|
end
|
|
146
146
|
|
|
147
|
+
# Infer labels from the test, and optionally from the issue and new_labels in arguments
|
|
148
|
+
#
|
|
149
|
+
# Called when we're updating a test health issue with a new test report.
|
|
147
150
|
def up_to_date_labels(test:, issue: nil, new_labels: Set.new)
|
|
148
151
|
labels = issue_labels(issue)
|
|
149
152
|
labels |= new_labels.to_set
|
|
@@ -34,6 +34,10 @@ module GitlabQuality
|
|
|
34
34
|
IDENTITY_LABELS
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
+
def new_issue_labels(test)
|
|
38
|
+
up_to_date_labels(test: test, new_labels: NEW_ISSUE_LABELS)
|
|
39
|
+
end
|
|
40
|
+
|
|
37
41
|
def report_section_header
|
|
38
42
|
REPORT_SECTION_HEADER
|
|
39
43
|
end
|
|
@@ -42,12 +46,16 @@ module GitlabQuality
|
|
|
42
46
|
REPORTS_DOCUMENTATION
|
|
43
47
|
end
|
|
44
48
|
|
|
45
|
-
def health_problem_status_label_quick_action(reports_list,
|
|
49
|
+
def health_problem_status_label_quick_action(reports_list, options: {})
|
|
46
50
|
case reports_list.reports_count
|
|
47
51
|
when 6099..Float::INFINITY
|
|
48
|
-
'
|
|
52
|
+
label_names = Set.new(['slowness::1'])
|
|
53
|
+
label_names += group_and_category_labels_for_test(options[:test]) if options.key?(:test)
|
|
54
|
+
label_names_to_label_quick_action(label_names)
|
|
49
55
|
when 2177..6098
|
|
50
|
-
'
|
|
56
|
+
label_names = Set.new(['slowness::2'])
|
|
57
|
+
label_names += group_and_category_labels_for_test(options[:test]) if options.key?(:test)
|
|
58
|
+
label_names_to_label_quick_action(label_names)
|
|
51
59
|
when 521..2176
|
|
52
60
|
'/label ~"slowness::3"'
|
|
53
61
|
else
|
|
@@ -19,9 +19,11 @@ module GitlabQuality
|
|
|
19
19
|
'CI_PROJECT_NAME' => :ci_project_name,
|
|
20
20
|
'CI_PROJECT_PATH' => :ci_project_path,
|
|
21
21
|
'CI_PIPELINE_ID' => :ci_pipeline_id,
|
|
22
|
+
'CI_PIPELINE_NAME' => :ci_pipeline_name,
|
|
22
23
|
'CI_PIPELINE_URL' => :ci_pipeline_url,
|
|
23
24
|
'SLACK_QA_CHANNEL' => :slack_qa_channel,
|
|
24
|
-
'DEPLOY_VERSION' => :deploy_version
|
|
25
|
+
'DEPLOY_VERSION' => :deploy_version,
|
|
26
|
+
'QA_GITLAB_CI_TOKEN' => :gitlab_ci_token
|
|
25
27
|
}.freeze
|
|
26
28
|
|
|
27
29
|
ENV_VARIABLES.each do |env_name, method_name|
|
|
@@ -11,7 +11,9 @@ module GitlabQuality
|
|
|
11
11
|
"unexpected token at 'GitLab is not responding'",
|
|
12
12
|
"GitLab: Internal API error (502).",
|
|
13
13
|
"could not be found (502)",
|
|
14
|
-
"Error reference number: 502"
|
|
14
|
+
"Error reference number: 502",
|
|
15
|
+
"(502): `GitLab is not responding`",
|
|
16
|
+
"<head><title>502 Bad Gateway</title></head>"
|
|
15
17
|
].freeze
|
|
16
18
|
|
|
17
19
|
SHARED_EXAMPLES_CALLERS = %w[include_examples it_behaves_like].freeze
|
|
@@ -100,6 +102,12 @@ module GitlabQuality
|
|
|
100
102
|
!!quarantine
|
|
101
103
|
end
|
|
102
104
|
|
|
105
|
+
def conditional_quarantine?
|
|
106
|
+
return true if quarantine? && quarantine.is_a?(Hash) && quarantine.has_key?('only')
|
|
107
|
+
|
|
108
|
+
false
|
|
109
|
+
end
|
|
110
|
+
|
|
103
111
|
def file
|
|
104
112
|
@file ||= relative_file.start_with?('qa/') ? "qa/#{relative_file}" : relative_file
|
|
105
113
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: gitlab_quality-test_tooling
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.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:
|
|
11
|
+
date: 2025-02-18 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: climate_control
|