gitlab-qa 10.4.1 → 11.1.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/changelog_config.yml +13 -0
- data/.gitlab/merge_request_templates/Release.md +9 -36
- data/.rubocop_todo.yml +0 -12
- data/Dangerfile +1 -5
- data/Gemfile.lock +4 -4
- data/README.md +1 -2
- data/docs/running_against_remote_grid.md +39 -2
- data/gitlab-qa.gemspec +1 -1
- data/lib/gitlab/qa/component/gitaly_cluster.rb +0 -1
- data/lib/gitlab/qa/component/gitlab.rb +10 -9
- data/lib/gitlab/qa/component/selenoid.rb +8 -3
- data/lib/gitlab/qa/runtime/env.rb +21 -41
- data/lib/gitlab/qa/scenario/test/instance/airgapped.rb +0 -2
- data/lib/gitlab/qa/scenario/test/integration/gitaly_cluster.rb +0 -2
- data/lib/gitlab/qa/scenario/test/integration/mtls.rb +0 -1
- data/lib/gitlab/qa/scenario/test/integration/praefect.rb +0 -2
- data/lib/gitlab/qa/scenario/test/integration/registry_with_cdn.rb +2 -2
- data/lib/gitlab/qa/version.rb +1 -1
- data/lib/gitlab/qa.rb +0 -1
- data/support/data/admin_access_token_seed.rb +4 -1
- metadata +5 -39
- data/bin/slack +0 -14
- data/exe/gitlab-qa-report +0 -10
- data/lib/gitlab/qa/report/base_test_results.rb +0 -39
- data/lib/gitlab/qa/report/find_set_dri.rb +0 -43
- data/lib/gitlab/qa/report/generate_test_session.rb +0 -275
- data/lib/gitlab/qa/report/gitlab_issue_client.rb +0 -190
- data/lib/gitlab/qa/report/gitlab_issue_dry_client.rb +0 -28
- data/lib/gitlab/qa/report/j_unit_test_results.rb +0 -27
- data/lib/gitlab/qa/report/json_test_results.rb +0 -29
- data/lib/gitlab/qa/report/prepare_stage_reports.rb +0 -86
- data/lib/gitlab/qa/report/relate_failure_issue.rb +0 -374
- data/lib/gitlab/qa/report/report_as_issue.rb +0 -176
- data/lib/gitlab/qa/report/report_results.rb +0 -64
- data/lib/gitlab/qa/report/results_in_issues.rb +0 -126
- data/lib/gitlab/qa/report/results_in_testcases.rb +0 -111
- data/lib/gitlab/qa/report/results_reporter_shared.rb +0 -70
- data/lib/gitlab/qa/report/summary_table.rb +0 -43
- data/lib/gitlab/qa/report/test_result.rb +0 -184
- data/lib/gitlab/qa/report/update_screenshot_path.rb +0 -63
- data/lib/gitlab/qa/reporter.rb +0 -131
- data/lib/gitlab/qa/runtime/token_finder.rb +0 -44
- data/lib/gitlab/qa/slack/post_to_slack.rb +0 -30
- data/lib/gitlab/qa/system_logs/finders/json_log_finder.rb +0 -65
- data/lib/gitlab/qa/system_logs/finders/rails/api_log_finder.rb +0 -21
- data/lib/gitlab/qa/system_logs/finders/rails/application_log_finder.rb +0 -21
- data/lib/gitlab/qa/system_logs/finders/rails/exception_log_finder.rb +0 -21
- data/lib/gitlab/qa/system_logs/finders/rails/graphql_log_finder.rb +0 -21
- data/lib/gitlab/qa/system_logs/log_types/log.rb +0 -38
- data/lib/gitlab/qa/system_logs/log_types/rails/api_log.rb +0 -34
- data/lib/gitlab/qa/system_logs/log_types/rails/application_log.rb +0 -27
- data/lib/gitlab/qa/system_logs/log_types/rails/exception_log.rb +0 -23
- data/lib/gitlab/qa/system_logs/log_types/rails/graphql_log.rb +0 -30
- data/lib/gitlab/qa/system_logs/shared_fields.rb +0 -29
- data/lib/gitlab/qa/system_logs/system_logs_formatter.rb +0 -65
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gitlab-qa
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 11.1.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: 2023-
|
11
|
+
date: 2023-06-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: climate_control
|
@@ -198,14 +198,14 @@ dependencies:
|
|
198
198
|
requirements:
|
199
199
|
- - "~>"
|
200
200
|
- !ruby/object:Gem::Version
|
201
|
-
version: 4.
|
201
|
+
version: '4.19'
|
202
202
|
type: :runtime
|
203
203
|
prerelease: false
|
204
204
|
version_requirements: !ruby/object:Gem::Requirement
|
205
205
|
requirements:
|
206
206
|
- - "~>"
|
207
207
|
- !ruby/object:Gem::Version
|
208
|
-
version: 4.
|
208
|
+
version: '4.19'
|
209
209
|
- !ruby/object:Gem::Dependency
|
210
210
|
name: http
|
211
211
|
requirement: !ruby/object:Gem::Requirement
|
@@ -313,13 +313,13 @@ email:
|
|
313
313
|
- gitlab-qa@gmail.com
|
314
314
|
executables:
|
315
315
|
- gitlab-qa
|
316
|
-
- gitlab-qa-report
|
317
316
|
extensions: []
|
318
317
|
extra_rdoc_files: []
|
319
318
|
files:
|
320
319
|
- ".dockerignore"
|
321
320
|
- ".gitignore"
|
322
321
|
- ".gitlab-ci.yml"
|
322
|
+
- ".gitlab/changelog_config.yml"
|
323
323
|
- ".gitlab/issue_templates/Default.md"
|
324
324
|
- ".gitlab/merge_request_templates/Default.md"
|
325
325
|
- ".gitlab/merge_request_templates/Release.md"
|
@@ -339,7 +339,6 @@ files:
|
|
339
339
|
- bin/gen-cert.sh
|
340
340
|
- bin/merge_html_reports
|
341
341
|
- bin/setup
|
342
|
-
- bin/slack
|
343
342
|
- docs/README.md
|
344
343
|
- docs/architecture.md
|
345
344
|
- docs/configuring_omnibus.md
|
@@ -355,7 +354,6 @@ files:
|
|
355
354
|
- docs/waits.md
|
356
355
|
- docs/what_tests_can_be_run.md
|
357
356
|
- exe/gitlab-qa
|
358
|
-
- exe/gitlab-qa-report
|
359
357
|
- fixtures/ldap/1_add_nodes.ldif
|
360
358
|
- fixtures/ldap/2_add_users.ldif
|
361
359
|
- fixtures/ldap/3_add_groups.ldif
|
@@ -393,24 +391,6 @@ files:
|
|
393
391
|
- lib/gitlab/qa/docker/engine.rb
|
394
392
|
- lib/gitlab/qa/docker/volumes.rb
|
395
393
|
- lib/gitlab/qa/release.rb
|
396
|
-
- lib/gitlab/qa/report/base_test_results.rb
|
397
|
-
- lib/gitlab/qa/report/find_set_dri.rb
|
398
|
-
- lib/gitlab/qa/report/generate_test_session.rb
|
399
|
-
- lib/gitlab/qa/report/gitlab_issue_client.rb
|
400
|
-
- lib/gitlab/qa/report/gitlab_issue_dry_client.rb
|
401
|
-
- lib/gitlab/qa/report/j_unit_test_results.rb
|
402
|
-
- lib/gitlab/qa/report/json_test_results.rb
|
403
|
-
- lib/gitlab/qa/report/prepare_stage_reports.rb
|
404
|
-
- lib/gitlab/qa/report/relate_failure_issue.rb
|
405
|
-
- lib/gitlab/qa/report/report_as_issue.rb
|
406
|
-
- lib/gitlab/qa/report/report_results.rb
|
407
|
-
- lib/gitlab/qa/report/results_in_issues.rb
|
408
|
-
- lib/gitlab/qa/report/results_in_testcases.rb
|
409
|
-
- lib/gitlab/qa/report/results_reporter_shared.rb
|
410
|
-
- lib/gitlab/qa/report/summary_table.rb
|
411
|
-
- lib/gitlab/qa/report/test_result.rb
|
412
|
-
- lib/gitlab/qa/report/update_screenshot_path.rb
|
413
|
-
- lib/gitlab/qa/reporter.rb
|
414
394
|
- lib/gitlab/qa/runner.rb
|
415
395
|
- lib/gitlab/qa/runtime/env.rb
|
416
396
|
- lib/gitlab/qa/runtime/logger.rb
|
@@ -424,7 +404,6 @@ files:
|
|
424
404
|
- lib/gitlab/qa/runtime/omnibus_configurations/packages.rb
|
425
405
|
- lib/gitlab/qa/runtime/omnibus_configurations/registry_object_storage.rb
|
426
406
|
- lib/gitlab/qa/runtime/scenario.rb
|
427
|
-
- lib/gitlab/qa/runtime/token_finder.rb
|
428
407
|
- lib/gitlab/qa/scenario/actable.rb
|
429
408
|
- lib/gitlab/qa/scenario/cli_commands.rb
|
430
409
|
- lib/gitlab/qa/scenario/template.rb
|
@@ -478,7 +457,6 @@ files:
|
|
478
457
|
- lib/gitlab/qa/service/cluster_provider/base.rb
|
479
458
|
- lib/gitlab/qa/service/cluster_provider/k3d.rb
|
480
459
|
- lib/gitlab/qa/service/kubernetes_cluster.rb
|
481
|
-
- lib/gitlab/qa/slack/post_to_slack.rb
|
482
460
|
- lib/gitlab/qa/support/config_scripts.rb
|
483
461
|
- lib/gitlab/qa/support/get_request.rb
|
484
462
|
- lib/gitlab/qa/support/gitlab_upgrade_path.rb
|
@@ -487,18 +465,6 @@ files:
|
|
487
465
|
- lib/gitlab/qa/support/invalid_response_error.rb
|
488
466
|
- lib/gitlab/qa/support/shell_command.rb
|
489
467
|
- lib/gitlab/qa/support/shellout.rb
|
490
|
-
- lib/gitlab/qa/system_logs/finders/json_log_finder.rb
|
491
|
-
- lib/gitlab/qa/system_logs/finders/rails/api_log_finder.rb
|
492
|
-
- lib/gitlab/qa/system_logs/finders/rails/application_log_finder.rb
|
493
|
-
- lib/gitlab/qa/system_logs/finders/rails/exception_log_finder.rb
|
494
|
-
- lib/gitlab/qa/system_logs/finders/rails/graphql_log_finder.rb
|
495
|
-
- lib/gitlab/qa/system_logs/log_types/log.rb
|
496
|
-
- lib/gitlab/qa/system_logs/log_types/rails/api_log.rb
|
497
|
-
- lib/gitlab/qa/system_logs/log_types/rails/application_log.rb
|
498
|
-
- lib/gitlab/qa/system_logs/log_types/rails/exception_log.rb
|
499
|
-
- lib/gitlab/qa/system_logs/log_types/rails/graphql_log.rb
|
500
|
-
- lib/gitlab/qa/system_logs/shared_fields.rb
|
501
|
-
- lib/gitlab/qa/system_logs/system_logs_formatter.rb
|
502
468
|
- lib/gitlab/qa/test_logger.rb
|
503
469
|
- lib/gitlab/qa/version.rb
|
504
470
|
- scripts/build-package-and-test-env
|
data/bin/slack
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
#!/bin/bash
|
2
|
-
# Sends Slack notification MSG to CI_SLACK_WEBHOOK_URL (which needs to be set).
|
3
|
-
# ICON_EMOJI needs to be set to an icon emoji name (without the `:` around it).
|
4
|
-
|
5
|
-
CHANNEL=$1
|
6
|
-
MSG=$2
|
7
|
-
ICON_EMOJI=$3
|
8
|
-
|
9
|
-
if [ -z "$CHANNEL" ] || [ -z "$CI_SLACK_WEBHOOK_URL" ] || [ -z "$MSG" ] || [ -z "$ICON_EMOJI" ]; then
|
10
|
-
echo "Missing argument(s) - Use: $0 channel message icon_emoji"
|
11
|
-
echo "and set CI_SLACK_WEBHOOK_URL environment variable."
|
12
|
-
else
|
13
|
-
curl -X POST --data-urlencode 'payload={"channel": "#'"$CHANNEL"'", "username": "GitLab QA Bot", "text": "'"$MSG"'", "icon_emoji": "'":$ICON_EMOJI:"'"}' "$CI_SLACK_WEBHOOK_URL"
|
14
|
-
fi
|
data/exe/gitlab-qa-report
DELETED
@@ -1,39 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Gitlab
|
4
|
-
module QA
|
5
|
-
module Report
|
6
|
-
class BaseTestResults
|
7
|
-
include Enumerable
|
8
|
-
|
9
|
-
attr_reader :path
|
10
|
-
|
11
|
-
def initialize(path)
|
12
|
-
@path = path
|
13
|
-
@results = parse
|
14
|
-
@testcases = process
|
15
|
-
end
|
16
|
-
|
17
|
-
def each(&block)
|
18
|
-
testcases.each(&block)
|
19
|
-
end
|
20
|
-
|
21
|
-
def write
|
22
|
-
raise NotImplementedError
|
23
|
-
end
|
24
|
-
|
25
|
-
private
|
26
|
-
|
27
|
-
attr_reader :results, :testcases
|
28
|
-
|
29
|
-
def parse
|
30
|
-
raise NotImplementedError
|
31
|
-
end
|
32
|
-
|
33
|
-
def process
|
34
|
-
raise NotImplementedError
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
@@ -1,43 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'json'
|
4
|
-
|
5
|
-
module Gitlab
|
6
|
-
module QA
|
7
|
-
module Report
|
8
|
-
module FindSetDri
|
9
|
-
def set_dri_via_group(product_group, test)
|
10
|
-
parse_json_with_sets
|
11
|
-
fetch_stage_sets(test)
|
12
|
-
|
13
|
-
return @sets.sample['username'] if @stage_sets.empty?
|
14
|
-
|
15
|
-
fetch_group_sets(product_group)
|
16
|
-
|
17
|
-
if @group_sets.empty?
|
18
|
-
@stage_sets.sample['username']
|
19
|
-
else
|
20
|
-
@group_sets.sample['username']
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
private
|
25
|
-
|
26
|
-
def parse_json_with_sets
|
27
|
-
response = Support::HttpRequest.make_http_request(
|
28
|
-
url: 'https://gitlab-org.gitlab.io/gitlab-roulette/roulette.json'
|
29
|
-
)
|
30
|
-
@sets = JSON.parse(response.body).select { |user| user['role'].include?('software-engineer-in-test') }
|
31
|
-
end
|
32
|
-
|
33
|
-
def fetch_stage_sets(test)
|
34
|
-
@stage_sets = @sets.select { |user| user['role'].include?(test.stage.split("_").map(&:capitalize).join(" ")) }
|
35
|
-
end
|
36
|
-
|
37
|
-
def fetch_group_sets(product_group)
|
38
|
-
@group_sets = @stage_sets.select { |user| user['role'].include?(product_group.split("_").map { |word| word == 'and' ? word : word.capitalize }.join(" ")) }
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
@@ -1,275 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'erb'
|
4
|
-
require 'date'
|
5
|
-
|
6
|
-
module Gitlab
|
7
|
-
module QA
|
8
|
-
module Report
|
9
|
-
class GenerateTestSession < ReportAsIssue
|
10
|
-
def initialize(**kwargs)
|
11
|
-
super
|
12
|
-
@issue_type = 'issue'
|
13
|
-
end
|
14
|
-
|
15
|
-
private
|
16
|
-
|
17
|
-
# rubocop:disable Metrics/AbcSize
|
18
|
-
def run!
|
19
|
-
puts "Generating test results in `#{files.join(',')}` as issues in project `#{project}` via the API at `#{Runtime::Env.gitlab_api_base}`."
|
20
|
-
|
21
|
-
tests = Dir.glob(files).flat_map do |path|
|
22
|
-
puts "Loading tests in #{path}"
|
23
|
-
|
24
|
-
Report::JsonTestResults.new(path).to_a
|
25
|
-
end
|
26
|
-
|
27
|
-
issue = gitlab.create_issue(
|
28
|
-
title: "#{Time.now.strftime('%Y-%m-%d')} Test session report | #{Runtime::Env.qa_run_type}",
|
29
|
-
description: generate_description(tests),
|
30
|
-
labels: ['Quality', 'QA', 'triage report', pipeline_name_label]
|
31
|
-
)
|
32
|
-
|
33
|
-
# Workaround for https://gitlab.com/gitlab-org/gitlab/-/issues/295493
|
34
|
-
unless Runtime::Env.qa_issue_url.to_s.empty?
|
35
|
-
gitlab.create_issue_note(
|
36
|
-
iid: issue.iid,
|
37
|
-
note: "/relate #{Runtime::Env.qa_issue_url}")
|
38
|
-
end
|
39
|
-
|
40
|
-
File.write('REPORT_ISSUE_URL', issue.web_url)
|
41
|
-
end
|
42
|
-
# rubocop:enable Metrics/AbcSize
|
43
|
-
|
44
|
-
def generate_description(tests)
|
45
|
-
<<~MARKDOWN.rstrip
|
46
|
-
## Session summary
|
47
|
-
|
48
|
-
* Deploy version: #{Runtime::Env.deploy_version}
|
49
|
-
* Deploy environment: #{Runtime::Env.deploy_environment}
|
50
|
-
* Pipeline: #{Runtime::Env.pipeline_from_project_name} [#{Runtime::Env.ci_pipeline_id}](#{Runtime::Env.ci_pipeline_url})
|
51
|
-
#{generate_summary(tests: tests)}
|
52
|
-
|
53
|
-
#{generate_failed_jobs_listing}
|
54
|
-
|
55
|
-
#{generate_stages_listing(tests)}
|
56
|
-
|
57
|
-
#{generate_qa_issue_relation}
|
58
|
-
|
59
|
-
#{generate_link_to_dashboard}
|
60
|
-
MARKDOWN
|
61
|
-
end
|
62
|
-
|
63
|
-
def generate_summary(tests:, tests_by_status: nil)
|
64
|
-
tests_by_status ||= tests.group_by(&:status)
|
65
|
-
total = tests.size
|
66
|
-
passed = tests_by_status['passed']&.size || 0
|
67
|
-
failed = tests_by_status['failed']&.size || 0
|
68
|
-
others = total - passed - failed
|
69
|
-
|
70
|
-
<<~MARKDOWN.chomp
|
71
|
-
* Total #{total} tests
|
72
|
-
* Passed #{passed} tests
|
73
|
-
* Failed #{failed} tests
|
74
|
-
* #{others} other tests (usually skipped)
|
75
|
-
MARKDOWN
|
76
|
-
end
|
77
|
-
|
78
|
-
def generate_failed_jobs_listing
|
79
|
-
failed_jobs = []
|
80
|
-
|
81
|
-
client = Gitlab.client(
|
82
|
-
endpoint: Runtime::Env.ci_api_v4_url,
|
83
|
-
private_token: Runtime::Env.gitlab_ci_api_token)
|
84
|
-
|
85
|
-
gitlab.handle_gitlab_client_exceptions do
|
86
|
-
failed_jobs = client.pipeline_jobs(
|
87
|
-
Runtime::Env.ci_project_id,
|
88
|
-
Runtime::Env.ci_pipeline_id,
|
89
|
-
scope: 'failed')
|
90
|
-
end
|
91
|
-
|
92
|
-
listings = failed_jobs.map do |job|
|
93
|
-
allowed_to_fail = ' (allowed to fail)' if job.allow_failure
|
94
|
-
|
95
|
-
"* [#{job.name}](#{job.web_url})#{allowed_to_fail}"
|
96
|
-
end.join("\n")
|
97
|
-
|
98
|
-
<<~MARKDOWN.chomp if failed_jobs.any?
|
99
|
-
## Failed jobs
|
100
|
-
|
101
|
-
#{listings}
|
102
|
-
MARKDOWN
|
103
|
-
end
|
104
|
-
|
105
|
-
def generate_stages_listing(tests)
|
106
|
-
generate_tests_by_stage(tests).map do |stage, tests_for_stage|
|
107
|
-
tests_by_status = tests_for_stage.group_by(&:status)
|
108
|
-
|
109
|
-
<<~MARKDOWN.chomp
|
110
|
-
### #{stage&.capitalize || 'Unknown'}
|
111
|
-
|
112
|
-
#{generate_summary(
|
113
|
-
tests: tests_for_stage, tests_by_status: tests_by_status)}
|
114
|
-
|
115
|
-
#{generate_testcase_listing_by_status(
|
116
|
-
tests: tests_for_stage, tests_by_status: tests_by_status)}
|
117
|
-
MARKDOWN
|
118
|
-
end.join("\n\n")
|
119
|
-
end
|
120
|
-
|
121
|
-
def generate_tests_by_stage(tests)
|
122
|
-
# https://about.gitlab.com/handbook/product/product-categories/#devops-stages
|
123
|
-
ordering = %w[
|
124
|
-
manage
|
125
|
-
plan
|
126
|
-
create
|
127
|
-
verify
|
128
|
-
package
|
129
|
-
release
|
130
|
-
configure
|
131
|
-
monitor
|
132
|
-
secure
|
133
|
-
defend
|
134
|
-
growth
|
135
|
-
fulfillment
|
136
|
-
enablement
|
137
|
-
]
|
138
|
-
|
139
|
-
tests.sort_by do |test|
|
140
|
-
ordering.index(test.stage) || ordering.size
|
141
|
-
end.group_by(&:stage)
|
142
|
-
end
|
143
|
-
|
144
|
-
def generate_testcase_listing_by_status(tests:, tests_by_status:)
|
145
|
-
failed_tests = tests_by_status['failed']
|
146
|
-
passed_tests = tests_by_status['passed']
|
147
|
-
other_tests = tests.reject do |test|
|
148
|
-
test.status == 'failed' || test.status == 'passed'
|
149
|
-
end
|
150
|
-
|
151
|
-
[
|
152
|
-
(failed_listings(failed_tests) if failed_tests),
|
153
|
-
(passed_listings(passed_tests) if passed_tests),
|
154
|
-
(other_listings(other_tests) if other_tests.any?)
|
155
|
-
].compact.join("\n\n")
|
156
|
-
end
|
157
|
-
|
158
|
-
def failed_listings(failed_tests)
|
159
|
-
generate_testcase_listing(failed_tests)
|
160
|
-
end
|
161
|
-
|
162
|
-
def passed_listings(passed_tests)
|
163
|
-
<<~MARKDOWN.chomp
|
164
|
-
<details><summary>Passed tests:</summary>
|
165
|
-
|
166
|
-
#{generate_testcase_listing(passed_tests, passed: true)}
|
167
|
-
|
168
|
-
</details>
|
169
|
-
MARKDOWN
|
170
|
-
end
|
171
|
-
|
172
|
-
def other_listings(other_tests)
|
173
|
-
<<~MARKDOWN.chomp
|
174
|
-
<details><summary>Other tests:</summary>
|
175
|
-
|
176
|
-
#{generate_testcase_listing(other_tests)}
|
177
|
-
|
178
|
-
</details>
|
179
|
-
MARKDOWN
|
180
|
-
end
|
181
|
-
|
182
|
-
def generate_testcase_listing(tests, passed: false)
|
183
|
-
body = tests.group_by(&:testcase).map do |testcase, tests_with_same_testcase|
|
184
|
-
tests_with_same_testcase.sort_by!(&:name)
|
185
|
-
[
|
186
|
-
generate_test_text(testcase, tests_with_same_testcase, passed),
|
187
|
-
generate_test_job(tests_with_same_testcase),
|
188
|
-
generate_test_status(tests_with_same_testcase),
|
189
|
-
generate_test_actions(tests_with_same_testcase)
|
190
|
-
].join(' | ')
|
191
|
-
end.join("\n")
|
192
|
-
|
193
|
-
<<~MARKDOWN.chomp
|
194
|
-
| Test | Job | Status | Action |
|
195
|
-
| - | - | - | - |
|
196
|
-
#{body}
|
197
|
-
MARKDOWN
|
198
|
-
end
|
199
|
-
|
200
|
-
def generate_test_text(testcase, tests_with_same_testcase, passed)
|
201
|
-
text = tests_with_same_testcase.map(&:name).uniq.join(', ')
|
202
|
-
encoded_text = ERB::Util.url_encode(text)
|
203
|
-
|
204
|
-
if testcase && !passed
|
205
|
-
# Workaround for reducing system notes on testcase issues
|
206
|
-
# 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.
|
207
|
-
"[#{text}](#{testcase.match(%r{[\s\S]+\/[^\/\d]+})}?state=opened&search=#{encoded_text})"
|
208
|
-
else
|
209
|
-
text
|
210
|
-
end
|
211
|
-
end
|
212
|
-
|
213
|
-
def generate_test_job(tests_with_same_testcase)
|
214
|
-
tests_with_same_testcase.map do |test|
|
215
|
-
ci_job_id = test.ci_job_url[/\d+\z/]
|
216
|
-
|
217
|
-
"[#{ci_job_id}](#{test.ci_job_url})#{' ~"quarantine"' if test.quarantine?}"
|
218
|
-
end.uniq.join(', ')
|
219
|
-
end
|
220
|
-
|
221
|
-
def generate_test_status(tests_with_same_testcase)
|
222
|
-
tests_with_same_testcase.map(&:status).uniq.map do |status|
|
223
|
-
%(~"#{status}")
|
224
|
-
end.join(', ')
|
225
|
-
end
|
226
|
-
|
227
|
-
def generate_test_actions(tests_with_same_testcase)
|
228
|
-
# All failed tests would be grouped together, meaning that
|
229
|
-
# if one failed, all the tests here would be failed too.
|
230
|
-
# So this check is safe. Same applies to 'passed'.
|
231
|
-
# But all other status might be mixing together,
|
232
|
-
# we cannot assume other statuses.
|
233
|
-
if tests_with_same_testcase.first.status == 'failed'
|
234
|
-
tests_having_failure_issue =
|
235
|
-
tests_with_same_testcase.select(&:failure_issue)
|
236
|
-
|
237
|
-
if tests_having_failure_issue.any?
|
238
|
-
items = tests_having_failure_issue.uniq(&:failure_issue).map do |test|
|
239
|
-
"<li>[ ] [failure issue](#{test.failure_issue})</li>"
|
240
|
-
end.join(' ')
|
241
|
-
|
242
|
-
"<ul>#{items}</ul>"
|
243
|
-
else
|
244
|
-
'<ul><li>[ ] failure issue exists or was created</li></ul>'
|
245
|
-
end
|
246
|
-
else
|
247
|
-
'-'
|
248
|
-
end
|
249
|
-
end
|
250
|
-
|
251
|
-
def generate_qa_issue_relation
|
252
|
-
return unless Runtime::Env.qa_issue_url
|
253
|
-
|
254
|
-
<<~MARKDOWN.chomp
|
255
|
-
## Release QA issue
|
256
|
-
|
257
|
-
* #{Runtime::Env.qa_issue_url}
|
258
|
-
|
259
|
-
/relate #{Runtime::Env.qa_issue_url}
|
260
|
-
MARKDOWN
|
261
|
-
end
|
262
|
-
|
263
|
-
def generate_link_to_dashboard
|
264
|
-
return unless Runtime::Env.qa_run_type
|
265
|
-
|
266
|
-
<<~MARKDOWN.chomp
|
267
|
-
## Link to Grafana dashboard for run-type of #{Runtime::Env.qa_run_type}
|
268
|
-
|
269
|
-
* https://dashboards.quality.gitlab.net/d/kuNYMgDnz/test-run-metrics?orgId=1&refresh=1m&var-run_type=#{Runtime::Env.qa_run_type}
|
270
|
-
MARKDOWN
|
271
|
-
end
|
272
|
-
end
|
273
|
-
end
|
274
|
-
end
|
275
|
-
end
|
@@ -1,190 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'gitlab'
|
4
|
-
|
5
|
-
module Gitlab
|
6
|
-
# Monkey patch the Gitlab client to use the correct API path and add required methods
|
7
|
-
class Client
|
8
|
-
def team_member(project, id)
|
9
|
-
get("/projects/#{url_encode(project)}/members/all/#{id}")
|
10
|
-
end
|
11
|
-
|
12
|
-
def issue_discussions(project, issue_id, options = {})
|
13
|
-
get("/projects/#{url_encode(project)}/issues/#{issue_id}/discussions", query: options)
|
14
|
-
end
|
15
|
-
|
16
|
-
def add_note_to_issue_discussion_as_thread(project, issue_id, discussion_id, options = {})
|
17
|
-
post("/projects/#{url_encode(project)}/issues/#{issue_id}/discussions/#{discussion_id}/notes", query: options)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
module QA
|
22
|
-
module Report
|
23
|
-
# The GitLab client is used for API access: https://github.com/NARKOZ/gitlab
|
24
|
-
class GitlabIssueClient
|
25
|
-
MAINTAINER_ACCESS_LEVEL = 40
|
26
|
-
RETRY_BACK_OFF_DELAY = 60
|
27
|
-
MAX_RETRY_ATTEMPTS = 3
|
28
|
-
|
29
|
-
def initialize(token:, project:)
|
30
|
-
@token = token
|
31
|
-
@project = project
|
32
|
-
@retry_backoff = 0
|
33
|
-
|
34
|
-
configure_gitlab_client
|
35
|
-
end
|
36
|
-
|
37
|
-
def assert_user_permission!
|
38
|
-
handle_gitlab_client_exceptions do
|
39
|
-
user = Gitlab.user
|
40
|
-
member = Gitlab.team_member(project, user.id)
|
41
|
-
|
42
|
-
abort_not_permitted if member.access_level < MAINTAINER_ACCESS_LEVEL
|
43
|
-
end
|
44
|
-
rescue Gitlab::Error::NotFound
|
45
|
-
abort_not_permitted
|
46
|
-
end
|
47
|
-
|
48
|
-
def find_issues(iid: nil, options: {}, &select)
|
49
|
-
select ||= :itself
|
50
|
-
|
51
|
-
handle_gitlab_client_exceptions do
|
52
|
-
return [Gitlab.issue(project, iid)].select(&select) if iid
|
53
|
-
|
54
|
-
Gitlab.issues(project, options)
|
55
|
-
.auto_paginate
|
56
|
-
.select(&select)
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
def find_issue_discussions(iid:)
|
61
|
-
handle_gitlab_client_exceptions do
|
62
|
-
Gitlab.issue_discussions(project, iid, order_by: 'created_at', sort: 'asc').auto_paginate
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
def create_issue(title:, description:, labels:, issue_type: 'issue')
|
67
|
-
attrs = { issue_type: issue_type, description: description, labels: labels }
|
68
|
-
|
69
|
-
handle_gitlab_client_exceptions do
|
70
|
-
Gitlab.create_issue(project, title, attrs)
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
def edit_issue(iid:, options: {})
|
75
|
-
handle_gitlab_client_exceptions do
|
76
|
-
Gitlab.edit_issue(project, iid, options)
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
def find_issue_notes(iid:)
|
81
|
-
handle_gitlab_client_exceptions do
|
82
|
-
Gitlab.issue_notes(project, iid, order_by: 'created_at', sort: 'asc')&.auto_paginate
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
def create_issue_note(iid:, note:)
|
87
|
-
handle_gitlab_client_exceptions do
|
88
|
-
Gitlab.create_issue_note(project, iid, note)
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
def edit_issue_note(issue_iid:, note_id:, note:)
|
93
|
-
handle_gitlab_client_exceptions do
|
94
|
-
Gitlab.edit_issue_note(project, issue_iid, note_id, note)
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
def add_note_to_issue_discussion_as_thread(iid:, discussion_id:, body:)
|
99
|
-
handle_gitlab_client_exceptions do
|
100
|
-
Gitlab.add_note_to_issue_discussion_as_thread(project, iid, discussion_id, body: body)
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
def find_user_id(username:)
|
105
|
-
handle_gitlab_client_exceptions do
|
106
|
-
user = Gitlab.users(username: username)&.first
|
107
|
-
user['id'] unless user.nil?
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
def upload_file(file_fullpath:)
|
112
|
-
ignore_gitlab_client_exceptions do
|
113
|
-
Gitlab.upload_file(project, file_fullpath)
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
|
-
def ignore_gitlab_client_exceptions
|
118
|
-
yield
|
119
|
-
rescue StandardError, SystemCallError, OpenSSL::SSL::SSLError, Net::OpenTimeout, Net::ReadTimeout, Gitlab::Error::Error => e
|
120
|
-
puts "Ignoring the following error: #{e}"
|
121
|
-
end
|
122
|
-
|
123
|
-
def handle_gitlab_client_exceptions
|
124
|
-
yield
|
125
|
-
rescue Gitlab::Error::NotFound
|
126
|
-
# This error could be raised in assert_user_permission!
|
127
|
-
# If so, we want it to terminate at that point
|
128
|
-
raise
|
129
|
-
rescue SystemCallError, OpenSSL::SSL::SSLError, Net::OpenTimeout, Net::ReadTimeout, Gitlab::Error::InternalServerError, Gitlab::Error::Parsing => e
|
130
|
-
@retry_backoff += RETRY_BACK_OFF_DELAY
|
131
|
-
|
132
|
-
raise if @retry_backoff > RETRY_BACK_OFF_DELAY * MAX_RETRY_ATTEMPTS
|
133
|
-
|
134
|
-
warn_exception(e)
|
135
|
-
warn("Sleeping for #{@retry_backoff} seconds before retrying...")
|
136
|
-
sleep @retry_backoff
|
137
|
-
|
138
|
-
retry
|
139
|
-
rescue StandardError => e
|
140
|
-
pipeline = QA::Runtime::Env.pipeline_from_project_name
|
141
|
-
channel = case pipeline
|
142
|
-
when "canary"
|
143
|
-
"qa-production"
|
144
|
-
when "staging-canary"
|
145
|
-
"qa-staging"
|
146
|
-
else
|
147
|
-
"qa-#{pipeline}"
|
148
|
-
end
|
149
|
-
error_msg = warn_exception(e)
|
150
|
-
|
151
|
-
return unless QA::Runtime::Env.ci_commit_ref_name == QA::Runtime::Env.default_branch
|
152
|
-
|
153
|
-
slack_options = {
|
154
|
-
channel: channel,
|
155
|
-
icon_emoji: ':ci_failing:',
|
156
|
-
message: <<~MSG
|
157
|
-
An unexpected error occurred while reporting test results in issues.
|
158
|
-
The error occurred in job: #{QA::Runtime::Env.ci_job_url}
|
159
|
-
`#{error_msg}`
|
160
|
-
MSG
|
161
|
-
}
|
162
|
-
puts "Posting Slack message to channel: #{channel}"
|
163
|
-
|
164
|
-
Gitlab::QA::Slack::PostToSlack.new(**slack_options).invoke!
|
165
|
-
end
|
166
|
-
|
167
|
-
private
|
168
|
-
|
169
|
-
attr_reader :token, :project
|
170
|
-
|
171
|
-
def configure_gitlab_client
|
172
|
-
Gitlab.configure do |config|
|
173
|
-
config.endpoint = Runtime::Env.gitlab_api_base
|
174
|
-
config.private_token = token
|
175
|
-
end
|
176
|
-
end
|
177
|
-
|
178
|
-
def abort_not_permitted
|
179
|
-
abort "You must have at least Maintainer access to the project to use this feature."
|
180
|
-
end
|
181
|
-
|
182
|
-
def warn_exception(error)
|
183
|
-
error_msg = "#{error.class.name} #{error.message}"
|
184
|
-
warn(error_msg)
|
185
|
-
error_msg
|
186
|
-
end
|
187
|
-
end
|
188
|
-
end
|
189
|
-
end
|
190
|
-
end
|