rspec-buildkite-analytics 0.3.6 → 0.4.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: a81e9b877577590dbb59202d72db1ee309a25be50c69e45361a0515cbfed1e5a
4
- data.tar.gz: 172a76b3343c2f460013c47b3893f7667aa3e965effde4b370fcf058ee0e3dbf
3
+ metadata.gz: 44dd3ce4c2f47452f1eb52c96a2b86a7f49dd971d38350152b773dd343a85969
4
+ data.tar.gz: 81fad9923f72c5a4e733b9e93917323c1037a6a0fa9ba4c0f76e9c3d0820f06b
5
5
  SHA512:
6
- metadata.gz: 471c20607b1dd61c55bfb1244ca8a5d95c3f30b4f3a26fbd9bf1912d11af3d3e7841762b077472dbc5b0fe3dc647771008a6e68cb5a4f85296a1f41b765ae4f2
7
- data.tar.gz: 16145862ef9c26a3132eead4ea6bf11323effe8b6f5bc32140cce966544d508d8a4d83a64e809814d35c71add64102d8e23f7175f02d9f914839381efddc85b8
6
+ metadata.gz: 6eb38e960fa8c7c8e6b4ff29070d188db20f0868c316a7b2b51051cd733793c8c86ea736385ddefa3eab04afb585d20ebef5fcf3bb7d3a2ee62513d9dcf448d5
7
+ data.tar.gz: dec76a5f780c47f7f340b83501450545ccd810dbf9b0c43755d4fff300197da750b9411a49eb757f41a094d7058780a78f7a6d859fef34514105be6e97fd8765
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rspec-buildkite-analytics (0.3.6)
4
+ rspec-buildkite-analytics (0.4.0)
5
5
  activesupport (>= 5.2, <= 7.0)
6
6
  rspec-core (~> 3.10)
7
7
  rspec-expectations (~> 3.10)
@@ -13,12 +13,14 @@ module RSpec::Buildkite::Analytics::CI
13
13
  "commit_sha" => ENV["BUILDKITE_COMMIT"],
14
14
  "number" => ENV["BUILDKITE_BUILD_NUMBER"],
15
15
  "job_id" => ENV["BUILDKITE_JOB_ID"],
16
- "message" => ENV["BUILDKITE_MESSAGE"]
16
+ "message" => ENV["BUILDKITE_MESSAGE"],
17
+ "debug" => ENV["BUILDKITE_ANALYTICS_DEBUG_ENABLED"]
17
18
  }
18
19
  else
19
20
  {
20
21
  "CI" => nil,
21
- "key" => SecureRandom.uuid
22
+ "key" => SecureRandom.uuid,
23
+ "debug" => ENV["BUILDKITE_ANALYTICS_DEBUG_ENABLED"]
22
24
  }
23
25
  end
24
26
  end
@@ -1,6 +1,6 @@
1
1
  module RSpec::Buildkite::Analytics
2
2
  class Reporter
3
- RSpec::Core::Formatters.register self, :example_passed, :example_failed, :example_pending
3
+ RSpec::Core::Formatters.register self, :example_passed, :example_failed, :example_pending, :dump_summary
4
4
 
5
5
  attr_reader :output
6
6
 
@@ -20,6 +20,30 @@ module RSpec::Buildkite::Analytics
20
20
  end
21
21
  end
22
22
 
23
+ def dump_summary(notification)
24
+ if RSpec::Buildkite::Analytics.session.present?
25
+ examples_count = {
26
+ examples: notification.examples.count,
27
+ failed: notification.failed_examples.count,
28
+ pending: notification.pending_examples.count,
29
+ errors_outside_examples: notification.errors_outside_of_examples_count
30
+ }
31
+
32
+ RSpec::Buildkite::Analytics.session.close(examples_count)
33
+
34
+ # Write the debug file, if debug mode is enabled
35
+ if RSpec::Buildkite::Analytics.debug_enabled
36
+ filename = "#{RSpec::Buildkite::Analytics.debug_filepath}/bk-analytics-#{DateTime.current.strftime("%F-%R:%S")}-#{ENV["BUILDKITE_JOB_ID"]}.log.gz"
37
+
38
+ File.open(filename, "wb") do |f|
39
+ gz = Zlib::GzipWriter.new(f)
40
+ gz.puts(RSpec::Buildkite::Analytics.session.logger.to_array)
41
+ gz.close
42
+ end
43
+ end
44
+ end
45
+ end
46
+
23
47
  alias_method :example_passed, :handle_example
