gitlab-qa 10.4.1 → 11.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/.gitlab/changelog_config.yml +13 -0
  3. data/.gitlab/merge_request_templates/Release.md +9 -36
  4. data/.rubocop_todo.yml +0 -12
  5. data/Dangerfile +1 -5
  6. data/Gemfile.lock +4 -4
  7. data/README.md +1 -2
  8. data/docs/running_against_remote_grid.md +39 -2
  9. data/gitlab-qa.gemspec +1 -1
  10. data/lib/gitlab/qa/component/gitaly_cluster.rb +0 -1
  11. data/lib/gitlab/qa/component/gitlab.rb +10 -9
  12. data/lib/gitlab/qa/component/selenoid.rb +8 -3
  13. data/lib/gitlab/qa/runtime/env.rb +21 -41
  14. data/lib/gitlab/qa/scenario/test/instance/airgapped.rb +0 -2
  15. data/lib/gitlab/qa/scenario/test/integration/gitaly_cluster.rb +0 -2
  16. data/lib/gitlab/qa/scenario/test/integration/mtls.rb +0 -1
  17. data/lib/gitlab/qa/scenario/test/integration/praefect.rb +0 -2
  18. data/lib/gitlab/qa/scenario/test/integration/registry_with_cdn.rb +2 -2
  19. data/lib/gitlab/qa/version.rb +1 -1
  20. data/lib/gitlab/qa.rb +0 -1
  21. data/support/data/admin_access_token_seed.rb +4 -1
  22. metadata +5 -39
  23. data/bin/slack +0 -14
  24. data/exe/gitlab-qa-report +0 -10
  25. data/lib/gitlab/qa/report/base_test_results.rb +0 -39
  26. data/lib/gitlab/qa/report/find_set_dri.rb +0 -43
  27. data/lib/gitlab/qa/report/generate_test_session.rb +0 -275
  28. data/lib/gitlab/qa/report/gitlab_issue_client.rb +0 -190
  29. data/lib/gitlab/qa/report/gitlab_issue_dry_client.rb +0 -28
  30. data/lib/gitlab/qa/report/j_unit_test_results.rb +0 -27
  31. data/lib/gitlab/qa/report/json_test_results.rb +0 -29
  32. data/lib/gitlab/qa/report/prepare_stage_reports.rb +0 -86
  33. data/lib/gitlab/qa/report/relate_failure_issue.rb +0 -374
  34. data/lib/gitlab/qa/report/report_as_issue.rb +0 -176
  35. data/lib/gitlab/qa/report/report_results.rb +0 -64
  36. data/lib/gitlab/qa/report/results_in_issues.rb +0 -126
  37. data/lib/gitlab/qa/report/results_in_testcases.rb +0 -111
  38. data/lib/gitlab/qa/report/results_reporter_shared.rb +0 -70
  39. data/lib/gitlab/qa/report/summary_table.rb +0 -43
  40. data/lib/gitlab/qa/report/test_result.rb +0 -184
  41. data/lib/gitlab/qa/report/update_screenshot_path.rb +0 -63
  42. data/lib/gitlab/qa/reporter.rb +0 -131
  43. data/lib/gitlab/qa/runtime/token_finder.rb +0 -44
  44. data/lib/gitlab/qa/slack/post_to_slack.rb +0 -30
  45. data/lib/gitlab/qa/system_logs/finders/json_log_finder.rb +0 -65
  46. data/lib/gitlab/qa/system_logs/finders/rails/api_log_finder.rb +0 -21
  47. data/lib/gitlab/qa/system_logs/finders/rails/application_log_finder.rb +0 -21
  48. data/lib/gitlab/qa/system_logs/finders/rails/exception_log_finder.rb +0 -21
  49. data/lib/gitlab/qa/system_logs/finders/rails/graphql_log_finder.rb +0 -21
  50. data/lib/gitlab/qa/system_logs/log_types/log.rb +0 -38
  51. data/lib/gitlab/qa/system_logs/log_types/rails/api_log.rb +0 -34
  52. data/lib/gitlab/qa/system_logs/log_types/rails/application_log.rb +0 -27
  53. data/lib/gitlab/qa/system_logs/log_types/rails/exception_log.rb +0 -23
  54. data/lib/gitlab/qa/system_logs/log_types/rails/graphql_log.rb +0 -30
  55. data/lib/gitlab/qa/system_logs/shared_fields.rb +0 -29
  56. 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: 10.4.1
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-05-18 00:00:00.000000000 Z
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.18.0
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.18.0
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,10 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- lib = File.expand_path('../lib', __dir__)
5
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
6
-
7
- require 'bundler/setup'
8
- require 'gitlab/qa'
9
-
10
- Gitlab::QA::Reporter.invoke(ARGV)
@@ -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