gitlab_quality-test_tooling 2.22.0 → 2.23.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: 9bc3f79d2fd78c6d8fd1ee931c50f126e8aa2c91a0608b6e3675605fffa5dd5e
4
- data.tar.gz: a80d4658c0781ec47d880b6dbf0166eb97f64742ce4e468ef86f9bc3e1e7615c
3
+ metadata.gz: 3c015244f26f542bc2a00450d783e83ce4067e785e4f3dcdd242178b5144a722
4
+ data.tar.gz: 46f7ed259a123fcee7fb3ce26f8e31aa15beed4f3eac08f41dfadb2715abfa76
5
5
  SHA512:
6
- metadata.gz: bb3d6fde7d83295c03059558525e6e4722416cd87dcf5e8a72c0b8f673dc384e08012d18aaed88877febd22cd400f039a24fb977ebbba985d0774e940b12f24e
7
- data.tar.gz: 4cd6cee22b30f7cc1bd6c6291f55c0ba6026d4688b04c00b054cbcb3598054e8a5f696758b7e23adad1853ae9222802ebe22179baea96027be0b88a4598d5b5b
6
+ metadata.gz: d5e93d7172b231765c6e58bd92eb567cb02fd1fdd522f749241359ce38af74ea058c8323a749e6eb54c5de6091236b3ab8a7494aa4da9870f0dfc5f7d1e2e41f
7
+ data.tar.gz: c9f71fd4df1d8af85264396cd0625e2b7cbaab934d164037c8edfc8bc90d542c780ea2394b9950c61e7e909cfcd40d482e66332c3cfecf1f2e58e29c80a77461
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- gitlab_quality-test_tooling (2.22.0)
4
+ gitlab_quality-test_tooling (2.23.0)
5
5
  activesupport (>= 7.0, < 7.3)
6
6
  amatch (~> 0.4.1)
7
7
  fog-google (~> 1.24, >= 1.24.1)
data/exe/post-to-slack CHANGED
@@ -77,6 +77,10 @@ options = OptionParser.new do |opts|
77
77
  params[:icon_emoji] = icon_emoji
78
78
  end
79
79
 
80
+ opts.on('-e', '--environment-issues-file FILE', String, 'Add environment issues alert based on grouped failure data in a file') do |file|
81
+ params[:environment_issues_file] = file
82
+ end
83
+
80
84
  opts.on_tail('-v', '--version', 'Show the version') do
81
85
  require_relative "../lib/gitlab_quality/test_tooling/version"
82
86
  puts "#{$PROGRAM_NAME} : #{GitlabQuality::TestTooling::VERSION}"
@@ -71,6 +71,7 @@ module GitlabQuality
71
71
  exclude_labels_for_search: nil,
72
72
  metrics_files: [],
73
73
  group_similar: false,
74
+ environment_issues_output_file: nil,
74
75
  **kwargs)
75
76
  super
76
77
  @max_diff_ratio = max_diff_ratio.to_f
@@ -81,11 +82,13 @@ module GitlabQuality
81
82
  @commented_issue_list = Set.new
82
83
  @metrics_files = Array(metrics_files)
83
84
  @group_similar = group_similar
85
+ @environment_issues_output_file = environment_issues_output_file
84
86
  end
85
87
 
86
88
  private
87
89
 
88
- attr_reader :max_diff_ratio, :system_logs, :base_issue_labels, :exclude_labels_for_search, :metrics_files, :ops_gitlab_client, :group_similar
90
+ attr_reader :max_diff_ratio, :system_logs, :base_issue_labels, :exclude_labels_for_search, :metrics_files, :ops_gitlab_client, :group_similar,
91
+ :environment_issues_output_file
89
92
 
90
93
  def run!
91
94
  puts "Reporting test failures in `#{files.join(',')}` as issues in project `#{project}` via the API at `#{Runtime::Env.gitlab_api_base}`."
@@ -123,6 +126,8 @@ module GitlabQuality
123
126
 
124
127
  grouper.process_failures(failure_data)
125
128
  grouper.process_issues
