gitlab_quality-test_tooling 2.6.0 → 2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/lib/gitlab_quality/test_tooling/gitlab_client/gitlab_client.rb +4 -3
- data/lib/gitlab_quality/test_tooling/gitlab_client/issues_client.rb +24 -0
- data/lib/gitlab_quality/test_tooling/report/relate_failure_issue.rb +135 -5
- data/lib/gitlab_quality/test_tooling/runtime/env.rb +3 -1
- data/lib/gitlab_quality/test_tooling/test_result/base_test_result.rb +3 -1
- data/lib/gitlab_quality/test_tooling/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bc2b98142fc0891fe937da031f64a665ef016c153263b01b7c9820b284967800
|
4
|
+
data.tar.gz: 588d0cec55fb89405d61d6e41528343929d948fe6fb03717edf2e895fe727e5c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2402289c4b5282ad62289b7560b0faadc8cabc675d7e0262b38e6582cee5e2e6a2711ab053dc677d2e32d917c8a29608e8b90b485d977d4fe244c6a7e4c47505
|
7
|
+
data.tar.gz: 4bebf905075eb9b8ca2fb707c004d78746d8d8e518a6e10ee7d1085a2e3450b8a33d672a9ef382103deaf996591bd1f7c6b5d86f2249276a7a576a61074acbd7
|
data/Gemfile.lock
CHANGED
@@ -9,10 +9,11 @@ module GitlabQuality
|
|
9
9
|
RETRY_BACK_OFF_DELAY = 60
|
10
10
|
MAX_RETRY_ATTEMPTS = 3
|
11
11
|
|
12
|
-
def initialize(token:, project:, **_kwargs)
|
12
|
+
def initialize(token:, project:, endpoint: nil, **_kwargs)
|
13
13
|
@token = token
|
14
14
|
@project = project
|
15
15
|
@retry_backoff = 0
|
16
|
+
@endpoint = endpoint
|
16
17
|
end
|
17
18
|
|
18
19
|
def handle_gitlab_client_exceptions
|
@@ -76,11 +77,11 @@ module GitlabQuality
|
|
76
77
|
|
77
78
|
private
|
78
79
|
|
79
|
-
attr_reader :project, :token
|
80
|
+
attr_reader :project, :token, :endpoint
|
80
81
|
|
81
82
|
def client
|
82
83
|
@client ||= Gitlab.client(
|
83
|
-
endpoint: Runtime::Env.gitlab_api_base,
|
84
|
+
endpoint: endpoint || Runtime::Env.gitlab_api_base,
|
84
85
|
private_token: token
|
85
86
|
)
|
86
87
|
end
|
@@ -128,6 +128,12 @@ module GitlabQuality
|
|
128
128
|
end
|
129
129
|
end
|
130
130
|
|
131
|
+
def find_commit(project, sha)
|
132
|
+
handle_gitlab_client_exceptions do
|
133
|
+
client.commit(project, sha)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
131
137
|
def find_commit_parent(project, sha)
|
132
138
|
handle_gitlab_client_exceptions do
|
133
139
|
# In a merged results commit, the first parent is the one from
|
@@ -137,6 +143,24 @@ module GitlabQuality
|
|
137
143
|
end
|
138
144
|
end
|
139
145
|
|
146
|
+
def find_commit_diff(project, sha)
|
147
|
+
handle_gitlab_client_exceptions do
|
148
|
+
client.commit_diff(project, sha)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def find_deployments(project, environment:, status:, order_by: 'id', sort: 'desc')
|
153
|
+
handle_gitlab_client_exceptions do
|
154
|
+
client.deployments(
|
155
|
+
project,
|
156
|
+
environment: environment,
|
157
|
+
status: status,
|
158
|
+
order_by: order_by,
|
159
|
+
sort: sort
|
160
|
+
)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
140
164
|
private
|
141
165
|
|
142
166
|
attr_reader :token, :project
|
@@ -33,6 +33,21 @@ module GitlabQuality
|
|
33
33
|
'gitlab-org/customers-gitlab-com' => 'gitlab-org/customers-gitlab-com'
|
34
34
|
}.freeze
|
35
35
|
|
36
|
+
# The project contains record of the deployments we use to determine the commit diff
|
37
|
+
OPS_RELEASES_METADATA_PROJECT = 'gitlab-org/release/metadata'
|
38
|
+
|
39
|
+
# Naming of the environments are different between `gitlab-org/release/metadata` and `gitlab-org/quality`
|
40
|
+
# This maps the gitlab-org/quality environment names to the equivalent in `gitlab-org/release/metadata`
|
41
|
+
ENVIRONMENT_MAPPING = {
|
42
|
+
'production' => 'gprd',
|
43
|
+
'canary' => 'gprd-cny',
|
44
|
+
'staging' => 'gstg',
|
45
|
+
'staging-canary' => 'gstg-cny',
|
46
|
+
'staging-ref' => 'gstg-ref',
|
47
|
+
'preprod' => 'pre',
|
48
|
+
'release' => 'release'
|
49
|
+
}.freeze
|
50
|
+
|
36
51
|
MultipleIssuesFound = Class.new(StandardError)
|
37
52
|
|
38
53
|
def initialize(
|
@@ -54,7 +69,7 @@ module GitlabQuality
|
|
54
69
|
|
55
70
|
private
|
56
71
|
|
57
|
-
attr_reader :max_diff_ratio, :system_logs, :base_issue_labels, :exclude_labels_for_search, :metrics_files
|
72
|
+
attr_reader :max_diff_ratio, :system_logs, :base_issue_labels, :exclude_labels_for_search, :metrics_files, :ops_gitlab_client
|
58
73
|
|
59
74
|
def run!
|
60
75
|
puts "Reporting test failures in `#{files.join(',')}` as issues in project `#{project}` via the API at `#{Runtime::Env.gitlab_api_base}`."
|
@@ -278,7 +293,7 @@ module GitlabQuality
|
|
278
293
|
|
279
294
|
def new_issue_description(test)
|
280
295
|
super + [
|
281
|
-
"\n#{
|
296
|
+
"\n#{commit_diff_section}",
|
282
297
|
"### Stack trace",
|
283
298
|
"```\n#{test.full_stacktrace}\n```",
|
284
299
|
screenshot_section(test),
|
@@ -287,8 +302,123 @@ module GitlabQuality
|
|
287
302
|
].compact.join("\n\n")
|
288
303
|
end
|
289
304
|
|
290
|
-
def
|
291
|
-
|
305
|
+
def commit_diff_section
|
306
|
+
"### Commit diff\n#{generate_diff_link}"
|
307
|
+
end
|
308
|
+
|
309
|
+
def generate_diff_link
|
310
|
+
initialize_gitlab_ops_client
|
311
|
+
|
312
|
+
if Runtime::Env.ci_pipeline_url.include?('ops.gitlab.net')
|
313
|
+
pipeline = ops_gitlab_client.find_pipeline(project, Runtime::Env.ci_pipeline_id.to_i)
|
314
|
+
generate_ops_gitlab_diff(pipeline)
|
315
|
+
else
|
316
|
+
pipeline = gitlab.find_pipeline(project, Runtime::Env.ci_pipeline_id.to_i)
|
317
|
+
generate_gitlab_diff(pipeline)
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
def generate_ops_gitlab_diff(pipeline)
|
322
|
+
deployment_info = fetch_deployment_info(pipeline)
|
323
|
+
|
324
|
+
return deployment_info if deployment_info.is_a?(String)
|
325
|
+
|
326
|
+
source_gitlab_ee_sha = fetch_deployment_gitlab_ee_sha(ops_gitlab_client, deployment_info[:source].sha)
|
327
|
+
target_gitlab_ee_sha = fetch_deployment_gitlab_ee_sha(ops_gitlab_client, deployment_info[:target].sha)
|
328
|
+
|
329
|
+
if source_gitlab_ee_sha == target_gitlab_ee_sha
|
330
|
+
"No diff"
|
331
|
+
else
|
332
|
+
"https://gitlab.com/gitlab-org/security/gitlab/-/compare/#{target_gitlab_ee_sha}...#{source_gitlab_ee_sha}"
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
def fetch_deployment_info(pipeline)
|
337
|
+
pipeline_deploy_version = Runtime::Env.ci_pipeline_name.match(/(\d+\.\d+\.\d+)(?:-|$)/)&.captures&.first
|
338
|
+
deployments = fetch_deployments(ops_gitlab_client, pipeline)
|
339
|
+
found_deployment = find_matching_deployment(pipeline_deploy_version, deployments)
|
340
|
+
|
341
|
+
return 'No matching deployment found to generate a diff link.' unless found_deployment
|
342
|
+
|
343
|
+
{
|
344
|
+
source: found_deployment[:deployment],
|
345
|
+
target: deployments[found_deployment[:index] + 1]
|
346
|
+
}
|
347
|
+
end
|
348
|
+
|
349
|
+
def fetch_deployments(client, pipeline)
|
350
|
+
ops_environment = pipeline.web_url.match(%r{gitlab-org/quality/([^/-]+(?:-[^/-]+)*?)(?:/-)?/pipelines})[1]
|
351
|
+
|
352
|
+
raise "Environment '#{ops_environment}' is not supported" unless ENVIRONMENT_MAPPING.key?(ops_environment)
|
353
|
+
|
354
|
+
environment = ENVIRONMENT_MAPPING[ops_environment]
|
355
|
+
|
356
|
+
client.find_deployments(
|
357
|
+
OPS_RELEASES_METADATA_PROJECT,
|
358
|
+
environment: environment,
|
359
|
+
status: 'success'
|
360
|
+
)
|
361
|
+
end
|
362
|
+
|
363
|
+
def initialize_gitlab_ops_client
|
364
|
+
@ops_gitlab_client = GitlabClient::IssuesClient.new(
|
365
|
+
endpoint: Runtime::Env.ci_api_v4_url,
|
366
|
+
token: Runtime::Env.gitlab_ci_token,
|
367
|
+
project: OPS_RELEASES_METADATA_PROJECT
|
368
|
+
)
|
369
|
+
end
|
370
|
+
|
371
|
+
def find_matching_deployment(pipeline_deploy_version, deployments)
|
372
|
+
return nil unless pipeline_deploy_version
|
373
|
+
return nil unless deployments
|
374
|
+
|
375
|
+
target_timestamp = extract_timestamp(pipeline_deploy_version)
|
376
|
+
return nil unless target_timestamp
|
377
|
+
|
378
|
+
matching_deployment = nil
|
379
|
+
|
380
|
+
deployments.each_with_index do |deployment, index|
|
381
|
+
deployment_version = extract_deployment_version(ops_gitlab_client, deployment)
|
382
|
+
next unless deployment_version
|
383
|
+
|
384
|
+
deployment_timestamp = extract_timestamp(deployment_version)
|
385
|
+
next unless deployment_timestamp
|
386
|
+
|
387
|
+
# Stop searching if the deployment timestamp is older than the target timestamp
|
388
|
+
break if deployment_timestamp && deployment_timestamp < target_timestamp
|
389
|
+
|
390
|
+
if deployment_version == pipeline_deploy_version
|
391
|
+
matching_deployment = { deployment: deployment, index: index }
|
392
|
+
break
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
matching_deployment
|
397
|
+
end
|
398
|
+
|
399
|
+
def extract_timestamp(version)
|
400
|
+
version.match(/\d{12}$/)&.to_s
|
401
|
+
end
|
402
|
+
|
403
|
+
def extract_deployment_version(client, deployment)
|
404
|
+
commit = client.find_commit(OPS_RELEASES_METADATA_PROJECT, deployment.sha)
|
405
|
+
version_match = commit.message&.match(/Product-Version: ([\d.]+)/)
|
406
|
+
version_match&.[](1)
|
407
|
+
end
|
408
|
+
|
409
|
+
def fetch_deployment_gitlab_ee_sha(client, deployment_sha)
|
410
|
+
commit_diff = client.find_commit_diff(OPS_RELEASES_METADATA_PROJECT, deployment_sha).first
|
411
|
+
diffs_content = commit_diff.diff.lines.select { |line| line.start_with?('+') }.map { |line| line[1..] }.join
|
412
|
+
|
413
|
+
begin
|
414
|
+
JSON.parse(diffs_content).dig('releases', 'gitlab-ee', 'sha')
|
415
|
+
rescue JSON::ParserError
|
416
|
+
raise "Failed to parse the diffs content"
|
417
|
+
end
|
418
|
+
end
|
419
|
+
|
420
|
+
def generate_gitlab_diff(pipeline)
|
421
|
+
pipeline_sha = pipeline.sha
|
292
422
|
parent_sha = gitlab.find_commit_parent(project, pipeline_sha)
|
293
423
|
diff_project = if DIFF_PROJECT_MAPPINGS.key?(project)
|
294
424
|
DIFF_PROJECT_MAPPINGS[project]
|
@@ -296,7 +426,7 @@ module GitlabQuality
|
|
296
426
|
raise "Project #{project} is not supported for commit diff links"
|
297
427
|
end
|
298
428
|
|
299
|
-
"
|
429
|
+
"https://gitlab.com/#{diff_project}/-/compare/#{parent_sha}...#{pipeline_sha}"
|
300
430
|
end
|
301
431
|
|
302
432
|
def system_log_errors_section(test)
|
@@ -19,9 +19,11 @@ module GitlabQuality
|
|
19
19
|
'CI_PROJECT_NAME' => :ci_project_name,
|
20
20
|
'CI_PROJECT_PATH' => :ci_project_path,
|
21
21
|
'CI_PIPELINE_ID' => :ci_pipeline_id,
|
22
|
+
'CI_PIPELINE_NAME' => :ci_pipeline_name,
|
22
23
|
'CI_PIPELINE_URL' => :ci_pipeline_url,
|
23
24
|
'SLACK_QA_CHANNEL' => :slack_qa_channel,
|
24
|
-
'DEPLOY_VERSION' => :deploy_version
|
25
|
+
'DEPLOY_VERSION' => :deploy_version,
|
26
|
+
'QA_GITLAB_CI_TOKEN' => :gitlab_ci_token
|
25
27
|
}.freeze
|
26
28
|
|
27
29
|
ENV_VARIABLES.each do |env_name, method_name|
|
@@ -11,7 +11,9 @@ module GitlabQuality
|
|
11
11
|
"unexpected token at 'GitLab is not responding'",
|
12
12
|
"GitLab: Internal API error (502).",
|
13
13
|
"could not be found (502)",
|
14
|
-
"Error reference number: 502"
|
14
|
+
"Error reference number: 502",
|
15
|
+
"(502): `GitLab is not responding`",
|
16
|
+
"<head><title>502 Bad Gateway</title></head>"
|
15
17
|
].freeze
|
16
18
|
|
17
19
|
SHARED_EXAMPLES_CALLERS = %w[include_examples it_behaves_like].freeze
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gitlab_quality-test_tooling
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.7.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: 2025-
|
11
|
+
date: 2025-02-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: climate_control
|