24
48
  alias_method :example_failed, :handle_example
25
49
  alias_method :example_pending, :handle_example
@@ -12,6 +12,28 @@ module RSpec::Buildkite::Analytics
12
12
  class RejectedSubscription < StandardError; end
13
13
  class InitialConnectionFailure < StandardError; end
14
14
 
15
+ class Logger
16
+ def initialize
17
+ @log = Queue.new
18
+ end
19
+
20
+ def write(str)
21
+ @log << "#{Time.now.strftime("%F-%R:%S.%9N")} #{Thread.current} #{str}"
22
+ end
23
+
24
+ def to_array
25
+ # This empty check is important cos calling pop on a Queue is blocking until
26
+ # it's not empty
27
+ if @log.empty?
28
+ []
29
+ else
30
+ Array.new(@log.size) { @log.pop }
31
+ end
32
+ end
33
+ end
34
+
35
+ attr_reader :logger
36
+
15
37
  def initialize(url, authorization_header, channel)
16
38
  @queue = Queue.new
17
39
  @channel = channel
@@ -25,6 +47,8 @@ module RSpec::Buildkite::Analytics
25
47
  @url = url
26
48
  @authorization_header = authorization_header
27
49
 
50
+ @logger = Logger.new
51
+
28
52
  connect
29
53
  rescue TimeoutError, InitialConnectionFailure => e
30
54
  $stderr.puts "rspec-buildkite-analytics could not establish an initial connection with Buildkite due to #{e.message}. You may be missing some data for this test suite, please contact support."
@@ -43,16 +67,19 @@ module RSpec::Buildkite::Analytics
43
67
  # time the mutex is released, the value of @connection has been refreshed, and so
44
68
  # the second thread returns early and does not reattempt the reconnection.
45
69
  return unless connection == @connection
70
+ @logger.write("starting reconnection")
46
71
 
47
72
  begin
48
73
  reconnection_count += 1
49
74
  connect
50
75
  rescue SocketConnection::HandshakeError, RejectedSubscription, TimeoutError, SocketConnection::SocketError => e
76
+ @logger.write("failed reconnection attempt #{reconnection_count} due to #{e}")
51
77
  if reconnection_count > MAX_RECONNECTION_ATTEMPTS
52
78
  $stderr.puts "rspec-buildkite-analytics experienced a disconnection and could not reconnect to Buildkite due to #{e.message}. Please contact support."
53
79
  raise e
54
80
  else
55
81
  sleep(WAIT_BETWEEN_RECONNECTIONS)
82
+ @logger.write("retrying reconnection")
56
83
  retry
57
84
  end
58
85
  end
@@ -60,8 +87,10 @@ module RSpec::Buildkite::Analytics
60
87
  retransmit
61
88
  end
62
89
 
63
- def close()
90
+ def close(examples_count)
64
91
  @closing = true
92
+ @examples_count = examples_count
93
+ @logger.write("closing socket connection")
65
94
 
66
95
  # Because the server only sends us confirmations after every 10mb of
67
96
  # data it uploads to S3, we'll never get confirmation of the
@@ -70,6 +99,7 @@ module RSpec::Buildkite::Analytics
70
99
  send_eot
71
100
 
72
101
  @idents_mutex.synchronize do
102
+ @logger.write("waiting for last confirm")
73
103
  # Here, we sleep for 75 seconds while waiting for the server to confirm the last idents.
74
104
  # We are woken up when the unconfirmed_idents is empty, and given back the mutex to
75
105
  # continue operation.
@@ -78,6 +108,7 @@ module RSpec::Buildkite::Analytics
78
108
 
79
109
  # Then we always disconnect cos we can't wait forever? 🤷‍♀️
80
110
  @connection.close
111
+ @logger.write("socket connection closed")
81
112
  end
82
113
 
83
114
  def handle(_connection, data)
@@ -86,11 +117,14 @@ module RSpec::Buildkite::Analytics
86
117
  when "ping"
