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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 279a7eab459c664263a3506753c09d78111784af48af599cab26ed8928f54e2a
4
- data.tar.gz: '06388341bfbb2c868ae25569af2597666f64cdb7e980465078a78778081ca107'
3
+ metadata.gz: bc2b98142fc0891fe937da031f64a665ef016c153263b01b7c9820b284967800
4
+ data.tar.gz: 588d0cec55fb89405d61d6e41528343929d948fe6fb03717edf2e895fe727e5c
5
5
  SHA512:
6
- metadata.gz: cd37eca775b6899df0e476d11499ee491a8ea4278d88bf8f9584feb3cb8d1245da3319aa534ed4743c06ca1bd7da7a4eefcf619aa26688eb895bf3168d58eb9d
7
- data.tar.gz: 58f7c549202b39a20d6d5b5fc117e9017a044ee2e6bff63f07b82dda9650ed819f43407f20a5363d638b986fb8860d69c2452ea68bdf7fe5b7e33e70abb281b4
6
+ metadata.gz: 2402289c4b5282ad62289b7560b0faadc8cabc675d7e0262b38e6582cee5e2e6a2711ab053dc677d2e32d917c8a29608e8b90b485d977d4fe244c6a7e4c47505
7
+ data.tar.gz: 4bebf905075eb9b8ca2fb707c004d78746d8d8e518a6e10ee7d1085a2e3450b8a33d672a9ef382103deaf996591bd1f7c6b5d86f2249276a7a576a61074acbd7
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- gitlab_quality-test_tooling (2.6.0)
4
+ gitlab_quality-test_tooling (2.7.0)
5
5
  activesupport (>= 7.0, < 7.2)
6
6
  amatch (~> 0.4.1)
7
7
  fog-google (~> 1.24, >= 1.24.1)
@@ -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#{compare_against_previous_commit}",
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 compare_against_previous_commit
291
- pipeline_sha = gitlab.find_pipeline(project, Runtime::Env.ci_pipeline_id.to_i).sha
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
- "### Commit diff\nhttps://gitlab.com/#{diff_project}/-/compare/#{parent_sha}...#{pipeline_sha}"
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
@@ -2,6 +2,6 @@
2
2
 
3
3
  module GitlabQuality
4
4
  module TestTooling
5
- VERSION = "2.6.0"
5
+ VERSION = "2.7.0"
6
6
  end
7
7
  end
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.6.0
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-01-24 00:00:00.000000000 Z
11
+ date: 2025-02-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: climate_control