129
+
130
+ export_environment_issues_for_slack(environment_issues_output_file) if environment_issues_output_file
126
131
  end
127
132
 
128
133
  def collect_all_test_results
@@ -690,6 +695,43 @@ module GitlabQuality
690
695
 
691
696
  "|| [Screenshot](#{ci_job_url}/artifacts/file/#{screenshot_path})"
692
697
  end
698
+
699
+ def export_environment_issues_for_slack(output_file)
700
+ return unless similar_issues_grouped?
701
+
702
+ File.write(output_file, build_environment_issues_data.to_json)
703
+ rescue StandardError => e
704
+ puts "Warning: Failed to export environment issues for Slack: #{e.message}"
705
+ end
706
+
707
+ def build_environment_issues_data
708
+ {
709
+ grouped_failures: format_grouped_failures,
710
+ summary: grouper.summary
711
+ }
712
+ end
713
+
714
+ def format_grouped_failures
715
+ grouper.grouped_failures.map do |_fingerprint, grouped_failure|
716
+ {
717
+ fingerprint: grouped_failure[:fingerprint],
718
+ pattern_name: grouped_failure[:pattern_name],
719
+ normalized_message: grouped_failure[:normalized_message],
720
+ failure_count: grouped_failure[:failures].size,
721
+ failures: format_individual_failures(grouped_failure[:failures])
722
+ }
723
+ end
724
+ end
725
+
726
+ def format_individual_failures(failures)
727
+ failures.map do |failure|
728
+ {
729
+ description: failure[:description],
730
+ file_path: failure[:file_path],
731
+ ci_job_url: failure[:ci_job_url]
732
+ }
733
+ end
734
+ end
693
735
  end
694
736
  end
695
737
  end
@@ -1,15 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'json'
4
+
3
5
  module GitlabQuality
4
6
  module TestTooling
5
7
  module Slack
6
8
  class PostToSlack
7
- def initialize(slack_webhook_url:, channel:, message:, username:, icon_emoji:)
9
+ MAX_PATTERN_MESSAGE_LENGTH = 150
10
+ MAX_GROUPED_FAILURES_TO_DISPLAY = 10
11
+
12
+ def initialize(slack_webhook_url:, channel:, message:, username:, icon_emoji:, environment_issues_file: nil)
8
13
  @slack_webhook_url = slack_webhook_url
9
14
  @channel = channel
10
15
  @message = message
11
16
  @username = username
12
17
  @icon_emoji = icon_emoji
18
+ @environment_issues_file = environment_issues_file
13
19
  end
14
20
 
15
21
  def invoke!
@@ -17,7 +23,7 @@ module GitlabQuality
17
23
  params['channel'] = channel
18
24
  params['username'] = username
19
25
  params['icon_emoji'] = icon_emoji
20
- params['text'] = message
26
+ params['text'] = build_message
21
27
 