87
118
  # In absence of other message, the server sends us a ping every 3 seconds
88
119
  # We are currently not doing anything with these
120
+ @logger.write("received ping")
89
121
  when "welcome", "confirm_subscription"
90
122
  # Push these two messages onto the queue, so that we block on waiting for the
91
123
  # initializing phase to complete
92
124
  @queue.push(data)
125
+ @logger.write("received #{data['type']}")
93
126
  when "reject_subscription"
127
+ @logger.write("received rejected_subscription")
94
128
  raise RejectedSubscription
95
129
  else
96
130
  process_message(data)
@@ -103,6 +137,8 @@ module RSpec::Buildkite::Analytics
103
137
  add_unconfirmed_idents(result.id, result_as_json)
104
138
 
105
139
  transmit_results([result_as_json])
140
+
141
+ @logger.write("transmitted #{result.id}")
106
142
  end
107
143
 
108
144
  def unconfirmed_idents_count
@@ -125,6 +161,8 @@ module RSpec::Buildkite::Analytics
125
161
  end
126
162
 
127
163
  def connect
164
+ @logger.write("starting socket connection process")
165
+
128
166
  @connection = SocketConnection.new(self, @url, {
129
167
  "Authorization" => @authorization_header,
130
168
  })
@@ -137,6 +175,8 @@ module RSpec::Buildkite::Analytics
137
175
  })
138
176
 
139
177
  wait_for_confirm
178
+
179
+ @logger.write("connected")
140
180
  end
141
181
 
142
182
  def pop_with_timeout(message_type)
@@ -174,6 +214,8 @@ module RSpec::Buildkite::Analytics
174
214
  # Remove received idents from unconfirmed_idents
175
215
  idents.each { |key| @unconfirmed_idents.delete(key) }
176
216
 
217
+ @logger.write("received confirm for indentifiers: #{idents.join(", ")}")
218
+
177
219
  # This @empty ConditionVariable broadcasts every time that @unconfirmed_idents is
178
220
  # empty, which will happen about every 10mb of data as that's when the server
179
221
  # sends back confirmations.
@@ -191,9 +233,12 @@ module RSpec::Buildkite::Analytics
191
233
  "identifier" => @channel,
192
234
  "command" => "message",
193
235
  "data" => {
194
- "action" => "end_of_transmission"
236
+ "action" => "end_of_transmission",
237
+ "examples_count" => @examples_count.to_json
195
238
  }.to_json
196
239
  })
240
+
241
+ @logger.write("transmitted EOT")
197
242
  end
198
243
 
199
244
  def process_message(data)
@@ -205,6 +250,7 @@ module RSpec::Buildkite::Analytics
205
250
  remove_unconfirmed_idents(data["message"]["confirm"])
206
251
  else
207
252
  # unhandled message
253
+ @logger.write("received unhandled message #{data["message"]}")
208
254
  end
209
255
  end
210
256
 
@@ -214,7 +260,11 @@ module RSpec::Buildkite::Analytics
214
260
  end
215
261
 
216
262
  # send the contents of the buffer, unless it's empty
217
- transmit_results(data) unless data.empty?
263
+ unless data.empty?
264
+ @logger.write("retransmitting data")
265
+ transmit_results(data)
266
+ end
267
+
218
268
  # if we were disconnected in the closing phase, then resend the EOT
219
269
  # message so the server can persist the last upload part
220
270
  send_eot if @closing
@@ -66,6 +66,7 @@ module RSpec::Buildkite::Analytics
66
66
  # Setting up a new thread that listens on the socket, and processes incoming
67
67
  # comms from the server
68
68
  @thread = Thread.new do
69
+ @session.logger.write("listening in on socket")
69
70
  frame = WebSocket::Frame::Incoming::Client.new
70
71
 
71
72
  while @socket
@@ -75,20 +76,25 @@ module RSpec::Buildkite::Analytics
75
76
  @session.handle(self, data.data)
76
77
  end
77
78
  end
78
- rescue EOFError, Errno::ECONNRESET
79
+ rescue EOFError, Errno::ECONNRESET => e
80
+ @session.logger.write("#{e}")
79
81
  if @socket
