bitfab 0.22.0 → 0.23.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 +4 -4
- data/lib/bitfab/client.rb +76 -12
- data/lib/bitfab/http_client.rb +8 -2
- data/lib/bitfab/replay.rb +5 -2
- data/lib/bitfab/version.rb +1 -1
- data/lib/bitfab.rb +2 -2
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 26ad3b53b9f3da35c01496ecc236ce0f5d27e7921761c7baca7654d1fb8de31e
|
|
4
|
+
data.tar.gz: e653a93c4bf8e5263b22a6114ebcc2a52db6cc3db6eecc7774cb7b6ed07c51c3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7c31a1ffd2e8be0cd31fd94941479351176823eabeadf453972bd493aeadbbc7de97002f484787a6e009024a42a6ed1e3a8fc99ded4b94eb3fd842d6e03772e9
|
|
7
|
+
data.tar.gz: 01ee591f9c3a51254abc101229ba261bc054acd02bfe7c67471367fd8cb0cc2fa015a1c1affc149358caf7dc5af98c7ea4f5351973153b60c6983befbc4d7ac7
|
data/lib/bitfab/client.rb
CHANGED
|
@@ -20,21 +20,40 @@ module Bitfab
|
|
|
20
20
|
# outputs (which may themselves be nil or false).
|
|
21
21
|
MOCK_REPLAY_MISS = Object.new.freeze
|
|
22
22
|
|
|
23
|
-
attr_reader :
|
|
23
|
+
attr_reader :service_url
|
|
24
24
|
|
|
25
|
-
def initialize(api_key
|
|
26
|
-
@
|
|
25
|
+
def initialize(api_key: nil, service_url: nil, enabled: true, strict: false)
|
|
26
|
+
@api_key_config = api_key
|
|
27
27
|
@service_url = service_url || DEFAULT_SERVICE_URL
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
@
|
|
28
|
+
# The user's on/off intent; effective enabled also requires a resolved key.
|
|
29
|
+
@explicitly_enabled = enabled
|
|
30
|
+
@strict = strict
|
|
31
|
+
# Cached only once a non-empty key is found, so an early resolve (before
|
|
32
|
+
# env loaded) can't poison a later one.
|
|
33
|
+
@resolved_api_key = nil
|
|
34
|
+
@api_key_warned = false
|
|
35
|
+
# The key is NOT read here. HttpClient gets a proc so the key is resolved
|
|
36
|
+
# at send time, after any env loading has run.
|
|
37
|
+
@http_client = HttpClient.new(api_key: -> { resolve_api_key }, service_url: @service_url)
|
|
34
38
|
@pending_span_threads = {}
|
|
35
39
|
@pending_span_mutex = Mutex.new
|
|
36
40
|
end
|
|
37
41
|
|
|
42
|
+
# The configured API key (a proc is resolved on read). Reflects what was
|
|
43
|
+
# passed to the client; the ENV["BITFAB_API_KEY"] fallback applied during
|
|
44
|
+
# actual tracing is not surfaced here, and reading this never warns.
|
|
45
|
+
def api_key
|
|
46
|
+
@api_key_config.respond_to?(:call) ? @api_key_config.call : @api_key_config
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Effective tracing state, evaluated lazily: enabled only when not
|
|
50
|
+
# explicitly disabled AND a key resolves. Reading this resolves the key
|
|
51
|
+
# (and may emit the one-time empty-key warning), exactly as the first traced
|
|
52
|
+
# call would.
|
|
53
|
+
def enabled
|
|
54
|
+
tracing_enabled?
|
|
55
|
+
end
|
|
56
|
+
|
|
38
57
|
# Replay historical traces through a method and create a test run.
|
|
39
58
|
#
|
|
40
59
|
# @param receiver [Object, Class] an instance for instance methods, or a Class for class methods
|
|
@@ -68,11 +87,11 @@ module Bitfab
|
|
|
68
87
|
# A raising callback never crashes the run.
|
|
69
88
|
# @return [Hash] with :items, :test_run_id, :test_run_url
|
|
70
89
|
def replay(receiver, method_name, trace_function_key:, limit: nil, trace_ids: nil, max_concurrency: 10,
|
|
71
|
-
code_change_description: nil, code_change_files: nil, experiment_group_id: nil, dataset_id: nil, mock: "none",
|
|
90
|
+
name: nil, code_change_description: nil, code_change_files: nil, experiment_group_id: nil, dataset_id: nil, mock: "none",
|
|
72
91
|
adapt_inputs: nil, environment: nil, on_progress: nil)
|
|
73
92
|
Replay.run(
|
|
74
93
|
self, receiver, method_name,
|
|
75
|
-
trace_function_key:, limit:, trace_ids:, max_concurrency:,
|
|
94
|
+
trace_function_key:, limit:, trace_ids:, name:, max_concurrency:,
|
|
76
95
|
code_change_description:, code_change_files:, experiment_group_id:, dataset_id:, mock:, adapt_inputs:, environment:,
|
|
77
96
|
on_progress:
|
|
78
97
|
)
|
|
@@ -100,7 +119,10 @@ module Bitfab
|
|
|
100
119
|
# Called by Traceable, not intended for direct use.
|
|
101
120
|
def execute_span(trace_function_key:, span_name:, span_type:, function_name:, args:, kwargs:,
|
|
102
121
|
mock_on_replay: false)
|
|
103
|
-
|
|
122
|
+
# Decide at CALL time, not construction. The key may be set after the
|
|
123
|
+
# client is built (env loaded later), so re-checking per call lets a
|
|
124
|
+
# late-resolved key take effect.
|
|
125
|
+
return yield unless tracing_enabled?
|
|
104
126
|
|
|
105
127
|
# Span setup runs before the user's block. Tracing is a side-channel, so
|
|
106
128
|
# if anything here raises (id generation, trace-state bookkeeping, a
|
|
@@ -321,6 +343,48 @@ module Bitfab
|
|
|
321
343
|
|
|
322
344
|
private
|
|
323
345
|
|
|
346
|
+
# Resolve the API key lazily, the first time a span actually needs it.
|
|
347
|
+
#
|
|
348
|
+
# The key is intentionally NOT read at construction: a client built when a
|
|
349
|
+
# file is required can run before the app loads its env, so the key would be
|
|
350
|
+
# empty at construction even though it is set moments later. Resolution
|
|
351
|
+
# order: the configured value (a proc is called while still unresolved),
|
|
352
|
+
# then a fallback read of ENV["BITFAB_API_KEY"]. Once a non-empty key is
|
|
353
|
+
# found it is cached.
|
|
354
|
+
def resolve_api_key
|
|
355
|
+
return @resolved_api_key unless @resolved_api_key.nil?
|
|
356
|
+
|
|
357
|
+
from_config = @api_key_config.respond_to?(:call) ? @api_key_config.call : @api_key_config
|
|
358
|
+
candidate = if from_config && !from_config.to_s.strip.empty?
|
|
359
|
+
from_config
|
|
360
|
+
else
|
|
361
|
+
ENV["BITFAB_API_KEY"]
|
|
362
|
+
end
|
|
363
|
+
key = (candidate && !candidate.to_s.strip.empty?) ? candidate : nil
|
|
364
|
+
if key
|
|
365
|
+
@resolved_api_key = key
|
|
366
|
+
return key
|
|
367
|
+
end
|
|
368
|
+
if @strict
|
|
369
|
+
raise "Bitfab: no API key resolved. Set BITFAB_API_KEY or pass api_key: to " \
|
|
370
|
+
"Bitfab.configure. If a script loads env later, load it before the first " \
|
|
371
|
+
"traced call, or pass api_key: -> { ENV[\"BITFAB_API_KEY\"] }."
|
|
372
|
+
end
|
|
373
|
+
if @explicitly_enabled && !@api_key_warned
|
|
374
|
+
@api_key_warned = true
|
|
375
|
+
warn "Bitfab: api_key is empty: tracing is disabled. Provide a valid API key to enable tracing."
|
|
376
|
+
end
|
|
377
|
+
nil
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
# Whether tracing should run, decided lazily at call time. An explicit
|
|
381
|
+
# `enabled: false` short-circuits without ever touching the key.
|
|
382
|
+
def tracing_enabled?
|
|
383
|
+
return false unless @explicitly_enabled
|
|
384
|
+
|
|
385
|
+
!resolve_api_key.nil?
|
|
386
|
+
end
|
|
387
|
+
|
|
324
388
|
# Build an Enumerator that drives `source`, restoring `[trace_id, span_id]`
|
|
325
389
|
# on the iterating fiber so nested `bitfab_span` calls inside lazy / `each`
|
|
326
390
|
# callbacks nest under the parent span. Yielded values are collected as the
|
data/lib/bitfab/http_client.rb
CHANGED
|
@@ -107,10 +107,12 @@ module Bitfab
|
|
|
107
107
|
# each as { path:, before:, after: } (use "" for new/deleted files)
|
|
108
108
|
# @param experiment_group_id [String, nil] optional UUID grouping multiple
|
|
109
109
|
# replay runs into a single experiment batch
|
|
110
|
+
# @param name [String, nil] optional display name for the resulting
|
|
111
|
+
# experiment/test run
|
|
110
112
|
# @param dataset_id [String, nil] optional UUID of the dataset this replay
|
|
111
113
|
# runs against, stored on the resulting experiment for durable attribution
|
|
112
114
|
def start_replay(trace_function_key, limit, trace_ids: nil, code_change_description: nil,
|
|
113
|
-
code_change_files: nil, experiment_group_id: nil, include_db_branch_lease: false, dataset_id: nil)
|
|
115
|
+
code_change_files: nil, experiment_group_id: nil, name: nil, include_db_branch_lease: false, dataset_id: nil)
|
|
114
116
|
payload = {
|
|
115
117
|
"traceFunctionKey" => trace_function_key
|
|
116
118
|
}
|
|
@@ -118,6 +120,7 @@ module Bitfab
|
|
|
118
120
|
# already determines the count), so it's omitted when nil.
|
|
119
121
|
payload["limit"] = limit unless limit.nil?
|
|
120
122
|
payload["traceIds"] = trace_ids if trace_ids
|
|
123
|
+
payload["name"] = name unless name.nil?
|
|
121
124
|
payload["codeChangeDescription"] = code_change_description unless code_change_description.nil?
|
|
122
125
|
payload["codeChangeFiles"] = normalize_code_change_files(code_change_files) unless code_change_files.nil?
|
|
123
126
|
payload["experimentGroupId"] = experiment_group_id unless experiment_group_id.nil?
|
|
@@ -223,9 +226,12 @@ module Bitfab
|
|
|
223
226
|
end
|
|
224
227
|
|
|
225
228
|
def headers
|
|
229
|
+
# Resolve the key at request time, invoking the proc form if one was
|
|
230
|
+
# supplied (never read at construction).
|
|
231
|
+
key = @api_key.respond_to?(:call) ? @api_key.call : @api_key
|
|
226
232
|
{
|
|
227
233
|
"Content-Type" => "application/json",
|
|
228
|
-
"Authorization" => "Bearer #{
|
|
234
|
+
"Authorization" => "Bearer #{key}"
|
|
229
235
|
}
|
|
230
236
|
end
|
|
231
237
|
end
|
data/lib/bitfab/replay.rb
CHANGED
|
@@ -82,6 +82,7 @@ module Bitfab
|
|
|
82
82
|
# Ignored when trace_ids is passed (with a warning): an explicit ID list
|
|
83
83
|
# already determines how many traces replay.
|
|
84
84
|
# @param trace_ids [Array<String>, nil] optional list of trace IDs to replay (max 100)
|
|
85
|
+
# @param name [String, nil] optional display name for the resulting experiment/test run
|
|
85
86
|
# @param max_concurrency [Integer, nil] max threads for parallel replay (default: 10)
|
|
86
87
|
# @param code_change_description [String, nil] optional rationale for the
|
|
87
88
|
# code change being tested in this replay (stored on the experiment)
|
|
@@ -109,8 +110,9 @@ module Bitfab
|
|
|
109
110
|
# render replay progress (e.g. a per-trace log). A raising callback never
|
|
110
111
|
# crashes the run.
|
|
111
112
|
# @return [Hash] with :items, :test_run_id, :test_run_url
|
|
112
|
-
def run(client, receiver, method_name, trace_function_key:, limit: nil, trace_ids: nil,
|
|
113
|
-
|
|
113
|
+
def run(client, receiver, method_name, trace_function_key:, limit: nil, trace_ids: nil, name: nil,
|
|
114
|
+
max_concurrency: 10, code_change_description: nil, code_change_files: nil, experiment_group_id: nil,
|
|
115
|
+
dataset_id: nil, mock: "none",
|
|
114
116
|
adapt_inputs: nil, environment: nil, on_progress: nil)
|
|
115
117
|
unless MOCK_STRATEGIES.include?(mock.to_s)
|
|
116
118
|
raise ArgumentError, "Invalid mock strategy '#{mock}'. Must be one of: #{MOCK_STRATEGIES.join(", ")}"
|
|
@@ -153,6 +155,7 @@ module Bitfab
|
|
|
153
155
|
trace_function_key,
|
|
154
156
|
effective_limit,
|
|
155
157
|
trace_ids:,
|
|
158
|
+
name:,
|
|
156
159
|
code_change_description:,
|
|
157
160
|
code_change_files:,
|
|
158
161
|
experiment_group_id:,
|
data/lib/bitfab/version.rb
CHANGED
data/lib/bitfab.rb
CHANGED
|
@@ -59,8 +59,8 @@ module Bitfab
|
|
|
59
59
|
# @example
|
|
60
60
|
# Bitfab.configure(api_key: ENV["BITFAB_API_KEY"])
|
|
61
61
|
#
|
|
62
|
-
def configure(api_key
|
|
63
|
-
@client = Client.new(api_key:, service_url:, enabled:)
|
|
62
|
+
def configure(api_key: nil, service_url: nil, enabled: true, strict: false)
|
|
63
|
+
@client = Client.new(api_key:, service_url:, enabled:, strict:)
|
|
64
64
|
end
|
|
65
65
|
|
|
66
66
|
# Returns the global client, raising if not configured.
|