bitfab 0.17.1 → 0.20.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: 9dd280a2cd6afdf47b2bbbf62508dd4918dccf42c83ed276845f592497168601
4
- data.tar.gz: 224fc7cfe39e847f3bfd56e9876151ea356f7a8ad7bff66b529f9704ab360141
3
+ metadata.gz: 9624757032370e1e318121cabcf2bfa984c7bcdcd07c9ecd5dcee449fef57a7c
4
+ data.tar.gz: c3925bbae804c16d21c3f373ec6cde4de340ee645fc6423505fc0b5d5f5e638e
5
5
  SHA512:
6
- metadata.gz: 4cba57762a9ae927ba34ac70fdd1d750d118a9f8f3cc47402f30d8a09c336337f27cef601f8b36e63140e01acaa410d1a58450f160cb1c3d7e7c2c5b1a198cb3
7
- data.tar.gz: eaa0a0b2eed0c0f7c0e24a73e2128540c53a504e9a3dd4d6858355e8076aa2fa8a4913ea170b278ac1579c5e407d4333759e1bae345dbe575b9fa91b666275a1
6
+ metadata.gz: d2c33cd4a3432d79703685f00bb8c8278e2bebbd694b3a695f1dee646680a5af11370f233635f04590eeb0d5cedee850c496b40bee2fc176c34cf22fb721cde8
7
+ data.tar.gz: c64be06a07ca8ea807bca23e0f96acb15e1647945b33c3ea731f10c0b52759870b88637d86b680aee07caac7836e3818946f2fb85f0979d83b3ea89ef8ea1704
data/lib/bitfab/client.rb CHANGED
@@ -50,6 +50,8 @@ module Bitfab
50
50
  # each as { path:, before:, after: } (use "" for new/deleted files)
51
51
  # @param experiment_group_id [String, nil] optional UUID grouping multiple
52
52
  # replay runs into a single experiment batch
53
+ # @param dataset_id [String, nil] optional UUID of the dataset this replay
54
+ # runs against, stored on the resulting experiment for durable attribution
53
55
  # @param mock [String] mock strategy for child spans: "none" (default),
54
56
  # "all", or "marked". "all" mocks every child span; "marked" only mocks
55
57
  # spans declared with mock_on_replay: true.
@@ -59,12 +61,12 @@ module Bitfab
59
61
  # { trace_id:, source_span_id: }, and returns [new_args, new_kwargs].
60
62
  # @return [Hash] with :items, :test_run_id, :test_run_url
61
63
  def replay(receiver, method_name, trace_function_key:, limit: nil, trace_ids: nil, max_concurrency: 10,
62
- code_change_description: nil, code_change_files: nil, experiment_group_id: nil, mock: "none",
64
+ code_change_description: nil, code_change_files: nil, experiment_group_id: nil, dataset_id: nil, mock: "none",
63
65
  adapt_inputs: nil, environment: nil)
64
66
  Replay.run(
65
67
  self, receiver, method_name,
66
68
  trace_function_key:, limit:, trace_ids:, max_concurrency:,
67
- code_change_description:, code_change_files:, experiment_group_id:, mock:, adapt_inputs:, environment:
69
+ code_change_description:, code_change_files:, experiment_group_id:, dataset_id:, mock:, adapt_inputs:, environment:
68
70
  )
69
71
  end
70
72
 
@@ -5,6 +5,7 @@ require "json"
5
5
  require "uri"
6
6
 
7
7
  require_relative "constants"
8
+ require_relative "serialize"
8
9
  require_relative "version"
9
10
 
10
11
  module Bitfab
@@ -32,7 +33,7 @@ module Bitfab
32
33
  http.read_timeout = request_timeout
33
34
 
34
35
  req = Net::HTTP::Post.new(uri.path, headers)
35
- req.body = JSON.generate(payload)
36
+ req.body = safe_generate(payload, endpoint)
36
37
 
37
38
  response = http.request(req)
38
39
 
@@ -105,8 +106,10 @@ module Bitfab
105
106
  # each as { path:, before:, after: } (use "" for new/deleted files)
106
107
  # @param experiment_group_id [String, nil] optional UUID grouping multiple
107
108
  # replay runs into a single experiment batch
109
+ # @param dataset_id [String, nil] optional UUID of the dataset this replay
110
+ # runs against, stored on the resulting experiment for durable attribution
108
111
  def start_replay(trace_function_key, limit, trace_ids: nil, code_change_description: nil,
109
- code_change_files: nil, experiment_group_id: nil, include_db_branch_lease: false)
112
+ code_change_files: nil, experiment_group_id: nil, include_db_branch_lease: false, dataset_id: nil)
110
113
  payload = {
111
114
  "traceFunctionKey" => trace_function_key
112
115
  }
