bitfab 0.18.0 → 0.20.1

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: 4401b0c7aa47516909e636557ab23f325fb0cbbd4ca8564f3d8864078e58f975
4
- data.tar.gz: 2b11fbfee500e0e03ec7b4b61772d623e2c236479965441b659aafbed4a687a6
3
+ metadata.gz: cd571926bd40dc39f337e87f9977bfb0fe3219c21532dc1cf7be382c175a4559
4
+ data.tar.gz: 17ff724ae1a42f97fa6eae82c97404de8dc9cef738a18674d76da48ba9ea762d
5
5
  SHA512:
6
- metadata.gz: e720bb8dd79d572f4671b5f891cb1528dac2b610d2357d1f16ec5a7e04656fff0969f434e5e683be991d8745865bacbbe1e37993380e1a09d2cc9377e221eda5
7
- data.tar.gz: c197da185d7966cd9861213c49f0db4e3246a5eab0bdb70e800d8a4a5a4e8f174b5294d833511232555d43f0ca026a7988297fa3660743a8fef94b05b17a7ca9
6
+ metadata.gz: 012d3433d2d59200c812557a95b4f2b7cb8d93d7885d9495f59b7e21d89a892ffcc3c0bb83200f4c9f324320d5a1153f4c59bd2facac2dc1924174a7f184d81f
7
+ data.tar.gz: 69aaffc897238560ebecf6fedbd81509eda58fd082126107585a913cc74a025456a32111356902251a0c5b3439ff2b84ef7ea5aac4325a10e531c93a5d460334
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
 
@@ -106,8 +106,10 @@ module Bitfab
106
106
  # each as { path:, before:, after: } (use "" for new/deleted files)
107
107
  # @param experiment_group_id [String, nil] optional UUID grouping multiple
108
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
109
111
  def start_replay(trace_function_key, limit, trace_ids: nil, code_change_description: nil,
110
- 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)
111
113
  payload = {
112
114
  "traceFunctionKey" => trace_function_key
113
115
  }
@@ -119,6 +121,7 @@ module Bitfab
119
121
  payload["codeChangeFiles"] = normalize_code_change_files(code_change_files) unless code_change_files.nil?
120
122
  payload["experimentGroupId"] = experiment_group_id unless experiment_group_id.nil?
121
123
  payload["includeDbBranchLease"] = true if include_db_branch_lease
124
+ payload["datasetId"] = dataset_id unless dataset_id.nil?
122
125
 
123
126
  # When DB branching is on, the server resolves a Neon preview branch per
124
127
  # item (snapshot + restore + poll), which can run several seconds each.
data/lib/bitfab/replay.rb CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative "constants"
4
4
  require_relative "serialize"
5
+ require_relative "traceable"
5
6
 
6
7
  module Bitfab
7
8
  # Replay mock strategies. Mirrors the Python and TypeScript SDKs.
@@ -88,6 +89,8 @@ module Bitfab
88
89
  # each as { path:, before:, after: } (empty string for new/deleted files)
89
90
  # @param experiment_group_id [String, nil] optional UUID grouping multiple
90
91
  # replay runs into a single experiment batch
92
+ # @param dataset_id [String, nil] optional UUID of the dataset this replay
93
+ # runs against, stored on the resulting experiment for durable attribution
91
94
  # @param mock [String] mock strategy for child spans: "none" (default),
92
95
  # "all", or "marked". "all" mocks every child span; "marked" only mocks
93
96
  # spans declared with mock_on_replay: true.
@@ -99,7 +102,7 @@ module Bitfab
99
102
  # that item's :error rather than crashing the run.
100
103
  # @return [Hash] with :items, :test_run_id, :test_run_url
101
104
  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",
105
+ code_change_description: nil, code_change_files: nil, experiment_group_id: nil, dataset_id: nil, mock: "none",
103
106
  adapt_inputs: nil, environment: nil)
104
107
  unless MOCK_STRATEGIES.include?(mock.to_s)
105
108
  raise ArgumentError, "Invalid mock strategy '#{mock}'. Must be one of: #{MOCK_STRATEGIES.join(", ")}"
@@ -115,6 +118,21 @@ module Bitfab
115
118
  "determines how many traces replay."
116
119
  end
117
120
 
