gitlab_quality-test_tooling 2.10.0 → 2.15.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/Gemfile.lock +2 -2
- data/README.md +22 -5
- data/exe/{feature-readiness-check → feature-readiness-checklist} +2 -2
- data/exe/feature-readiness-evaluation +62 -0
- data/lib/gitlab_quality/test_tooling/feature_readiness/analyzed_items/analyzed_epic.rb +94 -0
- data/lib/gitlab_quality/test_tooling/feature_readiness/analyzed_items/analyzed_issue.rb +92 -0
- data/lib/gitlab_quality/test_tooling/feature_readiness/analyzed_items/analyzed_merge_request.rb +139 -0
- data/lib/gitlab_quality/test_tooling/feature_readiness/concerns/work_item_concern.rb +26 -12
- data/lib/gitlab_quality/test_tooling/feature_readiness/evaluation.rb +82 -0
- data/lib/gitlab_quality/test_tooling/feature_readiness/operational_readiness_check.rb +4 -4
- data/lib/gitlab_quality/test_tooling/gitlab_client/issues_client.rb +7 -1
- data/lib/gitlab_quality/test_tooling/gitlab_client/issues_dry_client.rb +1 -1
- data/lib/gitlab_quality/test_tooling/gitlab_client/merge_requests_client.rb +21 -0
- data/lib/gitlab_quality/test_tooling/gitlab_client/merge_requests_dry_client.rb +0 -10
- data/lib/gitlab_quality/test_tooling/gitlab_client/work_items_client.rb +71 -34
- data/lib/gitlab_quality/test_tooling/report/concerns/results_reporter.rb +1 -1
- data/lib/gitlab_quality/test_tooling/report/concerns/utils.rb +3 -3
- data/lib/gitlab_quality/test_tooling/report/feature_readiness/report_on_epic.rb +174 -0
- data/lib/gitlab_quality/test_tooling/report/generate_test_session.rb +1 -1
- data/lib/gitlab_quality/test_tooling/report/relate_failure_issue.rb +21 -7
- data/lib/gitlab_quality/test_tooling/runtime/env.rb +1 -1
- data/lib/gitlab_quality/test_tooling/test_meta/processor/add_to_blocking_processor.rb +1 -1
- data/lib/gitlab_quality/test_tooling/test_meta/processor/add_to_quarantine_processor.rb +1 -1
- data/lib/gitlab_quality/test_tooling/test_result/base_test_result.rb +13 -4
- data/lib/gitlab_quality/test_tooling/version.rb +1 -1
- metadata +21 -8
@@ -72,6 +72,12 @@ module GitlabQuality
|
|
72
72
|
end
|
73
73
|
end
|
74
74
|
|
75
|
+
def related_merge_requests(iid:)
|
76
|
+
handle_gitlab_client_exceptions do
|
77
|
+
client.related_merge_requests(project, iid).auto_paginate
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
75
81
|
def find_issue_discussions(iid:)
|
76
82
|
handle_gitlab_client_exceptions do
|
77
83
|
client.issue_discussions(project, iid, order_by: 'created_at', sort: 'asc').auto_paginate
|
@@ -169,7 +175,7 @@ module GitlabQuality
|
|
169
175
|
def find_commit_parent(project, sha)
|
170
176
|
handle_gitlab_client_exceptions do
|
171
177
|
# In a merged results commit, the first parent is the one from
|
172
|
-
# the
|
178
|
+
# the default branch, and the second parent is from the branch
|
173
179
|
# itself (more likely to have caused the issue)
|
174
180
|
client.commit(project, sha).parent_ids.last
|
175
181
|
end
|
@@ -4,7 +4,7 @@ module GitlabQuality
|
|
4
4
|
module TestTooling
|
5
5
|
module GitlabClient
|
6
6
|
class IssuesDryClient < IssuesClient
|
7
|
-
def create_issue(title:, description:, labels:, issue_type: 'issue', confidential: false)
|
7
|
+
def create_issue(title:, description:, labels:, issue_type: 'issue', _assignee_id: nil, _due_date: nil, confidential: false)
|
8
8
|
attrs = { description: description, labels: labels, confidential: confidential }
|
9
9
|
|
10
10
|
puts "The following #{issue_type} would have been created:"
|
@@ -1,5 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'gitlab'
|
4
|
+
|
5
|
+
module Gitlab
|
6
|
+
# Monkey patch the Gitlab client to allow passing query options
|
7
|
+
class Client
|
8
|
+
def merge_request_diffs(project, merge_request_iid, options = {})
|
9
|
+
get("/projects/#{url_encode(project)}/merge_requests/#{merge_request_iid}/diffs", query: options).auto_paginate
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
3
14
|
module GitlabQuality
|
4
15
|
module TestTooling
|
5
16
|
module GitlabClient
|
@@ -10,6 +21,12 @@ module GitlabQuality
|
|
10
21
|
end
|
11
22
|
end
|
12
23
|
|
24
|
+
def merge_request_diffs(merge_request_iid:)
|
25
|
+
handle_gitlab_client_exceptions do
|
26
|
+
client.merge_request_diffs(project, merge_request_iid, per_page: 100)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
13
30
|
def create_merge_request(title:, source_branch:, target_branch:, description:, labels:, assignee_id: nil, reviewer_ids: [])
|
14
31
|
attrs = {
|
15
32
|
source_branch: source_branch,
|
@@ -33,6 +50,10 @@ module GitlabQuality
|
|
33
50
|
merge_request
|
34
51
|
end
|
35
52
|
|
53
|
+
def merge_request(id:, options: {})
|
54
|
+
client.merge_request(project, id, options)
|
55
|
+
end
|
56
|
+
|
36
57
|
def find(iid: nil, options: {}, &select)
|
37
58
|
select ||= :itself
|
38
59
|
|
@@ -4,16 +4,6 @@ module GitlabQuality
|
|
4
4
|
module TestTooling
|
5
5
|
module GitlabClient
|
6
6
|
class MergeRequestsDryClient < MergeRequestsClient
|
7
|
-
def find_merge_request_changes(merge_request_iid:)
|
8
|
-
puts "Finding changes for merge_request_id #{merge_request_iid}"
|
9
|
-
puts "project: #{project}"
|
10
|
-
end
|
11
|
-
|
12
|
-
def merge_request_changed_files(merge_request_iid:)
|
13
|
-
puts "Changed files for #{merge_request_iid}"
|
14
|
-
[]
|
15
|
-
end
|
16
|
-
|
17
7
|
def find_note(body:, merge_request_iid:)
|
18
8
|
puts "Find note for #{merge_request_iid} with body: #{body} for mr_iid: #{merge_request_iid}"
|
19
9
|
end
|
@@ -3,15 +3,14 @@
|
|
3
3
|
module GitlabQuality
|
4
4
|
module TestTooling
|
5
5
|
module GitlabClient
|
6
|
-
# The GitLab client is used for API access: https://github.com/NARKOZ/gitlab
|
7
6
|
class WorkItemsClient < GitlabGraphqlClient
|
8
|
-
def work_item(workitem_iid:
|
7
|
+
def work_item(workitem_iid:, widgets: [:notes, :linked_items, :labels, :hierarchy])
|
9
8
|
query = <<~GQL
|
10
9
|
query {
|
11
10
|
namespace(fullPath: "#{group}") {
|
12
11
|
workItem(iid: "#{workitem_iid}") {
|
13
12
|
#{work_item_fields}
|
14
|
-
#{work_item_widgets}
|
13
|
+
#{work_item_widgets(widgets)}
|
15
14
|
}
|
16
15
|
}
|
17
16
|
}
|
@@ -19,7 +18,7 @@ module GitlabQuality
|
|
19
18
|
post(query)[:workItem]
|
20
19
|
end
|
21
20
|
|
22
|
-
def group_work_items(labels: [], cursor: '', state: 'opened', created_after: nil, extras: [])
|
21
|
+
def group_work_items(labels: [], cursor: '', state: 'opened', created_after: nil, extras: [:work_item_fields])
|
23
22
|
query = <<~GQL
|
24
23
|
query {
|
25
24
|
group(fullPath: "#{group}") {
|
@@ -73,6 +72,18 @@ module GitlabQuality
|
|
73
72
|
post(query)
|
74
73
|
end
|
75
74
|
|
75
|
+
def update_note(note_id:, body:)
|
76
|
+
query = <<~GQL
|
77
|
+
mutation UpdateNote {
|
78
|
+
updateNote(input: { body: "#{body}", id: "#{note_id}" }) {
|
79
|
+
clientMutationId
|
80
|
+
errors
|
81
|
+
}
|
82
|
+
}
|
83
|
+
GQL
|
84
|
+
post(query)
|
85
|
+
end
|
86
|
+
|
76
87
|
def create_linked_items(work_item_id:, item_ids:, link_type:)
|
77
88
|
query = <<~GQL
|
78
89
|
mutation WorkItemAddLinkedItems {
|
@@ -160,50 +171,76 @@ module GitlabQuality
|
|
160
171
|
GQL
|
161
172
|
end
|
162
173
|
|
163
|
-
def
|
174
|
+
def work_item_widget_notes
|
164
175
|
<<~GQL
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
#{note_fields}
|
172
|
-
}
|
176
|
+
... on WorkItemWidgetNotes {
|
177
|
+
discussions(filter: ONLY_COMMENTS) {
|
178
|
+
nodes {
|
179
|
+
notes {
|
180
|
+
nodes {
|
181
|
+
#{note_fields}
|
173
182
|
}
|
174
183
|
}
|
175
|
-
}
|
176
|
-
}
|
177
|
-
... on WorkItemWidgetLinkedItems {
|
178
|
-
linkedItems {
|
179
|
-
nodes {
|
180
|
-
linkType
|
181
|
-
workItem {
|
182
|
-
#{work_item_fields}
|
183
|
-
}
|
184
184
|
}
|
185
|
-
}
|
186
185
|
}
|
186
|
+
}
|
187
|
+
GQL
|
188
|
+
end
|
187
189
|
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
190
|
+
def work_item_widget_linked_items
|
191
|
+
<<~GQL
|
192
|
+
... on WorkItemWidgetLinkedItems {
|
193
|
+
linkedItems {
|
194
|
+
nodes {
|
195
|
+
linkType
|
196
|
+
workItem {
|
197
|
+
#{work_item_fields}
|
198
|
+
}
|
199
|
+
}
|
193
200
|
}
|
201
|
+
}
|
202
|
+
GQL
|
203
|
+
end
|
204
|
+
|
205
|
+
def work_item_widget_labels
|
206
|
+
<<~GQL
|
207
|
+
... on WorkItemWidgetLabels{
|
208
|
+
labels{
|
209
|
+
nodes{
|
210
|
+
title
|
211
|
+
}
|
194
212
|
}
|
213
|
+
}
|
214
|
+
GQL
|
215
|
+
end
|
195
216
|
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
217
|
+
def work_item_widget_hierarchy
|
218
|
+
<<~GQL
|
219
|
+
... on WorkItemWidgetHierarchy {
|
220
|
+
children {
|
221
|
+
nodes{
|
222
|
+
#{work_item_fields}
|
223
|
+
}
|
202
224
|
}
|
203
225
|
}
|
204
226
|
GQL
|
205
227
|
end
|
206
228
|
|
229
|
+
def work_item_widgets(widgets = [])
|
230
|
+
<<~GQL
|
231
|
+
widgets(onlyTypes: [#{types_for_widgets(widgets)}]) {
|
232
|
+
#{work_item_widget_notes if widgets.include?(:notes)}
|
233
|
+
#{work_item_widget_linked_items if widgets.include?(:linked_items)}
|
234
|
+
#{work_item_widget_labels if widgets.include?(:labels)}
|
235
|
+
#{work_item_widget_hierarchy if widgets.include?(:hierarchy)}
|
236
|
+
}
|
237
|
+
GQL
|
238
|
+
end
|
239
|
+
|
240
|
+
def types_for_widgets(widgets = [])
|
241
|
+
widgets.map(&:upcase).join(', ')
|
242
|
+
end
|
243
|
+
|
207
244
|
# https://docs.gitlab.com/api/graphql/reference/#note
|
208
245
|
def note_fields
|
209
246
|
<<~GQL
|
@@ -43,9 +43,9 @@ module GitlabQuality
|
|
43
43
|
# Some of those run in their own project, so CI_PROJECT_NAME is the name we need. Those are:
|
44
44
|
# nightly, staging, canary, production, and preprod
|
45
45
|
#
|
46
|
-
# MR, master
|
47
|
-
# master
|
48
|
-
# So we assume that we're reporting a master
|
46
|
+
# MR, master, and gitlab tests run in gitlab-qa, but we only want to report tests run on
|
47
|
+
# master because the other pipelines will be monitored by the author of the MR that triggered them.
|
48
|
+
# So we assume that we're reporting a master pipeline if the project name is 'gitlab'.
|
49
49
|
|
50
50
|
@pipeline ||= Runtime::Env.pipeline_from_project_name
|
51
51
|
end
|
@@ -0,0 +1,174 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'pp'
|
4
|
+
require 'stringio'
|
5
|
+
|
6
|
+
module GitlabQuality
|
7
|
+
module TestTooling
|
8
|
+
module Report
|
9
|
+
module FeatureReadiness
|
10
|
+
class ReportOnEpic
|
11
|
+
FEATURE_READINESS_REPORT_COMMENT_ID = '<!-- FEATURE READINESS REPORT COMMENT -->'
|
12
|
+
|
13
|
+
class << self
|
14
|
+
include GitlabQuality::TestTooling::FeatureReadiness::Concerns::WorkItemConcern
|
15
|
+
|
16
|
+
def report(analyzed_epic, work_item_client)
|
17
|
+
must_haves_report_rows = generate_report_rows(analyzed_epic, :must_haves)
|
18
|
+
should_haves_report_rows = generate_report_rows(analyzed_epic, :should_haves)
|
19
|
+
|
20
|
+
existing_note = existing_note_containing_text(FEATURE_READINESS_REPORT_COMMENT_ID, analyzed_epic[:epic_iid], work_item_client)
|
21
|
+
|
22
|
+
if existing_note
|
23
|
+
work_item_client.update_note(note_id: existing_note[:id],
|
24
|
+
body: comment({ must_haves: must_haves_report_rows, should_haves: should_haves_report_rows }, analyzed_epic).tr('"', "'"))
|
25
|
+
else
|
26
|
+
work_item_client.create_discussion(id: analyzed_epic[:epic_id],
|
27
|
+
note: comment({ must_haves: must_haves_report_rows, should_haves: should_haves_report_rows }, analyzed_epic).tr('"', "'"))
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def generate_report_rows(epic, type)
|
34
|
+
status_checks = check_statuses(epic)
|
35
|
+
create_rows(epic, type, status_checks)
|
36
|
+
end
|
37
|
+
|
38
|
+
def create_rows(epic, type, status_checks)
|
39
|
+
if type == :must_haves
|
40
|
+
[
|
41
|
+
create_documentation_row(epic, status_checks),
|
42
|
+
create_feature_flag_row(epic, status_checks),
|
43
|
+
create_unit_tests_coverage_row(status_checks)
|
44
|
+
|
45
|
+
]
|
46
|
+
else
|
47
|
+
[
|
48
|
+
create_feature_tests_row(epic, status_checks),
|
49
|
+
create_e2e_tests_row(epic, status_checks)
|
50
|
+
]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def create_documentation_row(epic, status_checks)
|
55
|
+
["Documentation added?", status_icon(status_checks[:has_docs]),
|
56
|
+
prepend_text('Added in:', format_links(epic[:doc_mrs]))]
|
57
|
+
end
|
58
|
+
|
59
|
+
def create_feature_flag_row(epic, status_checks)
|
60
|
+
["Feature Flag added?", status_icon(status_checks[:feature_flag_added]),
|
61
|
+
prepend_text('Added in:', format_links(epic[:feature_flag_mrs]))]
|
62
|
+
end
|
63
|
+
|
64
|
+
def create_feature_tests_row(epic, status_checks)
|
65
|
+
["Feature tests added?", status_icon(status_checks[:has_feature_specs]),
|
66
|
+
format_links(epic[:feature_spec_mrs])]
|
67
|
+
end
|
68
|
+
|
69
|
+
def create_e2e_tests_row(epic, status_checks)
|
70
|
+
["End-to-end tests added?", status_icon(status_checks[:has_e2e_specs]),
|
71
|
+
format_links(epic[:e2e_spec_mrs])]
|
72
|
+
end
|
73
|
+
|
74
|
+
def create_unit_tests_coverage_row(status_checks)
|
75
|
+
["Unit tests coverage complete?", status_icon(status_checks[:has_complete_unit_tests]),
|
76
|
+
prepend_text('Coverage missing for:', format_links(status_checks[:missing_specs]))]
|
77
|
+
end
|
78
|
+
|
79
|
+
def prepend_text(prepend_text, text)
|
80
|
+
return "#{prepend_text} #{text}" unless text.empty?
|
81
|
+
|
82
|
+
text
|
83
|
+
end
|
84
|
+
|
85
|
+
def check_statuses(epic)
|
86
|
+
{
|
87
|
+
has_docs: epic[:doc_mrs].any?,
|
88
|
+
feature_flag_added: epic[:feature_flag_mrs].any?,
|
89
|
+
has_feature_specs: epic[:feature_spec_mrs].any?,
|
90
|
+
has_e2e_specs: epic[:e2e_spec_mrs].any?,
|
91
|
+
missing_specs: missing_spec_mrs(epic),
|
92
|
+
has_complete_unit_tests: missing_spec_mrs(epic).empty?
|
93
|
+
}
|
94
|
+
end
|
95
|
+
|
96
|
+
def comment(rows, epic)
|
97
|
+
# Generate markdown table
|
98
|
+
must_haves_table_rows = rows[:must_haves].map do |description, status, links|
|
99
|
+
"| #{description} | #{status} | #{links} |"
|
100
|
+
end.join("\n")
|
101
|
+
|
102
|
+
should_haves_table_rows = rows[:should_haves].map do |description, status, links|
|
103
|
+
"| #{description} | #{status} | #{links} |"
|
104
|
+
end.join("\n")
|
105
|
+
|
106
|
+
<<~COMMENT
|
107
|
+
#{FEATURE_READINESS_REPORT_COMMENT_ID}
|
108
|
+
|
109
|
+
# :vertical_traffic_light: Feature Readiness Evaluation Report
|
110
|
+
|
111
|
+
### :octagonal_sign: Must haves
|
112
|
+
|
113
|
+
| Evaluation | Result | Notes |
|
114
|
+
|------------|--------|-------|
|
115
|
+
#{must_haves_table_rows}
|
116
|
+
|
117
|
+
### :warning: Should haves
|
118
|
+
|
119
|
+
| Evaluation | Result | Notes |
|
120
|
+
|------------|--------|-------|
|
121
|
+
#{should_haves_table_rows}
|
122
|
+
|
123
|
+
#{data(epic)}
|
124
|
+
|
125
|
+
---
|
126
|
+
|
127
|
+
_Please note that this automation is under testing. Please add any feedback on [this issue](https://gitlab.com/gitlab-org/quality/quality-engineering/team-tasks/-/issues/3587)._
|
128
|
+
|
129
|
+
COMMENT
|
130
|
+
end
|
131
|
+
|
132
|
+
def status_icon(condition)
|
133
|
+
condition ? ':white_check_mark:' : ':x:'
|
134
|
+
end
|
135
|
+
|
136
|
+
def format_links(data)
|
137
|
+
return '' if data.empty?
|
138
|
+
|
139
|
+
data.map do |item|
|
140
|
+
item.map { |key, url| "[#{key}](#{url})" }.first
|
141
|
+
end.join(", ")
|
142
|
+
end
|
143
|
+
|
144
|
+
def missing_spec_mrs(epic)
|
145
|
+
epic[:issues].flat_map do |issue|
|
146
|
+
issue[:merge_requests].flat_map do |mr|
|
147
|
+
next [] unless mr[:files_with_missing_specs]&.any?
|
148
|
+
|
149
|
+
mr[:files_with_missing_specs].map do |file|
|
150
|
+
{ file => mr[:merge_request_web_url] }
|
151
|
+
end
|
152
|
+
end.compact
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def data(epic)
|
157
|
+
output = StringIO.new
|
158
|
+
PP.pp(epic, output)
|
159
|
+
<<~DATA
|
160
|
+
<details><summary>Expand for data</summary>
|
161
|
+
|
162
|
+
```ruby
|
163
|
+
#{output.string}
|
164
|
+
```
|
165
|
+
|
166
|
+
</details>
|
167
|
+
DATA
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
@@ -33,7 +33,7 @@ module GitlabQuality
|
|
33
33
|
issue = gitlab.create_issue(
|
34
34
|
title: "#{Time.now.strftime('%Y-%m-%d')} Test session report | #{Runtime::Env.qa_run_type}",
|
35
35
|
description: generate_description(tests),
|
36
|
-
labels: ['automation:bot-authored', '
|
36
|
+
labels: ['automation:bot-authored', 'E2E', 'triage report', pipeline_name_label],
|
37
37
|
confidential: confidential
|
38
38
|
)
|
39
39
|
|
@@ -23,16 +23,23 @@ module GitlabQuality
|
|
23
23
|
FAILURE_STACKTRACE_REGEX = %r{(?:(?:.*Failure/Error:(?<stacktrace>.+))|(?<stacktrace>.+))}m
|
24
24
|
ISSUE_STACKTRACE_REGEX = /### Stack trace\s*(```)#{FAILURE_STACKTRACE_REGEX}(```)\n*\n###/m
|
25
25
|
|
26
|
-
NEW_ISSUE_LABELS = Set.new(%w[test failure::new priority::2 automation:bot-authored]).freeze
|
26
|
+
NEW_ISSUE_LABELS = Set.new(%w[test failure::new priority::2 automation:bot-authored type::maintenance]).freeze
|
27
27
|
SCREENSHOT_IGNORED_ERRORS = ['500 Internal Server Error', 'fabricate_via_api!', 'Error Code 500'].freeze
|
28
|
-
|
28
|
+
FAILURE_ISSUE_GUIDE_URL = "https://handbook.gitlab.com/handbook/engineering/testing/guide-to-e2e-test-failure-issues/"
|
29
|
+
FAILURE_ISSUE_HANDBOOK_GUIDE = "**:rotating_light: [End-to-End Test Failure Issue Debugging Guide](#{FAILURE_ISSUE_GUIDE_URL}) :rotating_light:**\n".freeze
|
29
30
|
# Map commits to security fork (gitlab-org/security/gitlab) for gitlab-org/gitlab since security patches exist
|
30
31
|
# there before being released to the public repository
|
31
32
|
DIFF_PROJECT_MAPPINGS = {
|
33
|
+
'gitlab-org/quality/e2e-test-issues' => 'gitlab-org/security/gitlab',
|
32
34
|
'gitlab-org/gitlab' => 'gitlab-org/security/gitlab',
|
33
35
|
'gitlab-org/customers-gitlab-com' => 'gitlab-org/customers-gitlab-com'
|
34
36
|
}.freeze
|
35
37
|
|
38
|
+
# Don't use the E2E test issues project for commit parent
|
39
|
+
COMMIT_PROJECT_MAPPINGS = {
|
40
|
+
'gitlab-org/quality/e2e-test-issues' => 'gitlab-org/gitlab'
|
41
|
+
}.freeze
|
42
|
+
|
36
43
|
# The project contains record of the deployments we use to determine the commit diff
|
37
44
|
OPS_RELEASES_METADATA_PROJECT = 'gitlab-org/release/metadata'
|
38
45
|
|
@@ -179,7 +186,9 @@ module GitlabQuality
|
|
179
186
|
end
|
180
187
|
|
181
188
|
created_issue = super
|
182
|
-
|
189
|
+
|
190
|
+
# On a dry run, created_issue may not be populated
|
191
|
+
test.failure_issue ||= created_issue&.web_url
|
183
192
|
|
184
193
|
created_issue
|
185
194
|
end
|
@@ -300,7 +309,7 @@ module GitlabQuality
|
|
300
309
|
end
|
301
310
|
|
302
311
|
def new_issue_description(test)
|
303
|
-
super + [
|
312
|
+
FAILURE_ISSUE_HANDBOOK_GUIDE + super + [
|
304
313
|
"\n#{commit_diff_section}",
|
305
314
|
"### Stack trace",
|
306
315
|
"```\n#{test.full_stacktrace}\n```",
|
@@ -318,10 +327,10 @@ module GitlabQuality
|
|
318
327
|
initialize_gitlab_ops_client
|
319
328
|
|
320
329
|
if Runtime::Env.ci_pipeline_url.include?('ops.gitlab.net')
|
321
|
-
pipeline = ops_gitlab_client.find_pipeline(
|
330
|
+
pipeline = ops_gitlab_client.find_pipeline(Runtime::Env.ci_project_path, Runtime::Env.ci_pipeline_id.to_i)
|
322
331
|
generate_ops_gitlab_diff(pipeline)
|
323
332
|
else
|
324
|
-
pipeline = gitlab.find_pipeline(
|
333
|
+
pipeline = gitlab.find_pipeline(Runtime::Env.ci_project_path, Runtime::Env.ci_pipeline_id.to_i)
|
325
334
|
generate_gitlab_diff(pipeline)
|
326
335
|
end
|
327
336
|
end
|
@@ -342,6 +351,8 @@ module GitlabQuality
|
|
342
351
|
end
|
343
352
|
|
344
353
|
def fetch_deployment_info(pipeline)
|
354
|
+
return 'No pipeline name set.' unless Runtime::Env.ci_pipeline_name
|
355
|
+
|
345
356
|
pipeline_deploy_version = Runtime::Env.ci_pipeline_name.match(/(\d+\.\d+\.\d+)(?:-|$)/)&.captures&.first
|
346
357
|
deployments = fetch_deployments(ops_gitlab_client, pipeline)
|
347
358
|
found_deployment = find_matching_deployment(pipeline_deploy_version, deployments)
|
@@ -427,7 +438,8 @@ module GitlabQuality
|
|
427
438
|
|
428
439
|
def generate_gitlab_diff(pipeline)
|
429
440
|
pipeline_sha = pipeline.sha
|
430
|
-
|
441
|
+
commit_project = COMMIT_PROJECT_MAPPINGS.fetch(project, project)
|
442
|
+
parent_sha = gitlab.find_commit_parent(commit_project, pipeline_sha)
|
431
443
|
diff_project = if DIFF_PROJECT_MAPPINGS.key?(project)
|
432
444
|
DIFF_PROJECT_MAPPINGS[project]
|
433
445
|
else
|
@@ -542,6 +554,8 @@ module GitlabQuality
|
|
542
554
|
end
|
543
555
|
|
544
556
|
def screenshot_artifact_url(test)
|
557
|
+
return "" unless test.screenshot_image
|
558
|
+
|
545
559
|
ci_job_url = test.ci_job_url
|
546
560
|
screenshot_path = test.screenshot_image[%r{qa/.*$}]
|
547
561
|
|
@@ -97,7 +97,7 @@ module GitlabQuality
|
|
97
97
|
|
98
98
|
This MR was created based on data from reliable e2e test report: #{context.report_issue}
|
99
99
|
|
100
|
-
/label ~"
|
100
|
+
/label ~"E2E" ~"type::maintenance"
|
101
101
|
/label ~"devops::#{devops_stage}"
|
102
102
|
#{context.label_from_product_group(product_group)}
|
103
103
|
|
@@ -137,7 +137,7 @@ module GitlabQuality
|
|
137
137
|
- [ ] To ensure a faster turnaround, ask in the `#quality_maintainers` Slack channel for someone to review and merge the merge request, rather than assigning it directly.
|
138
138
|
|
139
139
|
<!-- Base labels. -->
|
140
|
-
/label ~"
|
140
|
+
/label ~"E2E" ~"type::maintenance" ~"maintenance::pipelines"
|
141
141
|
|
142
142
|
<!--
|
143
143
|
Choose the stage that appears in the test path, e.g. ~"devops::create" for
|
@@ -14,7 +14,8 @@ module GitlabQuality
|
|
14
14
|
"Error reference number: 502",
|
15
15
|
"(502): `GitLab is not responding`",
|
16
16
|
"<head><title>502 Bad Gateway</title></head>",
|
17
|
-
"14:connections to all backends failing"
|
17
|
+
"14:connections to all backends failing",
|
18
|
+
"gitlab_canary=true cookie was set in browser but 'Next' badge was not shown on UI"
|
18
19
|
].freeze
|
19
20
|
|
20
21
|
SHARED_EXAMPLES_CALLERS = %w[include_examples it_behaves_like].freeze
|
@@ -114,7 +115,7 @@ module GitlabQuality
|
|
114
115
|
end
|
115
116
|
|
116
117
|
def file_base_url
|
117
|
-
@file_base_url ||= "https://gitlab.com/#{project}/-/blob/#{ref}/"
|
118
|
+
@file_base_url ||= "https://gitlab.com/#{project == 'gitlab-org/quality/e2e-test-issues' ? 'gitlab-org/gitlab' : project}/-/blob/#{ref}/"
|
118
119
|
end
|
119
120
|
|
120
121
|
def test_file_link
|
@@ -143,15 +144,23 @@ module GitlabQuality
|
|
143
144
|
def files_client
|
144
145
|
@files_client ||= GitlabClient::RepositoryFilesClient.new(
|
145
146
|
token: token,
|
146
|
-
project:
|
147
|
+
project: mapped_project,
|
147
148
|
file_path: file,
|
148
|
-
ref: ref)
|
149
|
+
ref: ref.nil? || ref.empty? ? 'master' : ref)
|
149
150
|
end
|
150
151
|
|
151
152
|
private
|
152
153
|
|
153
154
|
attr_reader :token, :project, :ref
|
154
155
|
|
156
|
+
def mapped_project
|
157
|
+
if project == 'gitlab-org/quality/e2e-test-issues'
|
158
|
+
'gitlab-org/gitlab'
|
159
|
+
else
|
160
|
+
project
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
155
164
|
def screenshot
|
156
165
|
report.fetch('screenshot', nil)
|
157
166
|
end
|