@@ -118,6 +121,7 @@ module Bitfab
118
121
  payload["codeChangeFiles"] = normalize_code_change_files(code_change_files) unless code_change_files.nil?
119
122
  payload["experimentGroupId"] = experiment_group_id unless experiment_group_id.nil?
120
123
  payload["includeDbBranchLease"] = true if include_db_branch_lease
124
+ payload["datasetId"] = dataset_id unless dataset_id.nil?
121
125
 
122
126
  # When DB branching is on, the server resolves a Neon preview branch per
123
127
  # item (snapshot + restore + poll), which can run several seconds each.
@@ -166,6 +170,46 @@ module Bitfab
166
170
 
167
171
  private
168
172
 
173
+ # JSON-encode a request body without ever raising on a stray value.
174
+ #
175
+ # Upstream serialization (Serialize.serialize_value) should already have
176
+ # flattened user data. This is the boundary backstop: if anything
177
+ # non-serializable still slips through, it is run through serialize_value
178
+ # (which never raises and stubs strays) instead of letting JSON.generate
179
+ # raise and drop the whole span/trace silently. A degraded payload warns
180
+ # loudly so the trace isn't quietly left incomplete or not replayable.
181
+ def safe_generate(payload, endpoint)
182
+ JSON.generate(payload)
183
+ rescue => e
184
+ begin
185
+ warn "Bitfab: request body to #{endpoint} held a non-serializable " \
186
+ "value (#{e.message}); it was stubbed so the span still sends, " \
187
+ "but the trace may be incomplete or not replayable. Capture a " \
188
+ "JSON-safe projection of this input to make it replayable."
189
+ rescue
190
+ # Never crash the host app over a log line.
191
+ end
192
+
193
+ begin
194
+ JSON.generate(sanitize_payload(payload))
195
+ rescue
196
+ # Truly pathological. Still never drop silently: send a marker body.
197
+ JSON.generate({"error" => "payload_serialize_failed"})
198
+ end
199
+ end
200
+
201
+ # Serialize each top-level value so a bad or oversize value is stubbed in
202
+ # place while the payload keeps its object shape. Running serialize_value on
203
+ # the whole payload could collapse the entire body to a single stub string
204
+ # (oversize/cyclic), sending a JSON string instead of a span object.
205
+ def sanitize_payload(payload)
206
+ return {"error" => "payload_serialize_failed"} unless payload.is_a?(Hash)
207
+
208
+ payload.each_with_object({}) do |(k, v), acc|
209
+ acc[k.to_s] = Serialize.serialize_value(v)
210
+ end
211
+ end
212
+
169
213
  # Normalize each entry to a hash with stable string keys, accepting either
170
214
  # symbol-keyed or string-keyed hashes from callers.
171
215
  def normalize_code_change_files(files)
data/lib/bitfab/replay.rb CHANGED
@@ -88,6 +88,8 @@ module Bitfab
88
88
  # each as { path:, before:, after: } (empty string for new/deleted files)
89
89
  # @param experiment_group_id [String, nil] optional UUID grouping multiple
90
90
  # replay runs into a single experiment batch
91
+ # @param dataset_id [String, nil] optional UUID of the dataset this replay
92
+ # runs against, stored on the resulting experiment for durable attribution
91
93
  # @param mock [String] mock strategy for child spans: "none" (default),
92
94
  # "all", or "marked". "all" mocks every child span; "marked" only mocks
93
95
  # spans declared with mock_on_replay: true.
@@ -99,7 +101,7 @@ module Bitfab
99
101
  # that item's :error rather than crashing the run.
100
102
  # @return [Hash] with :items, :test_run_id, :test_run_url
101
103
  def run(client, receiver, method_name, trace_function_key:, limit: nil, trace_ids: nil, max_concurrency: 10,
102
- code_change_description: nil, code_change_files: nil, experiment_group_id: nil, mock: "none",
104
+ code_change_description: nil, code_change_files: nil, experiment_group_id: nil, dataset_id: nil, mock: "none",
103
105
  adapt_inputs: nil, environment: nil)
104
106
  unless MOCK_STRATEGIES.include?(mock.to_s)
105
107
  raise ArgumentError, "Invalid mock strategy '#{mock}'. Must be one of: #{MOCK_STRATEGIES.join(", ")}"
@@ -130,7 +132,8 @@ module Bitfab
130
132
  code_change_description:,
131
133
  code_change_files:,
132
134
  experiment_group_id:,
133
- include_db_branch_lease:
135
+ include_db_branch_lease:,
136
+ dataset_id:
134
137
  )
135
138
  test_run_id = replay_data["testRunId"]
136
139
  test_run_url = replay_data["testRunUrl"]
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bitfab
4
- VERSION = "0.17.1"
4
+ VERSION = "0.20.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bitfab
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.17.1
4
+ version: 0.20.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Harvest Team