bitfab 0.10.4 → 0.10.6

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: e5d6df92ce06e91ff963c8dc0e78af3a0f591c51b53330c1c8630bb5d66e7663
4
- data.tar.gz: 199d4883cfad921563b3b5786d2db5b017d4b618e000c7d259ed9f69be3eb7ba
3
+ metadata.gz: f20353e1bb03affda1786ed5f8566ab1b3a401f0f9635023bae2fd350bcc7de6
4
+ data.tar.gz: 8e10f199e9d81cfdbfa2bc9172327131301dc3959307340bbe6c55e1ef2bc235
5
5
  SHA512:
6
- metadata.gz: 02c87809df7f4039dd608d3e60ddc0ff85b791d834ea7dc5557a368987d5dba1a174705074a26a9df80d4a8bb7c4ff7493f4c8ac03beb457629d896768a70567
7
- data.tar.gz: 579f94120498658ceeb5129b3a21a9d1477d816be9216963c168e0c8747aef0687298f632539f33ace2960db53a19a9ab7847f776d27d971e1b006ad1ebbc909
6
+ metadata.gz: ade88c2b4d5878a3f6e0cee6f70c4a2d63b1a2f0359f425e9da6ab787806c8ef9396ca0e0a71f5ec7c75647e658419f0e9eb099fbbb4ce22a56c38adf26632e8
7
+ data.tar.gz: 9aa7b82e532a84893533b97d67d413cd4012729624d6bde8433bfdecf73c1d929269e6a1d779e50ed7d13e6dcc84a050f22fdf41282f2de4469bd0c5a6a35b30
data/lib/bitfab/client.rb CHANGED
@@ -36,9 +36,18 @@ module Bitfab
36
36
  # @param limit [Integer] maximum number of traces to replay (default: 5)
37
37
  # @param trace_ids [Array<String>, nil] optional list of trace IDs to filter
38
38
  # @param max_concurrency [Integer, nil] max threads for parallel replay (default: 10)
39
+ # @param code_change_description [String, nil] optional rationale for the
40
+ # code change being tested in this replay (stored on the experiment)
41
+ # @param code_change_files [Array<Hash>, nil] optional list of edited files,
42
+ # each as { path:, before:, after: } (use "" for new/deleted files)
39
43
  # @return [Hash] with :items, :test_run_id, :test_run_url
40
- def replay(receiver, method_name, trace_function_key:, limit: 5, trace_ids: nil, max_concurrency: 10)
41
- Replay.run(self, receiver, method_name, trace_function_key:, limit:, trace_ids:, max_concurrency:)
44
+ def replay(receiver, method_name, trace_function_key:, limit: 5, trace_ids: nil, max_concurrency: 10,
45
+ code_change_description: nil, code_change_files: nil)
46
+ Replay.run(
47
+ self, receiver, method_name,
48
+ trace_function_key:, limit:, trace_ids:, max_concurrency:,
49
+ code_change_description:, code_change_files:
50
+ )
42
51
  end
43
52
 
44
53
  # Execute a block inside a span context, sending trace data on completion.
@@ -56,10 +65,15 @@ module Bitfab
56
65
  replay_ctx = ReplayContext.current
57
66
  resolved_test_run_id = replay_ctx&.dig(:test_run_id)
58
67
  resolved_input_source_span_id = replay_ctx&.dig(:input_source_span_id)
68
+ resolved_input_source_trace_id = replay_ctx&.dig(:input_source_trace_id)
59
69
 
60
70
  # Register trace state for root spans
61
71
  if is_root_span && !TraceState.get(trace_id)
62
- TraceState.create(trace_id, test_run_id: resolved_test_run_id)
72
+ TraceState.create(
73
+ trace_id,
74
+ test_run_id: resolved_test_run_id,
75
+ input_source_trace_id: resolved_input_source_trace_id
76
+ )
63
77
  end
64
78
 
65
79
  if is_root_span
@@ -219,6 +233,9 @@ module Bitfab
219
233
  if trace_state&.dig(:contexts)
220
234
  raw_trace["contexts"] = trace_state[:contexts]
221
235
  end
236
+ if trace_state&.dig(:input_source_trace_id)
237
+ raw_trace["input_source_trace_id"] = trace_state[:input_source_trace_id]
238
+ end
222
239
 