22
28
  Support::HttpRequest.make_http_request(
23
29
  method: 'post',
@@ -29,7 +35,101 @@ module GitlabQuality
29
35
 
30
36
  private
31
37
 
32
- attr_reader :slack_webhook_url, :channel, :message, :username, :icon_emoji
38
+ attr_reader :slack_webhook_url, :channel, :message, :username, :icon_emoji, :environment_issues_file
39
+
40
+ def build_message
41
+ messages = []
42
+ messages << message if message && !message.empty?
43
+ messages << format_environment_issues if environment_issues_file && File.exist?(environment_issues_file)
44
+
45
+ messages.join("\n\n")
46
+ end
47
+
48
+ def format_environment_issues
49
+ issues_data = JSON.parse(File.read(environment_issues_file))
50
+ return nil if issues_data.nil? || issues_data['grouped_failures'].empty?
51
+
52
+ build_slack_message(issues_data)
53
+ rescue JSON::ParserError => e
54
+ ":x: Error parsing environment issues file: #{e.message}"
55
+ rescue StandardError => e
56
+ ":x: Error formatting environment issues: #{e.message}"
57
+ end
58
+
59
+ def format_single_environment_issue(failure)
60
+ pattern_title = pattern_display_name(failure['pattern_name'])
61
+
62
+ issue_text = build_issue_header(pattern_title, failure)
63
+ issue_text + build_job_info(failure)
64
+ end
65
+
66
+ def pattern_display_name(pattern_name)
67
+ case pattern_name&.downcase
68
+ when /http_500/
69
+ "HTTP 500 Internal Server Errors"
70
+ when /http_400/
71
+ "HTTP 400 Bad Request Errors"
72
+ when /http_503/
73
+ "HTTP 503 Service Unavailable"
74
+ when /timeout/
75
+ "Timeout Errors"
76
+ when /git_rpc|repository/
77
+ "Git/Repository Errors"
78
+ else
79
+ "#{pattern_name&.humanize || 'Unknown'} Errors"
80
+ end
81
+ end
82
+
83
+ def build_slack_message(issues_data)
84
+ header = ":warning: *Environment Issues Detected*\n"
85
+
86
+ issue_messages = format_issue_messages(issues_data['grouped_failures'])
87
+ truncation_note = build_truncation_note(issues_data['grouped_failures'].size)
88
+ summary = build_summary_text(issues_data['summary'])
89
+
90
+ header + issue_messages + truncation_note + summary
91
+ end
92
+
93
+ def format_issue_messages(grouped_failures)
94
+ failures_to_show = grouped_failures.first(MAX_GROUPED_FAILURES_TO_DISPLAY)
95
+ failures_to_show.map { |failure| format_single_environment_issue(failure) }.join("\n\n")
96
+ end
97
+
98
+ def build_truncation_note(total_failures)
99
+ return "" unless total_failures > MAX_GROUPED_FAILURES_TO_DISPLAY
100
+
101
+ "\n_... and #{total_failures - MAX_GROUPED_FAILURES_TO_DISPLAY} more environment issue(s)_"
102
+ end
103
+
104
+ def build_issue_header(pattern_title, failure)
105
+ <<~TEXT
106
+ *#{pattern_title}*
107
+ • Affected tests: #{failure['failure_count']}
108
+ • Pattern: `#{truncate_message(failure['normalized_message'])}`
109
+ TEXT
110
+ end
111
+
112
+ def build_job_info(failure)
113
+ return "" unless failure['failures']&.any?
114
+
115
+ job_urls = failure['failures'].filter_map { |f| f['ci_job_url'] }.uniq
116
+ job_urls.any? ? "• Jobs affected: #{job_urls.size}\n" : ""
117
+ end
118
+
119
+ def build_summary_text(summary)
120
+ <<~TEXT
121
+
122
+ *Summary:* #{summary['grouped_issues']} environment issue(s) affecting #{summary['total_grouped_failures']} test(s)
123
+
124
+ _Note: Future improvements will include direct GitLab issue links and enhanced filtering._
125
+ _Track progress: https://gitlab.com/groups/gitlab-org/quality/quality-engineering/-/epics/168_
126
+ TEXT
127
+ end
128
+
129
+ def truncate_message(message)
130
+ text = message.to_s
131
+ text.length > MAX_PATTERN_MESSAGE_LENGTH ? "#{text[0..MAX_PATTERN_MESSAGE_LENGTH]}..." : text
132
+ end
33
133
  end
34
134
  end
35
135
  end
@@ -78,7 +78,7 @@ module GitlabQuality
78
78
  )
79
79
  ENGINE = MergeTree()
80
80
  PARTITION BY toYYYYMM(timestamp)
81
- ORDER BY (ci_project_path, timestamp, run_type, status, feature_category, file_path, ci_pipeline_id)
81
+ ORDER BY (ci_project_path, status, run_type, feature_category, file_path, timestamp, ci_pipeline_id)
82
82
  SETTINGS index_granularity = 8192;
83
83
  SQL
84
84
  return if config.extra_metadata_columns.empty?
@@ -2,6 +2,6 @@
2
2
 
3
3
  module GitlabQuality
4
4
  module TestTooling
5
- VERSION = "2.22.0"
5
+ VERSION = "2.23.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.22.0
4
+ version: 2.23.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-10-15 00:00:00.000000000 Z
11
+ date: 2025-10-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: climate_control