bitfab 0.51.0 → 0.51.2
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/README.md +2 -0
- data/lib/bitfab/client.rb +14 -6
- data/lib/bitfab/commit_ref.rb +184 -0
- data/lib/bitfab/git_command.rb +65 -0
- data/lib/bitfab/replay.rb +6 -20
- data/lib/bitfab/replay_registry.rb +36 -10
- data/lib/bitfab/version.rb +1 -1
- data/lib/bitfab.rb +2 -0
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ea799ee55cf7c1ac7167300d800d9593162ddc6b906557c12c3079607fcfc4f4
|
|
4
|
+
data.tar.gz: f7985925a6a8b3cf8c5e2ea4389bd8122646f5bf2eb5bf59e6769d9f533636b1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 10c67dc35cf10ed1c445108ce1801993b8f4115afda9411fe1dc3b9e12213ab090441e91c6a59a0864925822a09d344360e4d10ab15cac0a1a183c56d007972e
|
|
7
|
+
data.tar.gz: 2f968a4c58b66fd51d49c74199b20207a44aecd56285656b2ece8df04a76e9070b9f87c7dcbb53f2eeedfe61ba0a271d68660b934b4a56eb1f8fca278221f452
|
data/README.md
CHANGED
|
@@ -399,6 +399,8 @@ batch is never re-encoded to measure its size. If Bitfab accepts only part of a
|
|
|
399
399
|
the standard OTLP `partialSuccess` response and the SDK warns with the rejected-span count and
|
|
400
400
|
reason.
|
|
401
401
|
|
|
402
|
+
Every root trace also records the commit its code ran at as `commit_ref`. Deploy platforms such as Vercel, GitHub Actions, Railway, Render, Heroku, Cloudflare Pages, GitLab CI, Azure Pipelines, and CircleCI supply it through their build variables, a plain checkout resolves it from `git` once per process in the background, and a container built without either should set `BITFAB_COMMIT_SHA` at build time. Set `BITFAB_DISABLE_COMMIT_REF` to send no `commit_ref` at all.
|
|
403
|
+
|
|
402
404
|
Before finalizing a replay, the SDK flushes OTel and polls Bitfab's replay-status API until every
|
|
403
405
|
expected trace completion and span count is persisted.
|
|
404
406
|
|
data/lib/bitfab/client.rb
CHANGED
|
@@ -51,6 +51,7 @@ module Bitfab
|
|
|
51
51
|
# replay on this client (after any per-call mock_override). Instance
|
|
52
52
|
# state, no global; clear_mock_overrides resets it.
|
|
53
53
|
@mock_overrides = []
|
|
54
|
+
CommitRef.start_resolution
|
|
54
55
|
end
|
|
55
56
|
|
|
56
57
|
# The configured API key (a proc is resolved on read). Reflects what was
|
|
@@ -95,9 +96,12 @@ module Bitfab
|
|
|
95
96
|
# @param limit [Integer, nil] maximum number of traces to replay (default: 5;
|
|
96
97
|
# maximum: 5,000). Ignored when trace_ids or a dataset is passed because
|
|
97
98
|
# either source already determines how many traces replay. Supplying
|
|
98
|
-
# trace_ids also emits a warning.
|
|
99
|
-
#
|
|
100
|
-
#
|
|
99
|
+
# trace_ids also emits a warning. To replay part of a dataset selection,
|
|
100
|
+
# name the members to run in trace_ids rather than bounding it here.
|
|
101
|
+
# @param trace_ids [Array<String>, nil] optional list of trace IDs to replay (max 100).
|
|
102
|
+
# Passed alongside dataset_id or dataset_ids it pins which members of that
|
|
103
|
+
# selection replay, and the server rejects any ID none of those datasets
|
|
104
|
+
# contains
|
|
101
105
|
# @param max_concurrency [Integer, nil] max threads for parallel replay (default: 10)
|
|
102
106
|
# @param code_change_description [String, nil] optional rationale for the
|
|
103
107
|
# code change being tested in this replay (stored on the experiment).
|
|
@@ -112,10 +116,12 @@ module Bitfab
|
|
|
112
116
|
# replay runs into a single experiment batch
|
|
113
117
|
# @param dataset_ids [Array<String>, nil] optional UUIDs of the datasets this
|
|
114
118
|
# replay runs against, for benchmarking one method against several corpora
|
|
115
|
-
# in a single run; mutually exclusive with dataset_id
|
|
119
|
+
# in a single run; mutually exclusive with dataset_id. Passing trace_ids
|
|
120
|
+
# alongside it narrows the run to those members
|
|
116
121
|
# @param dataset_id [String, nil] optional UUID of the dataset this replay
|
|
117
|
-
# runs against, mutually exclusive with
|
|
118
|
-
# resulting experiment for durable attribution
|
|
122
|
+
# runs against, mutually exclusive with dataset_ids and stored on the
|
|
123
|
+
# resulting experiment for durable attribution. Alone it replays the
|
|
124
|
+
# dataset's full membership, and with trace_ids only those members
|
|
119
125
|
# @param grader_ids [Array<String>, nil] optional UUIDs of graders attached
|
|
120
126
|
# directly to this experiment, graded as the union with the dataset's
|
|
121
127
|
# runnable graders at completion; each must be an active/live grader in the
|
|
@@ -668,6 +674,8 @@ module Bitfab
|
|
|
668
674
|
if trace_state&.dig(:db_snapshot_ref)
|
|
669
675
|
raw_trace["db_snapshot_ref"] = trace_state[:db_snapshot_ref]
|
|
670
676
|
end
|
|
677
|
+
commit_ref = CommitRef.current
|
|
678
|
+
raw_trace["commit_ref"] = commit_ref if commit_ref
|
|
671
679
|
if db_snapshot_usage
|
|
672
680
|
usage = {"neon_branch_id" => db_snapshot_usage[:neon_branch_id]}
|
|
673
681
|
if db_snapshot_usage[:snapshot_timestamp]
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "uri"
|
|
4
|
+
require_relative "git_command"
|
|
5
|
+
|
|
6
|
+
module Bitfab
|
|
7
|
+
module CommitRef
|
|
8
|
+
EXPLICIT_SHA_ENV = "BITFAB_COMMIT_SHA"
|
|
9
|
+
DISABLE_ENV = "BITFAB_DISABLE_COMMIT_REF"
|
|
10
|
+
GIT_TIMEOUT_SECONDS = 2
|
|
11
|
+
|
|
12
|
+
VERCEL_PROVIDER_HOSTS = {
|
|
13
|
+
"github" => "github.com",
|
|
14
|
+
"gitlab" => "gitlab.com",
|
|
15
|
+
"bitbucket" => "bitbucket.org"
|
|
16
|
+
}.freeze
|
|
17
|
+
|
|
18
|
+
PLATFORM_ENVS = [
|
|
19
|
+
["VERCEL_GIT_COMMIT_SHA", "VERCEL_GIT_COMMIT_REF", :vercel],
|
|
20
|
+
["GITHUB_SHA", "GITHUB_REF_NAME", ["GITHUB_SERVER_URL", "GITHUB_REPOSITORY"]],
|
|
21
|
+
["RAILWAY_GIT_COMMIT_SHA", "RAILWAY_GIT_BRANCH", nil],
|
|
22
|
+
["RENDER_GIT_COMMIT", "RENDER_GIT_BRANCH", nil],
|
|
23
|
+
["SOURCE_VERSION", nil, nil],
|
|
24
|
+
["CF_PAGES_COMMIT_SHA", "CF_PAGES_BRANCH", nil],
|
|
25
|
+
["CI_COMMIT_SHA", "CI_COMMIT_REF_NAME", ["CI_REPOSITORY_URL"]],
|
|
26
|
+
["BUILD_SOURCEVERSION", "BUILD_SOURCEBRANCHNAME", ["BUILD_REPOSITORY_URI"]],
|
|
27
|
+
["CIRCLE_SHA1", "CIRCLE_BRANCH", ["CIRCLE_REPOSITORY_URL"]]
|
|
28
|
+
].freeze
|
|
29
|
+
|
|
30
|
+
REMOTE_ENVS = %w[
|
|
31
|
+
GITHUB_SERVER_URL GITHUB_REPOSITORY CI_REPOSITORY_URL BUILD_REPOSITORY_URI
|
|
32
|
+
CIRCLE_REPOSITORY_URL VERCEL_GIT_PROVIDER VERCEL_GIT_REPO_OWNER VERCEL_GIT_REPO_SLUG
|
|
33
|
+
].freeze
|
|
34
|
+
|
|
35
|
+
ENV_NAMES = ([EXPLICIT_SHA_ENV, DISABLE_ENV] + PLATFORM_ENVS.flat_map { |sha, branch, _| [sha, branch].compact } + REMOTE_ENVS).freeze
|
|
36
|
+
|
|
37
|
+
@lock = Mutex.new
|
|
38
|
+
@resolved = false
|
|
39
|
+
@ref = nil
|
|
40
|
+
@git_thread = nil
|
|
41
|
+
|
|
42
|
+
module_function
|
|
43
|
+
|
|
44
|
+
def read(env, name)
|
|
45
|
+
return nil if name.nil?
|
|
46
|
+
|
|
47
|
+
value = env[name].to_s.strip
|
|
48
|
+
value.empty? ? nil : value
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def remote_from_env(env, spec)
|
|
52
|
+
case spec
|
|
53
|
+
when :vercel
|
|
54
|
+
host = VERCEL_PROVIDER_HOSTS[read(env, "VERCEL_GIT_PROVIDER").to_s.downcase]
|
|
55
|
+
owner = read(env, "VERCEL_GIT_REPO_OWNER")
|
|
56
|
+
slug = read(env, "VERCEL_GIT_REPO_SLUG")
|
|
57
|
+
(host && owner && slug) ? "#{host}/#{owner}/#{slug}" : nil
|
|
58
|
+
when Array
|
|
59
|
+
parts = spec.map { |name| read(env, name) }
|
|
60
|
+
parts.all? ? normalize_remote(parts.join("/")) : nil
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def normalize_remote(raw)
|
|
65
|
+
value = raw.to_s.strip
|
|
66
|
+
return nil if value.empty?
|
|
67
|
+
|
|
68
|
+
if value.include?("://")
|
|
69
|
+
parsed = begin
|
|
70
|
+
URI.parse(value)
|
|
71
|
+
rescue URI::InvalidURIError
|
|
72
|
+
return nil
|
|
73
|
+
end
|
|
74
|
+
host = parsed.host.to_s.downcase
|
|
75
|
+
path = parsed.path.to_s
|
|
76
|
+
else
|
|
77
|
+
head, separator, tail = value.partition(":")
|
|
78
|
+
host = separator.empty? ? "" : head.rpartition("@").last
|
|
79
|
+
path = separator.empty? ? value : tail
|
|
80
|
+
end
|
|
81
|
+
path = path.gsub(%r{\A/+|/+\z}, "")
|
|
82
|
+
path = path.delete_suffix(".git").sub(%r{/+\z}, "") if path.end_with?(".git")
|
|
83
|
+
return (path.empty? ? nil : path) if host.empty?
|
|
84
|
+
|
|
85
|
+
path.empty? ? host : "#{host}/#{path}"
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def resolve_from_env(env)
|
|
89
|
+
explicit = read(env, EXPLICIT_SHA_ENV)
|
|
90
|
+
platform = PLATFORM_ENVS.find { |sha_env, _, _| read(env, sha_env) }
|
|
91
|
+
if platform.nil?
|
|
92
|
+
return nil if explicit.nil?
|
|
93
|
+
|
|
94
|
+
return {"sha" => explicit, "branch" => nil, "dirty" => nil, "remote" => nil, "root_sha" => nil}
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
sha_env, branch_env, remote_spec = platform
|
|
98
|
+
{
|
|
99
|
+
"sha" => explicit || read(env, sha_env),
|
|
100
|
+
"branch" => read(env, branch_env),
|
|
101
|
+
"dirty" => nil,
|
|
102
|
+
"remote" => remote_from_env(env, remote_spec),
|
|
103
|
+
"root_sha" => nil
|
|
104
|
+
}
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def git(cwd, *args)
|
|
108
|
+
GitCommand.run(cwd, args, timeout: GIT_TIMEOUT_SECONDS)&.strip
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def resolve_from_git(cwd)
|
|
112
|
+
sha = git(cwd, "rev-parse", "HEAD")
|
|
113
|
+
return nil if sha.nil? || sha.empty?
|
|
114
|
+
|
|
115
|
+
branch = git(cwd, "symbolic-ref", "--short", "-q", "HEAD")
|
|
116
|
+
status = git(cwd, "status", "--porcelain")
|
|
117
|
+
roots = git(cwd, "rev-list", "--max-parents=0", "HEAD")
|
|
118
|
+
{
|
|
119
|
+
"sha" => sha,
|
|
120
|
+
"branch" => (branch.nil? || branch.empty?) ? nil : branch,
|
|
121
|
+
"dirty" => status.nil? ? nil : !status.empty?,
|
|
122
|
+
"remote" => normalize_remote(git(cwd, "remote", "get-url", "origin")),
|
|
123
|
+
"root_sha" => (roots.nil? || roots.empty?) ? nil : roots.split.min
|
|
124
|
+
}
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def start_resolution
|
|
128
|
+
@lock.synchronize do
|
|
129
|
+
return if @resolved || @git_thread
|
|
130
|
+
|
|
131
|
+
if read(ENV, DISABLE_ENV)
|
|
132
|
+
@resolved = true
|
|
133
|
+
return
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
from_env = resolve_from_env(ENV)
|
|
137
|
+
if from_env
|
|
138
|
+
@ref = from_env
|
|
139
|
+
@resolved = true
|
|
140
|
+
return
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
cwd = begin
|
|
144
|
+
Dir.pwd
|
|
145
|
+
rescue SystemCallError
|
|
146
|
+
@resolved = true
|
|
147
|
+
return
|
|
148
|
+
end
|
|
149
|
+
@git_thread = Thread.new { resolve_git_in_background(cwd) }
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def resolve_git_in_background(cwd)
|
|
154
|
+
ref = resolve_from_git(cwd)
|
|
155
|
+
@lock.synchronize do
|
|
156
|
+
unless @resolved
|
|
157
|
+
@ref = ref
|
|
158
|
+
@resolved = true
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
rescue
|
|
162
|
+
@lock.synchronize { @resolved = true }
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def current
|
|
166
|
+
start_resolution
|
|
167
|
+
@lock.synchronize { @ref }
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def settle(timeout = 5)
|
|
171
|
+
thread = @lock.synchronize { @git_thread }
|
|
172
|
+
thread&.join(timeout)
|
|
173
|
+
nil
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def reset!
|
|
177
|
+
@lock.synchronize do
|
|
178
|
+
@ref = nil
|
|
179
|
+
@resolved = false
|
|
180
|
+
@git_thread = nil
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "open3"
|
|
4
|
+
|
|
5
|
+
module Bitfab
|
|
6
|
+
module GitCommand
|
|
7
|
+
KILL_GRACE_SECONDS = 1
|
|
8
|
+
|
|
9
|
+
module_function
|
|
10
|
+
|
|
11
|
+
def run(cwd, args, timeout:)
|
|
12
|
+
Open3.popen3("git", *args, chdir: cwd, **spawn_options) do |stdin, stdout, stderr, wait_thread|
|
|
13
|
+
stdin.close
|
|
14
|
+
out = +""
|
|
15
|
+
reader = Thread.new { out = read_all(stdout) }
|
|
16
|
+
drain = Thread.new { read_all(stderr) }
|
|
17
|
+
finished = wait_thread.join(timeout)
|
|
18
|
+
stop(wait_thread) unless finished
|
|
19
|
+
settle(reader, stdout)
|
|
20
|
+
settle(drain, stderr)
|
|
21
|
+
(finished && wait_thread.value.success?) ? out.force_encoding("UTF-8").scrub : nil
|
|
22
|
+
end
|
|
23
|
+
rescue
|
|
24
|
+
nil
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def spawn_options
|
|
28
|
+
Gem.win_platform? ? {} : {pgroup: true}
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def kill_target(wait_thread)
|
|
32
|
+
Gem.win_platform? ? wait_thread.pid : -wait_thread.pid
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def read_all(io)
|
|
36
|
+
io.read.to_s
|
|
37
|
+
rescue
|
|
38
|
+
""
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def settle(thread, io)
|
|
42
|
+
return if thread.join(KILL_GRACE_SECONDS)
|
|
43
|
+
|
|
44
|
+
io.close
|
|
45
|
+
thread.join
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def stop(wait_thread)
|
|
49
|
+
target = kill_target(wait_thread)
|
|
50
|
+
if Gem.win_platform?
|
|
51
|
+
signal("KILL", target)
|
|
52
|
+
else
|
|
53
|
+
signal("TERM", target)
|
|
54
|
+
signal("KILL", target) unless wait_thread.join(KILL_GRACE_SECONDS)
|
|
55
|
+
end
|
|
56
|
+
wait_thread.join
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def signal(name, target)
|
|
60
|
+
Process.kill(name, target)
|
|
61
|
+
rescue
|
|
62
|
+
nil
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
data/lib/bitfab/replay.rb
CHANGED
|
@@ -6,6 +6,7 @@ require "open3"
|
|
|
6
6
|
require "timeout"
|
|
7
7
|
|
|
8
8
|
require_relative "constants"
|
|
9
|
+
require_relative "git_command"
|
|
9
10
|
require_relative "mock_override"
|
|
10
11
|
require_relative "serialize"
|
|
11
12
|
require_relative "traceable"
|
|
@@ -206,12 +207,14 @@ module Bitfab
|
|
|
206
207
|
# replay runs into a single experiment batch
|
|
207
208
|
# @param dataset_id [String, nil] optional UUID of the dataset this replay
|
|
208
209
|
# runs against, stored on the resulting experiment for durable attribution;
|
|
209
|
-
# mutually exclusive with dataset_ids
|
|
210
|
+
# mutually exclusive with dataset_ids. Alone it replays the dataset's full
|
|
211
|
+
# membership, and with trace_ids it replays only those members
|
|
210
212
|
# @param dataset_ids [Array<String>, nil] optional UUIDs of the datasets this
|
|
211
213
|
# replay runs against, for benchmarking one method against several corpora
|
|
212
214
|
# in a single run; mutually exclusive with dataset_id. The run replays the
|
|
213
215
|
# union of their traces, graded by the union of their graders, and is
|
|
214
|
-
# attributed to every one of them
|
|
216
|
+
# attributed to every one of them. Passing trace_ids alongside it narrows
|
|
217
|
+
# the run to those members
|
|
215
218
|
# @param grader_ids [Array<String>, nil] optional UUIDs of graders attached
|
|
216
219
|
# directly to this experiment, graded as the union with the dataset's
|
|
217
220
|
# runnable graders at completion; each must be an active/live grader in the
|
|
@@ -267,10 +270,6 @@ module Bitfab
|
|
|
267
270
|
end
|
|
268
271
|
end
|
|
269
272
|
resolved_dataset_ids = resolve_dataset_ids(dataset_id, dataset_ids)
|
|
270
|
-
if trace_ids && resolved_dataset_ids
|
|
271
|
-
raise ArgumentError,
|
|
272
|
-
"trace_ids and dataset_ids select different replay sources and cannot be used together."
|
|
273
|
-
end
|
|
274
273
|
if limit && trace_ids
|
|
275
274
|
warn "Bitfab: limit is ignored when trace_ids is passed: the explicit trace ID list already " \
|
|
276
275
|
"determines how many traces replay."
|
|
@@ -562,20 +561,7 @@ module Bitfab
|
|
|
562
561
|
end
|
|
563
562
|
|
|
564
563
|
def git(cwd, args)
|
|
565
|
-
|
|
566
|
-
# leaked to the host process's console.
|
|
567
|
-
out, _err, status = Timeout.timeout(30) do
|
|
568
|
-
Open3.capture3("git", *args, chdir: cwd)
|
|
569
|
-
end
|
|
570
|
-
return nil unless status.success?
|
|
571
|
-
|
|
572
|
-
# Tag as UTF-8 and scrub invalid bytes so blob contents compare cleanly
|
|
573
|
-
# against the UTF-8 working-file reads and never break JSON.generate later
|
|
574
|
-
# (a non-UTF-8 file that slips past the null-byte check would otherwise
|
|
575
|
-
# abort the whole start-replay request).
|
|
576
|
-
out.force_encoding("UTF-8").scrub
|
|
577
|
-
rescue
|
|
578
|
-
nil
|
|
564
|
+
GitCommand.run(cwd, args, timeout: 30)
|
|
579
565
|
end
|
|
580
566
|
|
|
581
567
|
def cc_ref_exists?(root, ref)
|
|
@@ -81,8 +81,26 @@ module Bitfab
|
|
|
81
81
|
|
|
82
82
|
# Argument parsing and output for the SDK-owned replay command.
|
|
83
83
|
module ReplayCli
|
|
84
|
+
MAX_PINNED_TRACE_IDS = 100
|
|
85
|
+
|
|
84
86
|
module_function
|
|
85
87
|
|
|
88
|
+
def pinned_dataset_members(client, dataset_ids, limit)
|
|
89
|
+
members = dataset_ids.flat_map { |dataset_id|
|
|
90
|
+
client.datasets.list_traces(dataset_id).fetch("traceIds")
|
|
91
|
+
}.uniq.sort
|
|
92
|
+
return nil if limit >= members.length
|
|
93
|
+
|
|
94
|
+
if limit > MAX_PINNED_TRACE_IDS
|
|
95
|
+
raise ArgumentError,
|
|
96
|
+
"Bounding a dataset replay to #{limit} names one trace ID per replayed item, and " \
|
|
97
|
+
"a replay accepts at most #{MAX_PINNED_TRACE_IDS}. Drop the limit to replay " \
|
|
98
|
+
"every selected dataset in full."
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
members.first(limit)
|
|
102
|
+
end
|
|
103
|
+
|
|
86
104
|
def run(registry, argv: ARGV, stdout: $stdout, stderr: $stderr)
|
|
87
105
|
args = parse(registry, argv.dup)
|
|
88
106
|
registration = registry.fetch(args.fetch(:pipeline))
|
|
@@ -108,20 +126,25 @@ module Bitfab
|
|
|
108
126
|
end
|
|
109
127
|
|
|
110
128
|
if args[:trace_ids]
|
|
111
|
-
options.delete(:limit)
|
|
112
129
|
options.delete(:dataset_ids)
|
|
113
130
|
options[:trace_ids] = args[:trace_ids]
|
|
114
131
|
elsif args[:dataset_ids]
|
|
115
132
|
options.delete(:trace_ids)
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
bound = args[:limit] || options[:limit]
|
|
136
|
+
options.delete(:limit)
|
|
137
|
+
bounded_dataset_ids = args[:dataset_ids] || options[:dataset_ids]
|
|
138
|
+
|
|
139
|
+
if options[:trace_ids]
|
|
140
|
+
options[:trace_ids] = options[:trace_ids].first(bound) if bound
|
|
141
|
+
elsif bounded_dataset_ids
|
|
142
|
+
if bound
|
|
143
|
+
pinned = pinned_dataset_members(registration.client, bounded_dataset_ids, bound)
|
|
144
|
+
options[:trace_ids] = pinned if pinned
|
|
145
|
+
end
|
|
123
146
|
else
|
|
124
|
-
options[:limit] =
|
|
147
|
+
options[:limit] = bound || 10
|
|
125
148
|
end
|
|
126
149
|
|
|
127
150
|
%i[name max_concurrency experiment_group_id dataset_ids grader_ids mock].each do |key|
|
|
@@ -179,7 +202,10 @@ module Bitfab
|
|
|
179
202
|
args = {}
|
|
180
203
|
parser = OptionParser.new do |options|
|
|
181
204
|
options.banner = "Usage: bitfab-replay <#{registry.names.join("|")}> [options]"
|
|
182
|
-
options.on("--limit N", Integer
|
|
205
|
+
options.on("--limit N", Integer,
|
|
206
|
+
"How many traces to replay. It bounds a trace ID or dataset selection instead of replacing it") do |value|
|
|
207
|
+
args[:limit] = positive_integer("--limit", value)
|
|
208
|
+
end
|
|
183
209
|
options.on("--trace-ids IDS") { |value| args[:trace_ids] = comma_separated("--trace-ids", value) }
|
|
184
210
|
options.on("--name NAME") { |value| args[:name] = value }
|
|
185
211
|
options.on("--concurrency N", Integer) do |value|
|
data/lib/bitfab/version.rb
CHANGED
data/lib/bitfab.rb
CHANGED
|
@@ -8,6 +8,8 @@ require_relative "bitfab/warn_once"
|
|
|
8
8
|
require_relative "bitfab/payload_budget"
|
|
9
9
|
require_relative "bitfab/serialize"
|
|
10
10
|
require_relative "bitfab/db_snapshot"
|
|
11
|
+
require_relative "bitfab/git_command"
|
|
12
|
+
require_relative "bitfab/commit_ref"
|
|
11
13
|
require_relative "bitfab/span_context"
|
|
12
14
|
require_relative "bitfab/http_client"
|
|
13
15
|
require_relative "bitfab/datasets"
|
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.51.
|
|
4
|
+
version: 0.51.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Harvest Team
|
|
@@ -140,10 +140,12 @@ files:
|
|
|
140
140
|
- exe/bitfab-replay
|
|
141
141
|
- lib/bitfab.rb
|
|
142
142
|
- lib/bitfab/client.rb
|
|
143
|
+
- lib/bitfab/commit_ref.rb
|
|
143
144
|
- lib/bitfab/compress.rb
|
|
144
145
|
- lib/bitfab/constants.rb
|
|
145
146
|
- lib/bitfab/datasets.rb
|
|
146
147
|
- lib/bitfab/db_snapshot.rb
|
|
148
|
+
- lib/bitfab/git_command.rb
|
|
147
149
|
- lib/bitfab/http_client.rb
|
|
148
150
|
- lib/bitfab/mock_override.rb
|
|
149
151
|
- lib/bitfab/otel.rb
|