bugsnag-maze-runner 9.32.4 → 9.34.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: 1e914d52a832ce9c1596600d911196bc7c1c384ce46d27cdb2619549db7330dc
4
- data.tar.gz: b2bd2bbb29542442035fce4bc11dc21325ac2cbcfecc3a18e5d59a3df13df6a2
3
+ metadata.gz: 80810dfc99cf7946d853a4a799dd90a899cec2136e35fd6ee1bc837de7eb112c
4
+ data.tar.gz: 57443e7f4fb41bd51aa3b81e66e1e41827f54d29a9f479c2fd69de947a391116
5
5
  SHA512:
6
- metadata.gz: 7f2643e9018637de0670df405b64da137caad4c7e7a22acdf855da385ab9157d3c2b77336ece49e22fa2b04fc86af85f294e821b8b9f66e6cda39c84bc199e2a
7
- data.tar.gz: b61855618b15bcc14ee9c0ea154747a11a10f42da1550171eb99b623e610b1e05e862404acf4da4cb802d06570c17c6a8e96db6fc9c9aaeb0e3c7b9b97196654
6
+ metadata.gz: 375f8b7e1e2b9fce8477dd746119ff8c713a56100233b684db2f73d6af14b87cb587462f2d180cc7066fc2db93bc222b08b323b0a200c30f82aa070ba5cf3d67
7
+ data.tar.gz: f7170735f827fcd5964aadf1336a8c0105d87d93bfc39400040ce0f7738beeb422b879fb7c7daa3edb4ce6b264cb0ecbaa26b8cb4f464247e4d24fefbdf41333
@@ -0,0 +1,186 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'csv'
5
+ require 'time'
6
+ require 'optimist'
7
+ require 'datadog_api_client'
8
+
9
+ require_relative '../lib/maze'
10
+ require_relative '../lib/maze/loggers/logger'
11
+
12
+ class DatadogMetricsIngester
13
+ def initialize(api_key:, app_key:, host: 'platforms-benchmark')
14
+ configuration = DatadogAPIClient::Configuration.new
15
+ configuration.api_key['apiKeyAuth'] = api_key
16
+ configuration.api_key['appKeyAuth'] = app_key
17
+
18
+ api_client = DatadogAPIClient::APIClient.new(configuration)
19
+ @api = DatadogAPIClient::V2::MetricsAPI.new(api_client)
20
+ @host = host
21
+ end
22
+
23
+ def self.run(args)
24
+ parser = Optimist::Parser.new do
25
+ text 'Ingest CSV metrics into Datadog'
26
+ opt :csv_file, 'Path to the CSV file containing metrics', type: :string, required: true
27
+ opt :api_key, 'Datadog API key', type: :string
28
+ opt :app_key, 'Datadog App key', type: :string
29
+ end
30
+
31
+ opts = Optimist::with_standard_exception_handling(parser) do
32
+ raise Optimist::HelpNeeded if args.empty?
33
+ parser.parse(args)
34
+ end
35
+
36
+ csv_file = opts[:csv_file]
37
+ api_key = opts[:api_key] || ENV['DATADOG_API_KEY']
38
+ app_key = opts[:app_key] || ENV['DATADOG_APP_KEY']
39
+
40
+ abort_with_help(parser, "CSV file is missing") if csv_file.to_s.empty?
41
+ abort_with_help(parser, "API key is missing") if api_key.to_s.empty?
42
+ abort_with_help(parser, "App key is missing") if app_key.to_s.empty?
43
+
44
+ new(api_key: api_key, app_key: app_key).ingest_csv(csv_file)
45
+ end
46
+
47
+ def self.abort_with_help(parser, message)
48
+ $logger.warn message
49
+ Optimist::with_standard_exception_handling(parser) { raise Optimist::HelpNeeded }
50
+ end
51
+
52
+ def ingest_csv(file_path)
53
+ total_rows = 0
54
+
55
+ CSV.foreach(file_path, headers: true) do |row|
56
+ next unless valid_benchmark_row?(row)
57
+
58
+ total_rows += 1
59
+ benchmark_name = row['benchmark']
60
+ tags = build_tags(row)
61
+ timestamp = parse_timestamp(row['timestamp'])
62
+
63
+ $logger.info "Processing row #{total_rows}: #{benchmark_name} at #{row['timestamp']}"
64
+
65
+ process_cpu_metrics(row, tags, timestamp)
66
+ process_run_metrics(row, tags, timestamp)
67
+ process_total_metrics(row, tags, timestamp)
68
+
69
+ $logger.info "Completed processing #{benchmark_name}"
70
+ $logger.info '-' * 60
71
+
72
+ # Sleep briefly to avoid overwhelming the Datadog API
73
+ sleep(0.1)
74
+ end
75
+
76
+ $logger.info "Ingestion Summary:"
77
+ $logger.info "Total rows processed: #{total_rows}"
78
+ end
79
+
80
+ def valid_benchmark_row?(row)
81
+ row['benchmark'] && !row['benchmark'].strip.empty?
82
+ end
83
+
84
+ def parse_timestamp(timestamp_str)
85
+ Time.strptime(timestamp_str, "%a %b %d %H:%M:%S GMT %Y").to_i
86
+ rescue ArgumentError
87
+ Time.now.to_i
88
+ end
89
+
90
+ def build_tags(row)
91
+ tags = ["env:platforms-testing", "benchmark:#{row['benchmark']}"]
92
+ tags << "cpu_intensive:true" if row['cpu'] == 'true'
93
+ tags << "memory_intensive:true" if row['memory'] == 'true'
94
+ tags << "rendering_enabled:true" if row['rendering'] == 'true'
95
+ tags
96
+ end
97
+
98
+ def send_metric(name, value, tags, type = DatadogAPIClient::V2::MetricIntakeType::GAUGE, timestamp = Time.now.to_i)
99
+ metric_series = DatadogAPIClient::V2::MetricSeries.new(
100
+ metric: name,
101
+ type: type,
102
+ points: [
103
+ DatadogAPIClient::V2::MetricPoint.new(timestamp: timestamp, value: value)
104
+ ],
105
+ tags: tags,
106
+ resources: [
107
+ DatadogAPIClient::V2::MetricResource.new(name: @host, type: "host")
108
+ ]
109
+ )
110
+
111
+ payload = DatadogAPIClient::V2::MetricPayload.new(series: [metric_series])
112
+
113
+ begin
114
+ @api.submit_metrics(payload)
115
+ rescue DatadogAPIClient::APIError => e
116
+ $logger.error "Failed to send metric #{name}: #{e.message}"
117
+ $logger.error "HTTP status code: #{e.code}"
118
+ $logger.error "Response body: #{e.response_body}"
119
+ end
120
+ end
121
+
122
+ def process_cpu_metrics(row, tags, timestamp)
123
+ # Select all headers matching cpuUse.<number> pattern and extract their float values
124
+ cpu_values = row.headers.grep(/^cpuUse\.\d+$/).map { |key|
125
+ val = row[key]
126
+ val&.empty? ? nil : val.to_f
127
+ }.compact
128
+
129
+ return if cpu_values.empty?
130
+
131
+ avg_cpu = cpu_values.sum / cpu_values.size
132
+ send_metric('benchmark.cpu_usage_percent', avg_cpu, tags, timestamp)
133
+ end
134
+
135
+ def process_run_metrics(row, base_tags, timestamp)
136
+ # Extract unique run indices from headers like measuredTime.1, iterations.2, etc.
137
+ run_indices = row.headers
138
+ .grep(/^(measuredTime|iterations|timeTaken|excludedTime)\.(\d+)$/)
139
+ .map { |key| key.split('.').last.to_i } # Extract the run index number
140
+ .uniq
141
+ .sort
142
+
143
+ run_indices.each do |i|
144
+ run_tags = base_tags + ["run:#{i}"]
145
+
146
+ {
147
+ 'benchmark.measured_time_ns' => row["measuredTime.#{i}"],
148
+ 'benchmark.iterations' => row["iterations.#{i}"],
149
+ 'benchmark.time_taken_ns' => row["timeTaken.#{i}"],
150
+ 'benchmark.excluded_time_ns' => row["excludedTime.#{i}"]
151
+ }.each do |metric, value|
152
+ send_if_valid(metric, value, run_tags, timestamp)
153
+ end
154
+ end
155
+ end
156
+
157
+ def process_total_metrics(row, tags, timestamp)
158
+ totals = {
159
+ 'benchmark.total_measured_time_ns' => row['totalMeasuredTime'],
160
+ 'benchmark.total_iterations' => row['totalIterations'],
161
+ 'benchmark.total_time_taken_ns' => row['totalTimeTaken'],
162
+ 'benchmark.total_excluded_time_ns' => row['totalExcludedTime']
163
+ }
164
+
165
+ totals.each { |metric, value| send_if_valid(metric, value, tags, timestamp) }
166
+
167
+ if valid_value?(row['totalMeasuredTime']) && valid_value?(row['totalIterations'])
168
+ total_time = row['totalMeasuredTime'].to_f
169
+ total_iters = row['totalIterations'].to_f
170
+ if total_iters > 0
171
+ avg = total_time / total_iters
172
+ send_metric('benchmark.avg_time_per_iteration_ns', avg, tags, timestamp)
173
+ end
174
+ end
175
+ end
176
+
177
+ def valid_value?(value)
178
+ value && !value.empty? && value.to_f > 0
179
+ end
180
+
181
+ def send_if_valid(metric, value, tags, timestamp)
182
+ send_metric(metric, value.to_f, tags, timestamp) if valid_value?(value)
183
+ end
184
+ end
185
+
186
+ DatadogMetricsIngester.run(ARGV)
@@ -82,11 +82,9 @@ InstallPlugin do |config|
82
82
  # Start Bugsnag