121
+ # Reject a trace_function_key that contradicts the method's declared key.
122
+ # replay() fetches historical traces by trace_function_key but records the
123
+ # replayed spans under the method's own bitfab_span key (via send below),
124
+ # so a mismatch produces an incoherent test run (traces fetched for one
125
+ # function, recorded under another). Only fires when the method's key is
126
+ # introspectable; an untraced method falls through to the persistence
127
+ # check in complete_replay. Mirrors the TypeScript/Python SDKs.
128
+ declared_key = Traceable.trace_function_key_for(receiver, method_name)
129
+ if declared_key && declared_key != trace_function_key
130
+ raise ArgumentError,
131
+ "Method #{method_name} is traced under trace function key '#{declared_key}' but replay was " \
132
+ "called with '#{trace_function_key}'. Pass trace_function_key: '#{declared_key}' to replay it, " \
133
+ "or point at the method traced under '#{trace_function_key}'."
134
+ end
135
+
118
136
  http_client = client.instance_variable_get(:@http_client)
119
137
 
120
138
  # limit is meaningless with explicit trace_ids (the ID list determines
@@ -130,7 +148,8 @@ module Bitfab
130
148
  code_change_description:,
131
149
  code_change_files:,
132
150
  experiment_group_id:,
133
- include_db_branch_lease:
151
+ include_db_branch_lease:,
152
+ dataset_id:
134
153
  )
135
154
  test_run_id = replay_data["testRunId"]
136
155
  test_run_url = replay_data["testRunUrl"]
@@ -69,9 +69,43 @@ module Bitfab
69
69
  end
70
70
  end
71
71
 
72
+ wrapper.instance_variable_set(:@bitfab_span_method, method_name_str)
73
+ wrapper.instance_variable_set(:@bitfab_span_key, trace_function_key)
72
74
  klass.prepend(wrapper)
73
75
  end
74
76
 
77
+ # Resolve the trace function key a traced method records spans under, or
78
+ # nil if the method carries no Bitfab span wrapper. Walks the receiver's
79
+ # ancestor chain for the wrapper module that bitfab_span / Traceable.wrap
80
+ # prepended. Instances resolve via the class. A Class/Module receiver
81
+ # resolves either way: singleton-class ancestors first (class/module
82
+ # methods wrapped on the singleton class), then the class's own ancestors
83
+ # (methods whose wrapper was prepended onto the class itself), so a
84
+ # class-method replay is guarded regardless of which surface carries the
85
+ # wrapper. Singleton-first keeps the method actually dispatched by
86
+ # `receiver.send` (a class method shadows a same-named instance wrapper).
87
+ #
88
+ # replay() uses this to reject a trace_function_key that contradicts the
89
+ # method's declared key: a mismatch fetches one function's historical
90
+ # traces but re-records the replay under the method's own key, producing
91
+ # an incoherent test run. Mirrors the TypeScript and Python SDKs, which
92
+ # throw on the same key mismatch.
93
+ def self.trace_function_key_for(receiver, method_name)
94
+ name = method_name.to_s
95
+ ancestors = if receiver.is_a?(Module)
96
+ receiver.singleton_class.ancestors + receiver.ancestors
97
+ else
98
+ receiver.class.ancestors
99
+ end
100
+ ancestors.each do |mod|
101
+ next unless mod.instance_variable_defined?(:@bitfab_span_method)
102
+ next unless mod.instance_variable_get(:@bitfab_span_method) == name
103
+
104
+ return mod.instance_variable_get(:@bitfab_span_key)
105
+ end
106
+ nil
107
+ end
108
+
75
109
  module ClassMethods
76
110
  # Set the trace function key for this class.
77
111
  # All spans declared in this class will be grouped under this key.
@@ -152,6 +186,8 @@ module Bitfab
152
186
  end
153
187
  end
154
188
 
189
+ wrapper.instance_variable_set(:@bitfab_span_method, method_name_str)
190
+ wrapper.instance_variable_set(:@bitfab_span_key, trace_function_key)
155
191
  prepend(wrapper)
156
192
  end
157
193
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bitfab
4
- VERSION = "0.18.0"
4
+ VERSION = "0.20.1"
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.18.0
4
+ version: 0.20.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Harvest Team