gitlab_quality-test_tooling 1.14.0 → 1.17.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 +73 -70
- data/exe/flaky-test-issues +4 -4
- data/lefthook.yml +13 -0
- data/lib/gitlab_quality/test_tooling/gitlab_client/branches_client.rb +1 -1
- data/lib/gitlab_quality/test_tooling/gitlab_client/commits_client.rb +6 -4
- data/lib/gitlab_quality/test_tooling/gitlab_client/gitlab_client.rb +12 -13
- data/lib/gitlab_quality/test_tooling/gitlab_client/issues_client.rb +6 -6
- data/lib/gitlab_quality/test_tooling/gitlab_client/merge_requests_client.rb +6 -3
- data/lib/gitlab_quality/test_tooling/gitlab_client/merge_requests_dry_client.rb +4 -2
- data/lib/gitlab_quality/test_tooling/report/concerns/issue_reports.rb +41 -28
- data/lib/gitlab_quality/test_tooling/report/concerns/utils.rb +1 -1
- data/lib/gitlab_quality/test_tooling/report/flaky_test_issue.rb +78 -43
- data/lib/gitlab_quality/test_tooling/report/generate_test_session.rb +1 -4
- data/lib/gitlab_quality/test_tooling/report/knapsack_report_issue.rb +0 -3
- data/lib/gitlab_quality/test_tooling/report/relate_failure_issue.rb +4 -8
- data/lib/gitlab_quality/test_tooling/report/report_as_issue.rb +5 -3
- data/lib/gitlab_quality/test_tooling/report/slow_test_issue.rb +71 -78
- data/lib/gitlab_quality/test_tooling/runtime/env.rb +5 -1
- data/lib/gitlab_quality/test_tooling/test_meta/processor/add_to_blocking_processor.rb +33 -16
- data/lib/gitlab_quality/test_tooling/test_meta/processor/add_to_quarantine_processor.rb +34 -19
- data/lib/gitlab_quality/test_tooling/test_meta/processor/meta_processor.rb +21 -0
- data/lib/gitlab_quality/test_tooling/test_meta/test_meta_updater.rb +70 -9
- data/lib/gitlab_quality/test_tooling/version.rb +1 -1
- metadata +4 -4
|
@@ -13,11 +13,19 @@ module GitlabQuality
|
|
|
13
13
|
# - Find issue by test hash
|
|
14
14
|
# - Reopen issue if it already exists, but is closed
|
|
15
15
|
class FlakyTestIssue < ReportAsIssue
|
|
16
|
+
include Concerns::GroupAndCategoryLabels
|
|
16
17
|
include Concerns::IssueReports
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
IDENTITY_LABELS = ['test', 'failure::flaky-test', 'automation:bot-authored'].freeze
|
|
20
|
+
NEW_ISSUE_LABELS = Set.new(['type::maintenance', 'priority::3', 'severity::3', *IDENTITY_LABELS]).freeze
|
|
21
|
+
SEARCH_LABELS = ['test'].freeze
|
|
22
|
+
FOUND_IN_MR_LABEL = '~"found:in MR"'
|
|
23
|
+
FOUND_IN_MASTER_LABEL = '~"found:master"'
|
|
24
|
+
REPORT_SECTION_HEADER = '### Flakiness reports'
|
|
25
|
+
REPORTS_DOCUMENTATION = <<~DOC
|
|
26
|
+
Flaky tests were detected, please refer to the [Flaky tests documentation](https://docs.gitlab.com/ee/development/testing_guide/flaky_tests.html)
|
|
27
|
+
to learn more about how to reproduce them.
|
|
28
|
+
DOC
|
|
21
29
|
|
|
22
30
|
def initialize(
|
|
23
31
|
token:,
|
|
@@ -25,18 +33,16 @@ module GitlabQuality
|
|
|
25
33
|
base_issue_labels: nil,
|
|
26
34
|
confidential: false,
|
|
27
35
|
dry_run: false,
|
|
28
|
-
merge_request_iid: nil,
|
|
29
36
|
project: nil,
|
|
30
37
|
**_kwargs)
|
|
31
38
|
super(token: token, input_files: input_files, project: project, confidential: confidential, dry_run: dry_run)
|
|
32
39
|
|
|
33
40
|
@base_issue_labels = Set.new(base_issue_labels)
|
|
34
|
-
@merge_request_iid = merge_request_iid
|
|
35
41
|
end
|
|
36
42
|
|
|
37
43
|
private
|
|
38
44
|
|
|
39
|
-
attr_reader :base_issue_labels
|
|
45
|
+
attr_reader :base_issue_labels
|
|
40
46
|
|
|
41
47
|
def run!
|
|
42
48
|
puts "Reporting flaky tests in `#{files.join(',')}` as issues in project `#{project}` via the API at `#{Runtime::Env.gitlab_api_base}`."
|
|
@@ -44,62 +50,91 @@ module GitlabQuality
|
|
|
44
50
|
TestResults::Builder.new(files).test_results_per_file do |test_results|
|
|
45
51
|
puts "=> Reporting #{test_results.count} tests in #{test_results.path}"
|
|
46
52
|
|
|
47
|
-
test_results
|
|
48
|
-
next if test.status != 'passed' # We only want failed tests that passed in the end
|
|
49
|
-
|
|
50
|
-
create_flaky_issue(test)
|
|
51
|
-
end
|
|
53
|
+
process_test_results(test_results)
|
|
52
54
|
end
|
|
53
55
|
end
|
|
54
56
|
|
|
55
|
-
def
|
|
56
|
-
|
|
57
|
-
|
|
57
|
+
def process_test_results(test_results)
|
|
58
|
+
test_results.each do |test|
|
|
59
|
+
next unless test_is_applicable?(test)
|
|
58
60
|
|
|
59
|
-
|
|
60
|
-
found_label =
|
|
61
|
-
if !merge_request_iid || merge_request_iid.empty?
|
|
62
|
-
FOUND_IN_MASTER_LABEL
|
|
63
|
-
else
|
|
64
|
-
FOUND_IN_MR_LABEL
|
|
65
|
-
end
|
|
61
|
+
puts " => Reporting flakiness for test '#{test.name}'..."
|
|
66
62
|
|
|
67
|
-
|
|
63
|
+
issues = find_issues_by_hash(test_hash(test), state: 'opened', labels: SEARCH_LABELS)
|
|
64
|
+
issues << create_issue(test) if issues.empty?
|
|
65
|
+
|
|
66
|
+
update_reports(issues, test)
|
|
67
|
+
collect_issues(test, issues)
|
|
68
|
+
end
|
|
68
69
|
end
|
|
69
70
|
|
|
70
|
-
def
|
|
71
|
-
|
|
72
|
-
"Flaky tests were detected, please refer to the [Flaky tests documentation](https://docs.gitlab.com/ee/development/testing_guide/flaky_tests.html) " \
|
|
73
|
-
"to learn more about how to reproduce them.",
|
|
74
|
-
initial_reports_section(test)
|
|
75
|
-
].compact.join("\n\n")
|
|
71
|
+
def test_is_applicable?(test)
|
|
72
|
+
test.status == 'passed' # We only want failed tests that passed in the end
|
|
76
73
|
end
|
|
77
74
|
|
|
78
|
-
def
|
|
79
|
-
|
|
75
|
+
def up_to_date_labels(test:, issue: nil, new_labels: Set.new)
|
|
76
|
+
(base_issue_labels + super).to_a
|
|
77
|
+
end
|
|
80
78
|
|
|
81
|
-
|
|
79
|
+
def update_reports(issues, test)
|
|
82
80
|
issues.each do |issue|
|
|
83
|
-
puts " =>
|
|
81
|
+
puts " => Adding the flaky test to the existing issue: #{issue.web_url}"
|
|
82
|
+
add_report_to_issue(issue: issue, test: test, related_issues: (issues - [issue]))
|
|
83
|
+
end
|
|
84
|
+
end
|
|
84
85
|
|
|
85
|
-
|
|
86
|
-
|
|
86
|
+
def add_report_to_issue(issue:, test:, related_issues:)
|
|
87
|
+
reports_note = existing_reports_note(issue: issue)
|
|
88
|
+
note_body = [
|
|
89
|
+
report_body(reports_note: reports_note, test: test),
|
|
90
|
+
identity_labels_quick_action,
|
|
91
|
+
relate_issues_quick_actions(related_issues)
|
|
92
|
+
].join("\n")
|
|
93
|
+
|
|
94
|
+
if reports_note
|
|
95
|
+
gitlab.edit_issue_note(
|
|
96
|
+
issue_iid: issue.iid,
|
|
97
|
+
note_id: reports_note.id,
|
|
98
|
+
note: note_body
|
|
99
|
+
)
|
|
100
|
+
else
|
|
101
|
+
gitlab.create_issue_note(iid: issue.iid, note: note_body)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
87
104
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
end
|
|
105
|
+
def existing_reports_note(issue:)
|
|
106
|
+
gitlab.find_issue_notes(iid: issue.iid).find do |note|
|
|
107
|
+
note.body.start_with?(REPORT_SECTION_HEADER)
|
|
92
108
|
end
|
|
109
|
+
end
|
|
93
110
|
|
|
94
|
-
|
|
111
|
+
def report_body(reports_note:, test:)
|
|
112
|
+
increment_reports(
|
|
113
|
+
current_reports_content: reports_note&.body.to_s,
|
|
114
|
+
test: test,
|
|
115
|
+
reports_section_header: REPORT_SECTION_HEADER,
|
|
116
|
+
item_extra_content: found_label,
|
|
117
|
+
reports_extra_content: REPORTS_DOCUMENTATION
|
|
118
|
+
)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def found_label
|
|
122
|
+
if ENV.key?('CI_MERGE_REQUEST_IID')
|
|
123
|
+
FOUND_IN_MR_LABEL
|
|
124
|
+
else
|
|
125
|
+
FOUND_IN_MASTER_LABEL
|
|
126
|
+
end
|
|
95
127
|
end
|
|
96
128
|
|
|
97
|
-
def
|
|
98
|
-
|
|
129
|
+
def identity_labels_quick_action
|
|
130
|
+
labels_list = IDENTITY_LABELS.map { |label| %(~"#{label}") }.join(' ')
|
|
131
|
+
%(/label #{labels_list})
|
|
99
132
|
end
|
|
100
133
|
|
|
101
|
-
def
|
|
102
|
-
|
|
134
|
+
def relate_issues_quick_actions(issues)
|
|
135
|
+
issues.map do |issue|
|
|
136
|
+
"/relate #{issue.web_url}"
|
|
137
|
+
end.join("\n")
|
|
103
138
|
end
|
|
104
139
|
end
|
|
105
140
|
end
|
|
@@ -205,12 +205,9 @@ module GitlabQuality
|
|
|
205
205
|
|
|
206
206
|
def generate_test_text(testcase, tests_with_same_testcase, passed)
|
|
207
207
|
text = tests_with_same_testcase.map(&:name).uniq.join(', ')
|
|
208
|
-
encoded_text = ERB::Util.url_encode(text)
|
|
209
208
|
|
|
210
209
|
if testcase && !passed
|
|
211
|
-
#
|
|
212
|
-
# The first regex extracts the link to the issues list page from a link to a single issue show page by removing the issue id.
|
|
213
|
-
"[#{text}](#{testcase.match(%r{[\s\S]+/[^/\d]+})}?state=opened&search=#{encoded_text})"
|
|
210
|
+
"[#{text}](#{testcase})"
|
|
214
211
|
else
|
|
215
212
|
text
|
|
216
213
|
end
|
|
@@ -99,7 +99,6 @@ module GitlabQuality
|
|
|
99
99
|
end
|
|
100
100
|
|
|
101
101
|
def update_issue(issue:, spec_run_time:)
|
|
102
|
-
state_event = issue.state == 'closed' ? 'reopen' : nil
|
|
103
102
|
updated_description = <<~MARKDOWN.chomp
|
|
104
103
|
#{issue.description}
|
|
105
104
|
|
|
@@ -110,8 +109,6 @@ module GitlabQuality
|
|
|
110
109
|
description: updated_description
|
|
111
110
|
}
|
|
112
111
|
|
|
113
|
-
issue_attrs[:state_event] = state_event if state_event
|
|
114
|
-
|
|
115
112
|
gitlab.edit_issue(iid: issue.iid, options: issue_attrs)
|
|
116
113
|
puts " => Added a report in #{issue.web_url}!"
|
|
117
114
|
end
|
|
@@ -190,8 +190,8 @@ module GitlabQuality
|
|
|
190
190
|
end
|
|
191
191
|
|
|
192
192
|
def failure_issues(test)
|
|
193
|
-
|
|
194
|
-
test,
|
|
193
|
+
find_issues_by_hash(
|
|
194
|
+
test_hash(test),
|
|
195
195
|
state: 'opened',
|
|
196
196
|
labels: base_issue_labels + Set.new(%w[test]),
|
|
197
197
|
not_labels: exclude_labels_for_search
|
|
@@ -268,7 +268,7 @@ module GitlabQuality
|
|
|
268
268
|
if stacktrace_match
|
|
269
269
|
stacktrace_match[:stacktrace].gsub(/^\s*#.*$/, '').gsub(/^[[:space:]]+/, '').strip
|
|
270
270
|
else
|
|
271
|
-
puts " => [DEBUG] Stacktrace doesn't match the regex (#{regex})
|
|
271
|
+
puts " => [DEBUG] Stacktrace doesn't match the regex (#{regex})!"
|
|
272
272
|
end
|
|
273
273
|
end
|
|
274
274
|
|
|
@@ -347,7 +347,7 @@ module GitlabQuality
|
|
|
347
347
|
state_event = issue.state == 'closed' ? 'reopen' : nil
|
|
348
348
|
|
|
349
349
|
issue_attrs = {
|
|
350
|
-
description:
|
|
350
|
+
description: increment_reports(current_reports_content: issue.description, test: test),
|
|
351
351
|
labels: up_to_date_labels(test: test, issue: issue)
|
|
352
352
|
}
|
|
353
353
|
issue_attrs[:state_event] = state_event if state_event
|
|
@@ -356,10 +356,6 @@ module GitlabQuality
|
|
|
356
356
|
puts " => Added a report in '#{issue.title}': #{issue.web_url}!"
|
|
357
357
|
end
|
|
358
358
|
|
|
359
|
-
def new_issue_title(test)
|
|
360
|
-
"Failure in #{super}"
|
|
361
|
-
end
|
|
362
|
-
|
|
363
359
|
def screenshot_section(test)
|
|
364
360
|
return unless test.screenshot?
|
|
365
361
|
|
|
@@ -60,7 +60,7 @@ module GitlabQuality
|
|
|
60
60
|
| Description | `#{test.name}` |
|
|
61
61
|
| Test level | #{test.level} |
|
|
62
62
|
| Hash | `#{test_hash(test)}` |
|
|
63
|
-
|
|
|
63
|
+
| Reference duration | #{test.run_time} seconds |
|
|
64
64
|
| Expected duration | < #{test.max_duration_for_test} seconds |
|
|
65
65
|
#{"| Test case | #{test.testcase} |" if test.testcase}
|
|
66
66
|
DESCRIPTION
|
|
@@ -142,8 +142,10 @@ module GitlabQuality
|
|
|
142
142
|
labels
|
|
143
143
|
end
|
|
144
144
|
|
|
145
|
-
def find_issues_by_hash(test_hash)
|
|
146
|
-
search_options = { search: test_hash }
|
|
145
|
+
def find_issues_by_hash(test_hash, labels: Set.new, not_labels: Set.new, state: nil)
|
|
146
|
+
search_options = { search: test_hash, labels: labels.to_a, not: { labels: not_labels.to_a } }
|
|
147
|
+
search_options[:state] = state if state
|
|
148
|
+
search_options[:in] = 'description'
|
|
147
149
|
gitlab.find_issues(options: search_options)
|
|
148
150
|
end
|
|
149
151
|
|
|
@@ -10,16 +10,21 @@ module GitlabQuality
|
|
|
10
10
|
# - Find issue by title (with test description or test file)
|
|
11
11
|
# - Add test metadata, duration to the issue with group and category labels
|
|
12
12
|
class SlowTestIssue < ReportAsIssue
|
|
13
|
-
include TestTooling::Concerns::FindSetDri
|
|
14
13
|
include Concerns::GroupAndCategoryLabels
|
|
14
|
+
include Concerns::IssueReports
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
IDENTITY_LABELS = ['test', 'rspec:slow test', 'rspec profiling', 'automation:bot-authored'].freeze
|
|
17
|
+
NEW_ISSUE_LABELS = Set.new(['test', 'type::maintenance', 'maintenance::performance', 'priority::3', 'severity::3']).freeze
|
|
18
|
+
SEARCH_LABELS = ['test'].freeze
|
|
19
|
+
FOUND_IN_MR_LABEL = '~"found:in MR"'
|
|
20
|
+
FOUND_IN_MASTER_LABEL = '~"found:master"'
|
|
21
|
+
REPORT_SECTION_HEADER = '### Slowness reports'
|
|
22
|
+
REPORTS_DOCUMENTATION = <<~DOC
|
|
23
|
+
Slow tests were detected, please see the [test speed best practices guide](https://docs.gitlab.com/ee/development/testing_guide/best_practices.html#test-speed)
|
|
24
|
+
to improve them. More context available about this issue in the [top slow tests guide](https://docs.gitlab.com/ee/development/testing_guide/best_practices.html#top-slow-tests).
|
|
18
25
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
MultipleIssuesFound = Class.new(StandardError)
|
|
26
|
+
Add `allowed_to_be_slow: true` to the RSpec test if this is a legit slow test and close the issue.
|
|
27
|
+
DOC
|
|
23
28
|
|
|
24
29
|
private
|
|
25
30
|
|
|
@@ -29,99 +34,87 @@ module GitlabQuality
|
|
|
29
34
|
TestResults::Builder.new(files).test_results_per_file do |test_results|
|
|
30
35
|
puts "=> Reporting #{test_results.count} tests in #{test_results.path}"
|
|
31
36
|
|
|
32
|
-
test_results
|
|
33
|
-
create_slow_issue(test) if test.slow_test?
|
|
34
|
-
end
|
|
37
|
+
process_test_results(test_results)
|
|
35
38
|
end
|
|
36
39
|
end
|
|
37
40
|
|
|
38
|
-
def
|
|
39
|
-
|
|
40
|
-
|
|
41
|
+
def process_test_results(test_results)
|
|
42
|
+
test_results.each do |test|
|
|
43
|
+
next unless test.slow_test?
|
|
41
44
|
|
|
42
|
-
|
|
43
|
-
super +
|
|
44
|
-
<<~DESCRIPTION.chomp
|
|
45
|
-
Slow tests were detected, please see the [test speed best practices guide](https://docs.gitlab.com/ee/development/testing_guide/best_practices.html#test-speed)
|
|
46
|
-
to improve them. More context available about this issue in the [top slow tests guide](https://docs.gitlab.com/ee/development/testing_guide/best_practices.html#top-slow-tests).
|
|
45
|
+
puts " => Reporting slowness for test '#{test.name}'..."
|
|
47
46
|
|
|
48
|
-
|
|
47
|
+
issues = find_issues_by_hash(test_hash(test), state: 'opened', labels: SEARCH_LABELS)
|
|
48
|
+
issues << create_issue(test) if issues.empty?
|
|
49
49
|
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
update_reports(issues, test)
|
|
51
|
+
collect_issues(test, issues)
|
|
52
|
+
end
|
|
52
53
|
end
|
|
53
54
|
|
|
54
|
-
def
|
|
55
|
-
|
|
56
|
-
### Reports (1)
|
|
57
|
-
|
|
58
|
-
#{report_list_item(test)}
|
|
59
|
-
REPORTS
|
|
55
|
+
def test_is_applicable?(test)
|
|
56
|
+
test.slow_test?
|
|
60
57
|
end
|
|
61
58
|
|
|
62
|
-
def
|
|
63
|
-
|
|
59
|
+
def update_reports(issues, test)
|
|
60
|
+
issues.each do |issue|
|
|
61
|
+
puts " => Adding the slow test to the existing issue: #{issue.web_url}"
|
|
62
|
+
add_report_to_issue(issue: issue, test: test, related_issues: (issues - [issue]))
|
|
63
|
+
end
|
|
64
64
|
end
|
|
65
65
|
|
|
66
|
-
def
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
66
|
+
def add_report_to_issue(issue:, test:, related_issues:)
|
|
67
|
+
reports_note = existing_reports_note(issue: issue)
|
|
68
|
+
note_body = [
|
|
69
|
+
report_body(reports_note: reports_note, test: test),
|
|
70
|
+
identity_labels_quick_action,
|
|
71
|
+
relate_issues_quick_actions(related_issues)
|
|
72
|
+
].join("\n")
|
|
73
|
+
|
|
74
|
+
if reports_note
|
|
75
|
+
gitlab.edit_issue_note(
|
|
76
|
+
issue_iid: issue.iid,
|
|
77
|
+
note_id: reports_note.id,
|
|
78
|
+
note: note_body
|
|
79
|
+
)
|
|
80
|
+
else
|
|
81
|
+
gitlab.create_issue_note(iid: issue.iid, note: note_body)
|
|
82
|
+
end
|
|
72
83
|
end
|
|
73
84
|
|
|
74
|
-
def
|
|
75
|
-
|
|
85
|
+
def existing_reports_note(issue:)
|
|
86
|
+
gitlab.find_issue_notes(iid: issue.iid).find do |note|
|
|
87
|
+
note.body.start_with?(REPORT_SECTION_HEADER)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
76
90
|
|
|
77
|
-
|
|
91
|
+
def report_body(reports_note:, test:)
|
|
92
|
+
increment_reports(
|
|
93
|
+
current_reports_content: reports_note&.body.to_s,
|
|
94
|
+
test: test,
|
|
95
|
+
reports_section_header: REPORT_SECTION_HEADER,
|
|
96
|
+
item_extra_content: found_label,
|
|
97
|
+
reports_extra_content: REPORTS_DOCUMENTATION
|
|
98
|
+
)
|
|
99
|
+
end
|
|
78
100
|
|
|
79
|
-
|
|
80
|
-
|
|
101
|
+
def found_label
|
|
102
|
+
if ENV.key?('CI_MERGE_REQUEST_IID')
|
|
103
|
+
FOUND_IN_MR_LABEL
|
|
81
104
|
else
|
|
82
|
-
|
|
83
|
-
puts " => Existing issue link #{issue['web_url']}"
|
|
84
|
-
|
|
85
|
-
update_reports(issue, test)
|
|
86
|
-
end
|
|
105
|
+
FOUND_IN_MASTER_LABEL
|
|
87
106
|
end
|
|
88
|
-
|
|
89
|
-
collect_issues(test, issues)
|
|
90
|
-
rescue MultipleIssuesFound => e
|
|
91
|
-
warn(e.message)
|
|
92
107
|
end
|
|
93
108
|
|
|
94
|
-
def
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
issue_attrs = {
|
|
99
|
-
description: up_to_date_issue_description(issue.description, test)
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
issue_attrs[:state_event] = state_event if state_event
|
|
103
|
-
|
|
104
|
-
gitlab.edit_issue(iid: issue.iid, options: issue_attrs)
|
|
105
|
-
puts " => Added a report in '#{issue.title}': #{issue.web_url}!"
|
|
109
|
+
def identity_labels_quick_action
|
|
110
|
+
labels_list = IDENTITY_LABELS.map { |label| %(~"#{label}") }.join(' ')
|
|
111
|
+
%(/label #{labels_list})
|
|
106
112
|
end
|
|
107
113
|
|
|
108
|
-
def
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
reports_count = issue_description
|
|
113
|
-
.scan(REPORT_ITEM_REGEX)
|
|
114
|
-
.size.to_i + 1
|
|
115
|
-
issue_description.sub(/^### Reports.*$/, "### Reports (#{reports_count})")
|
|
116
|
-
else # For issue with the legacy format, we add the Reports section
|
|
117
|
-
reports_count = issue_description
|
|
118
|
-
.scan(JOB_URL_REGEX)
|
|
119
|
-
.size.to_i + 1
|
|
120
|
-
|
|
121
|
-
"#{issue_description}\n\n### Reports (#{reports_count})"
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
"#{new_issue_description}\n#{report_list_item(test)}"
|
|
114
|
+
def relate_issues_quick_actions(issues)
|
|
115
|
+
issues.map do |issue|
|
|
116
|
+
"/relate #{issue.web_url}"
|
|
117
|
+
end.join("\n")
|
|
125
118
|
end
|
|
126
119
|
end
|
|
127
120
|
end
|
|
@@ -25,7 +25,7 @@ module GitlabQuality
|
|
|
25
25
|
|
|
26
26
|
ENV_VARIABLES.each do |env_name, method_name|
|
|
27
27
|
define_method(method_name) do
|
|
28
|
-
env_var_value_if_defined(env_name) || (instance_variable_get("@#{method_name}") if instance_variable_defined?("@#{method_name}"))
|
|
28
|
+
env_var_value_if_defined(env_name) || (instance_variable_get(:"@#{method_name}") if instance_variable_defined?(:"@#{method_name}"))
|
|
29
29
|
end
|
|
30
30
|
end
|
|
31
31
|
|
|
@@ -33,6 +33,10 @@ module GitlabQuality
|
|
|
33
33
|
env_var_value_if_defined('QA_LOG_LEVEL')&.upcase || 'INFO'
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
+
def gitlab_bot_username
|
|
37
|
+
env_var_value_if_defined('GITLAB_BOT_USERNAME') || 'gitlab-bot'
|
|
38
|
+
end
|
|
39
|
+
|
|
36
40
|
def log_path
|
|
37
41
|
env_var_value_if_defined('QA_LOG_PATH') || host_artifacts_dir
|
|
38
42
|
end
|
|
@@ -14,41 +14,48 @@ module GitlabQuality
|
|
|
14
14
|
# @param [TestMetaUpdater] context instance of TestMetaUpdater
|
|
15
15
|
def execute(spec, context) # rubocop:disable Metrics/AbcSize
|
|
16
16
|
@context = context
|
|
17
|
-
|
|
17
|
+
@existing_mrs = nil
|
|
18
18
|
@file_path = spec["file_path"]
|
|
19
|
+
testcase = spec["testcase"]
|
|
19
20
|
devops_stage = spec["stage"]
|
|
20
21
|
product_group = spec["product_group"]
|
|
21
22
|
@example_name = spec["name"]
|
|
22
|
-
@mr_title = format("%{prefix} %{example_name}", prefix: '[PROMOTE TO BLOCKING]', example_name: example_name)
|
|
23
|
-
|
|
24
|
-
return unless proceed_with_merge_request?
|
|
23
|
+
@mr_title = format("%{prefix} %{example_name}", prefix: '[PROMOTE TO BLOCKING]', example_name: example_name).truncate(72, omission: '')
|
|
25
24
|
|
|
26
25
|
@file_contents = context.get_file_contents(file_path)
|
|
27
26
|
|
|
28
|
-
new_content, changed_line_no = add_blocking_metadata
|
|
27
|
+
new_content, @changed_line_no = add_blocking_metadata
|
|
29
28
|
|
|
30
|
-
return
|
|
29
|
+
return unless proceed_with_merge_request?
|
|
31
30
|
|
|
32
31
|
branch = context.create_branch("blocking-promotion-#{SecureRandom.hex(4)}", example_name, context.ref)
|
|
33
32
|
|
|
34
33
|
context.commit_changes(branch, <<~COMMIT_MESSAGE, file_path, new_content)
|
|
35
34
|
Promote end-to-end test to blocking
|
|
36
35
|
|
|
37
|
-
Promote to blocking: #{example_name}
|
|
36
|
+
#{"Promote to blocking: #{example_name}".truncate(72)}
|
|
38
37
|
COMMIT_MESSAGE
|
|
39
38
|
|
|
40
|
-
|
|
39
|
+
reviewer_id, assignee_handle = context.fetch_dri_id(product_group, devops_stage)
|
|
40
|
+
|
|
41
|
+
gitlab_bot_user_id = context.user_id_for_username(Runtime::Env.gitlab_bot_username)
|
|
41
42
|
|
|
42
|
-
merge_request = context.create_merge_request(mr_title, branch,
|
|
43
|
+
merge_request = context.create_merge_request(mr_title, branch, gitlab_bot_user_id, [reviewer_id]) do
|
|
43
44
|
<<~MARKDOWN
|
|
44
45
|
## What does this MR do?
|
|
45
46
|
|
|
46
47
|
Promotes the test [`#{example_name}`](https://gitlab.com/#{context.project}/-/blob/#{context.ref}/#{file_path}#L#{changed_line_no + 1})
|
|
47
48
|
to the blocking bucket
|
|
48
49
|
|
|
50
|
+
This test was identified in the reliable e2e test report: #{context.report_issue}
|
|
51
|
+
|
|
52
|
+
[Testcase link](#{testcase})
|
|
53
|
+
|
|
54
|
+
[Spec metrics link](#{context.single_spec_metrics_link(example_name)})
|
|
49
55
|
|
|
50
56
|
/label ~"Quality" ~"QA" ~"type::maintenance"
|
|
51
57
|
/label ~"devops::#{devops_stage}"
|
|
58
|
+
#{context.label_from_product_group(product_group)}
|
|
52
59
|
|
|
53
60
|
<div align="center">
|
|
54
61
|
(This MR was automatically generated by [`gitlab_quality-test_tooling`](https://gitlab.com/gitlab-org/ruby/gems/gitlab_quality-test_tooling) at #{Time.now.utc})
|
|
@@ -62,6 +69,11 @@ module GitlabQuality
|
|
|
62
69
|
If you think this MR should not be merged, please close it and add a note of the reason to the blocking report: #{context.report_issue}
|
|
63
70
|
MARKDOWN
|
|
64
71
|
|
|
72
|
+
if merge_request
|
|
73
|
+
context.add_processed_record({ file_path => changed_line_no })
|
|
74
|
+
Runtime::Logger.info(" Created MR for promotion to blocking: #{merge_request.web_url}")
|
|
75
|
+
end
|
|
76
|
+
|
|
65
77
|
merge_request
|
|
66
78
|
end
|
|
67
79
|
|
|
@@ -82,15 +94,20 @@ module GitlabQuality
|
|
|
82
94
|
|
|
83
95
|
private
|
|
84
96
|
|
|
85
|
-
attr_reader :context, :file_path, :file_contents, :example_name, :mr_title
|
|
97
|
+
attr_reader :context, :file_path, :file_contents, :example_name, :mr_title, :changed_line_no
|
|
86
98
|
|
|
87
99
|
# Checks if there is already an MR open
|
|
88
100
|
#
|
|
89
101
|
# @return [Boolean]
|
|
90
|
-
def proceed_with_merge_request?
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
102
|
+
def proceed_with_merge_request? # rubocop:disable Metrics/AbcSize
|
|
103
|
+
if changed_line_no.negative?
|
|
104
|
+
Runtime::Logger.info(" No lines were changed in #{file_path}. Will not proceed with creating MR.")
|
|
105
|
+
return false
|
|
106
|
+
elsif context.record_processed?(file_path, changed_line_no)
|
|
107
|
+
Runtime::Logger.info(" Record already processed for #{file_path}:#{changed_line_no}. Will not proceed with creating MR.")
|
|
108
|
+
return false
|
|
109
|
+
elsif existing_mrs&.any?
|
|
110
|
+
Runtime::Logger.info(" An open MR already exists for '#{example_name}': #{existing_mrs.first['web_url']}. Will not proceed with creating MR.")
|
|
94
111
|
return false
|
|
95
112
|
end
|
|
96
113
|
|
|
@@ -109,8 +126,8 @@ module GitlabQuality
|
|
|
109
126
|
end
|
|
110
127
|
|
|
111
128
|
context.update_matched_line(matched_lines.last, file_contents.dup) do |line|
|
|
112
|
-
if line.include?(',')
|
|
113
|
-
line[line.index(',')] = format(BLOCKING_METADATA, suffix: ',')
|
|
129
|
+
if line.sub(DESCRIPTION_REGEX, '').include?(',')
|
|
130
|
+
line[line.index(',', end_of_description_index(line))] = format(BLOCKING_METADATA, suffix: ',')
|
|
114
131
|
else
|
|
115
132
|
line[line.rindex(' ')] = format(BLOCKING_METADATA, suffix: ' ')
|
|
116
133
|
end
|