83
83
  Maze::ErrorMonitor::Config.start_bugsnag(config)
84
84
 
85
- if config.fail_fast?
86
- # Register exit code handler
87
- Maze::Hooks::ErrorCodeHook.register_exit_code_hook
88
- config.filters << Maze::Plugins::ErrorCodePlugin.new(config)
89
- end
85
+ # Register exit code handler
86
+ Maze::Hooks::ErrorCodeHook.register_exit_code_hook
87
+ config.filters << Maze::Plugins::ErrorCodePlugin.new(config)
90
88
 
91
89
  # Only add the retry plugin if --retry is not used on the command line
92
90
  config.filters << Maze::Plugins::GlobalRetryPlugin.new(config) if config.options[:retry].zero?
@@ -15,6 +15,7 @@ module Maze
15
15
  Bugsnag.notify(exception)
16
16
  exception.instance_eval { def skip_bugsnag; true; end }
17
17
  @driver.fail_driver(exception.message)
18
+ Maze::Hooks::ErrorCodeHook.exit_code = Maze::Api::ExitCode::APPIUM_SESSION_FAILURE
18
19
  end
19
20
  end
20
21
  end
data/lib/maze.rb CHANGED
@@ -8,7 +8,7 @@ require_relative 'maze/timers'
8
8
  # providing an alternative to the proliferation of global variables or singletons.
9
9
  module Maze
10
10
 
11
- VERSION = '9.32.4'
11
+ VERSION = '9.34.0'
12
12
 
13
13
  class << self
14
14
  attr_accessor :check, :driver, :internal_hooks, :mode, :start_time, :dynamic_retry, :public_address,
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bugsnag-maze-runner
3
3
  version: !ruby/object:Gem::Version
4
- version: 9.32.4
4
+ version: 9.34.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Steve Kirkland
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-08-01 00:00:00.000000000 Z
11
+ date: 2025-08-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: cucumber
@@ -379,6 +379,7 @@ email:
379
379
  - steve@bugsnag.com
380
380
  executables:
381
381
  - bb-failed-sessions
382
+ - benchmarks-to-datadog
382
383
  - bugsnag-print-load-paths
383
384
  - download-logs
384
385
  - maze-runner
@@ -388,6 +389,7 @@ extensions: []
388
389
  extra_rdoc_files: []
389
390
  files:
390
391
  - bin/bb-failed-sessions
392
+ - bin/benchmarks-to-datadog
391
393
  - bin/bugsnag-print-load-paths
392
394
  - bin/download-logs
393
395
  - bin/maze-runner