223
240
  payload = {
224
241
  "type" => "sdk-function",
@@ -98,12 +98,19 @@ module Bitfab
98
98
 
99
99
  # Start a replay session by fetching historical traces.
100
100
  # Blocking call. Returns hash with testRunId, testRunUrl, and items array.
101
- def start_replay(trace_function_key, limit, trace_ids: nil)
101
+ #
102
+ # @param code_change_description [String, nil] optional rationale for the
103
+ # code change being tested in this replay
104
+ # @param code_change_files [Array<Hash>, nil] optional list of edited files,
105
+ # each as { path:, before:, after: } (use "" for new/deleted files)
106
+ def start_replay(trace_function_key, limit, trace_ids: nil, code_change_description: nil, code_change_files: nil)
102
107
  payload = {
103
108
  "traceFunctionKey" => trace_function_key,
104
109
  "limit" => limit
105
110
  }
106
111
  payload["traceIds"] = trace_ids if trace_ids
112
+ payload["codeChangeDescription"] = code_change_description unless code_change_description.nil?
113
+ payload["codeChangeFiles"] = normalize_code_change_files(code_change_files) unless code_change_files.nil?
107
114
 
108
115
  request("/api/sdk/replay/start", payload, timeout: 30)
109
116
  end
@@ -129,6 +136,18 @@ module Bitfab
129
136
 
130
137
  private
131
138
 
139
+ # Normalize each entry to a hash with stable string keys, accepting either
140
+ # symbol-keyed or string-keyed hashes from callers.
141
+ def normalize_code_change_files(files)
142
+ files.map do |file|
143
+ {
144
+ "path" => file[:path] || file["path"],
145
+ "before" => file[:before] || file["before"],
146
+ "after" => file[:after] || file["after"]
147
+ }
148
+ end
149
+ end
150
+
132
151
  def headers
133
152
  {
134
153
  "Content-Type" => "application/json",
data/lib/bitfab/replay.rb CHANGED
@@ -14,11 +14,12 @@ module Bitfab
14
14
 
15
15
  # Execute a block with replay context set on the current thread.
16
16
  # The context is automatically cleared when the block completes.
17
- def with_context(test_run_id:, input_source_span_id: nil)
17
+ def with_context(test_run_id:, input_source_span_id: nil, input_source_trace_id: nil)
18
18
  previous = Thread.current[REPLAY_CONTEXT_KEY]
19
19
  Thread.current[REPLAY_CONTEXT_KEY] = {
20
20
  test_run_id:,
21
- input_source_span_id:
21
+ input_source_span_id:,
22
+ input_source_trace_id:
22
23
  }
23
24
  yield
24
25
  ensure
@@ -42,11 +43,22 @@ module Bitfab
42
43
  # @param limit [Integer] maximum number of traces to replay (default: 5)
43
44
  # @param trace_ids [Array<String>, nil] optional list of trace IDs to filter
44
45
  # @param max_concurrency [Integer, nil] max threads for parallel replay (default: 10)
46
+ # @param code_change_description [String, nil] optional rationale for the
47
+ # code change being tested in this replay (stored on the experiment)
48
+ # @param code_change_files [Array<Hash>, nil] optional list of edited files,
49
+ # each as { path:, before:, after: } (empty string for new/deleted files)
45
50
  # @return [Hash] with :items, :test_run_id, :test_run_url
46
- def run(client, receiver, method_name, trace_function_key:, limit: 5, trace_ids: nil, max_concurrency: 10)
51
+ def run(client, receiver, method_name, trace_function_key:, limit: 5, trace_ids: nil, max_concurrency: 10,
52
+ code_change_description: nil, code_change_files: nil)
47
53
  http_client = client.instance_variable_get(:@http_client)
48
54
 
49
- replay_data = http_client.start_replay(trace_function_key, limit, trace_ids:)
55
+ replay_data = http_client.start_replay(
56
+ trace_function_key,
57
+ limit,
58
+ trace_ids:,
59
+ code_change_description:,
60
+ code_change_files:
61
+ )
50
62
  test_run_id = replay_data["testRunId"]
51
63
  test_run_url = replay_data["testRunUrl"]
52
64
  server_items = replay_data["items"] || []
@@ -106,7 +118,15 @@ module Bitfab
106
118
  span = http_client.get_external_span(server_item["externalSpanId"])
107
119
  item_data = extract_span_data(span)
108
120
  metrics = extract_server_item_metrics(server_item)
109
- execute_item(item_data, receiver, method_name, test_run_id, span["id"], metrics)
121
+ execute_item(
122
+ item_data,
123
+ receiver,
124
+ method_name,
125
+ test_run_id,
126
+ span["id"],
127
+ metrics,
128
+ input_source_trace_id: span["externalTraceId"]
129
+ )
110
130
  end
111
131
 
112
132
  # Extract input/output data from an external span's rawData.
@@ -144,13 +164,14 @@ module Bitfab
144
164
  end
145
165
 
146
166
  # Execute a single replay item: deserialize inputs, call method with replay context.
147
- def execute_item(item, receiver, method_name, test_run_id, input_source_span_id = nil, metrics = {})
167
+ def execute_item(item, receiver, method_name, test_run_id, input_source_span_id = nil, metrics = {},
168
+ input_source_trace_id: nil)
148
169
  args, kwargs = Serialize.deserialize_inputs(item)
149
170
 
150
171
  fn_result = nil
151
172
  fn_error = nil
152
173
 
153
- ReplayContext.with_context(test_run_id:, input_source_span_id:) do
174
+ ReplayContext.with_context(test_run_id:, input_source_span_id:, input_source_trace_id:) do
154
175
  fn_result = if kwargs.empty?
155
176
  receiver.send(method_name, *args)
156
177
  else
@@ -131,12 +131,13 @@ module Bitfab
131
131
  @states_mutex.synchronize { @states[trace_id] }
132
132
  end
133
133
 
134
- def create(trace_id, test_run_id: nil)
134
+ def create(trace_id, test_run_id: nil, input_source_trace_id: nil)
135
135
  @states_mutex.synchronize do
136
136
  @states[trace_id] ||= {
137
137
  trace_id:,
138
138
  started_at: Time.now.utc.strftime("%Y-%m-%dT%H:%M:%S.%3NZ"),
139
- test_run_id:
139
+ test_run_id:,
140
+ input_source_trace_id:
140
141
  }.compact
141
142
  end
142
143
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bitfab
4
- VERSION = "0.10.4"
4
+ VERSION = "0.10.6"
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.10.4
4
+ version: 0.10.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Harvest Team