82
+ @session.logger.write("attempting disconnected flow")
80
83
  @session.disconnected(self)
81
84
  disconnect
82
85
  end
83
86
  rescue IOError
84
87
  # This is fine to ignore
88
+ @session.logger.write("IOError")
85
89
  rescue IndexError
86
90
  # I don't like that we're doing this but I think it's the best of the options
87
91
  #
88
92
  # This relates to this issue https://github.com/ruby/openssl/issues/452
89
93
  # A fix for it has been released but the repercussions of overriding
90
94
  # the OpenSSL version in the stdlib seem worse than catching this error here.
95
+ @session.logger.write("IndexError")
91
96
  if @socket
97
+ @session.logger.write("attempting disconnected flow")
92
98
  @session.disconnected(self)
93
99
  disconnect
94
100
  end
@@ -104,6 +110,7 @@ module RSpec::Buildkite::Analytics
104
110
  @socket.write(frame.to_s)
105
111
  rescue Errno::EPIPE, Errno::ECONNRESET, OpenSSL::SSL::SSLError => e
106
112
  return unless @socket
113
+ @session.logger.write("got #{e}, attempting disconnected flow")
107
114
  @session.disconnected(self)
108
115
  disconnect
109
116
  rescue IndexError
@@ -112,13 +119,16 @@ module RSpec::Buildkite::Analytics
112
119
  # This relates to this issue https://github.com/ruby/openssl/issues/452
113
120
  # A fix for it has been released but the repercussions of overriding
114
121
  # the OpenSSL version in the stdlib seem worse than catching this error here.
122
+ @session.logger.write("IndexError")
115
123
  if @socket
124
+ @session.logger.write("attempting disconnected flow")
116
125
  @session.disconnected(self)
117
126
  disconnect
118
127
  end
119
128
  end
120
129
 
121
130
  def close
131
+ @session.logger.write("socket close")
122
132
  transmit(nil, type: :close)
123
133
  disconnect
124
134
  end
@@ -126,6 +136,7 @@ module RSpec::Buildkite::Analytics
126
136
  private
127
137
 
128
138
  def disconnect
139
+ @session.logger.write("socket disconnect")
129
140
  socket = @socket
130
141
  @socket = nil
131
142
  socket&.close
@@ -162,12 +162,6 @@ module RSpec::Buildkite::Analytics
162
162
  trace = RSpec::Buildkite::Analytics::Uploader::Trace.new(example, tracer.history)
163
163
  RSpec::Buildkite::Analytics.uploader.traces << trace
164
164
  end
165
-
166
- config.after(:suite) do
167
- # This needs the lonely operater as the session will be nil
168
- # if auth against the API token fails
169
- RSpec::Buildkite::Analytics.session&.close
170
- end
171
165
  end
172
166
 
173
167
  RSpec::Buildkite::Analytics::Network.configure
@@ -3,7 +3,7 @@
3
3
  module RSpec
4
4
  module Buildkite
5
5
  module Analytics
6
- VERSION = "0.3.6"
6
+ VERSION = "0.4.0"
7
7
  end
8
8
  end
9
9
  end
@@ -15,11 +15,15 @@ module RSpec::Buildkite::Analytics
15
15
  attr_accessor :url
16
16
  attr_accessor :uploader
17
17
  attr_accessor :session
18
+ attr_accessor :debug_enabled
19
+ attr_accessor :debug_filepath
18
20
  end
19
21
 
20
- def self.configure(token: nil, url: nil)
22
+ def self.configure(token: nil, url: nil, debug_enabled: false, debug_filepath: nil)
21
23
  self.api_token = token || ENV["BUILDKITE_ANALYTICS_TOKEN"]
22
24
  self.url = url || DEFAULT_URL
25
+ self.debug_enabled = debug_enabled || !!(ENV["BUILDKITE_ANALYTICS_DEBUG_ENABLED"])
26
+ self.debug_filepath = debug_filepath || ENV["BUILDKITE_ANALYTICS_DEBUG_FILEPATH"]
23
27
 
24
28
  require_relative "analytics/uploader"
25
29
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec-buildkite-analytics
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.6
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Buildkite
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-10-29 00:00:00.000000000 Z
11
+ date: 2021-11-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport