@bitfab/sdk 0.65.8 → 0.65.10

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.
package/cloudReplay.py CHANGED
@@ -154,6 +154,7 @@ CODE_CHANGE_PATH_ENV = "BITFAB_CODE_CHANGE_PATH"
154
154
  CODE_CHANGE_MAX_FILES = 60
155
155
  CODE_CHANGE_MAX_FILE_BYTES = 500_000
156
156
  CODE_CHANGE_MAX_TOTAL_BYTES = 2_000_000
157
+ BRANCH_WALK_LIMIT = 1000
157
158
  CHECK_ARGUMENTS_SECONDS = 300
158
159
  CHECK_ARGUMENTS_FLAG = "--check-arguments"
159
160
  REPLAY_ARGUMENTS_FLAG = "--replay-arguments"
@@ -933,6 +934,98 @@ def current_branch(root):
933
934
  return None
934
935
 
935
936
 
937
+ def quiet_git(root, *args):
938
+ try:
939
+ return git(root, *args)
940
+ except CommandError:
941
+ return ""
942
+
943
+
944
+ def default_branch(root):
945
+ origin_head = quiet_git(root, "rev-parse", "--abbrev-ref", "origin/HEAD")
946
+ candidates = [
947
+ origin_head if origin_head and origin_head != "origin/HEAD" else None,
948
+ "origin/main",
949
+ "origin/master",
950
+ "main",
951
+ "master",
952
+ ]
953
+ return next(
954
+ (
955
+ ref
956
+ for ref in dict.fromkeys(filter(None, candidates))
957
+ if quiet_git(root, "rev-parse", "--verify", "-q", f"{ref}^{{commit}}")
958
+ ),
959
+ None,
960
+ )
961
+
962
+
963
+ def current_branch_refs(root):
964
+ branch = current_branch(root)
965
+ if not branch:
966
+ return set()
967
+ return {f"refs/heads/{branch}"} | {
968
+ f"refs/remotes/{remote}/{branch}"
969
+ for remote in quiet_git(root, "remote").split()
970
+ }
971
+
972
+
973
+ def own_branch_refs(root):
974
+ upstream = quiet_git(root, "rev-parse", "--symbolic-full-name", "@{upstream}")
975
+ own = current_branch_refs(root)
976
+ if upstream:
977
+ own.add(upstream)
978
+ return own
979
+
980
+
981
+ def on_default_branch(root, default):
982
+ full_name = quiet_git(root, "rev-parse", "--symbolic-full-name", default)
983
+ return bool(full_name) and full_name in current_branch_refs(root)
984
+
985
+
986
+ def branch_tips(root):
987
+ own = own_branch_refs(root)
988
+ listing = quiet_git(
989
+ root,
990
+ "for-each-ref",
991
+ "--format=%(objectname) %(refname)",
992
+ "refs/heads",
993
+ "refs/remotes",
994
+ )
995
+ tips = {}
996
+ for line in listing.splitlines():
997
+ sha, _, ref = line.partition(" ")
998
+ if ref and ref not in own and not ref.endswith("/HEAD"):
999
+ tips.setdefault(
1000
+ sha, ref.removeprefix("refs/heads/").removeprefix("refs/remotes/")
1001
+ )
1002
+ return tips
1003
+
1004
+
1005
+ def branch_point(root):
1006
+ head = quiet_git(root, "rev-parse", "HEAD")
1007
+ if not head:
1008
+ return None
1009
+ default = default_branch(root)
1010
+ if default and on_default_branch(root, default):
1011
+ return None
1012
+ default_base = quiet_git(root, "merge-base", "HEAD", default) if default else ""
1013
+ walk = ["rev-list", "--first-parent", f"--max-count={BRANCH_WALK_LIMIT}", "HEAD"]
1014
+ if default_base:
1015
+ walk.append(f"^{default_base}")
1016
+ tips = branch_tips(root)
1017
+ for sha in quiet_git(root, *walk).split()[1:]:
1018
+ if sha in tips:
1019
+ return {"sha": sha, "ref": tips[sha]}
1020
+ if default_base and default_base != head:
1021
+ return {"sha": default_base, "ref": default}
1022
+ return None
1023
+
1024
+
1025
+ def replay_artifact(path):
1026
+ return path == ".bitfab" or path.startswith(".bitfab/")
1027
+
1028
+
936
1029
  def sensitive(path):
937
1030
  parts = Path(path).parts
938
1031
  name = Path(path).name.lower()
@@ -1033,12 +1126,20 @@ def snapshot(
1033
1126
  }
1034
1127
  if preview:
1035
1128
  return summary
1129
+ code_change_base = request.get("codeChangeBase") or {}
1130
+ extra_parent = (
1131
+ ["-p", code_change_base["sha"]]
1132
+ if not any(not replay_artifact(path) for _, path in changes)
1133
+ and SHA.fullmatch(str(code_change_base.get("sha", "")))
1134
+ else []
1135
+ )
1036
1136
  sha = git(
1037
1137
  root,
1038
1138
  "commit-tree",
1039
1139
  tree,
1040
1140
  "-p",
1041
1141
  head,
1142
+ *extra_parent,
1042
1143
  input=f"{snapshot_subject(request['id'], on_push)}\n\n{json.dumps(request)}\n",
1043
1144
  )
1044
1145
  return {**summary, "sha": sha}
@@ -1360,6 +1461,9 @@ def submit(root, repo, workflow, parsed, path):
1360
1461
  branch = current_branch(root)
1361
1462
  if branch:
1362
1463
  request["branch"] = branch
1464
+ code_change_base = branch_point(root)
1465
+ if code_change_base:
1466
+ request["codeChangeBase"] = code_change_base
1363
1467
  check_arguments(parsed["replay"])
1364
1468
  allowed = allowed_files(root, parsed)
1365
1469
  if "--cloud-preview" in parsed["switches"]:
@@ -2067,7 +2171,9 @@ def changed_paths(root, base, commit):
2067
2171
  yield status, before_path, path
2068
2172
 
2069
2173
 
2070
- def snapshot_code_change(root, base, commit, name):
2174
+ def snapshot_code_change(
2175
+ root, base, commit, name, against="uncommitted (vs HEAD)", subject_ref=None
2176
+ ):
2071
2177
  files, total = [], 0
2072
2178
  for status, before_path, path in changed_paths(root, base, commit):
2073
2179
  if len(files) >= CODE_CHANGE_MAX_FILES:
@@ -2083,14 +2189,34 @@ def snapshot_code_change(root, base, commit, name):
2083
2189
  files.append({"path": path, "before": before, "after": after})
2084
2190
  if not files:
2085
2191
  return None
2086
- head = (name or "").strip() or git(root, "log", "-1", "--format=%s", base)
2192
+ head = (name or "").strip() or git(
2193
+ root, "log", "-1", "--format=%s", subject_ref or base
2194
+ )
2087
2195
  word = "file" if len(files) == 1 else "files"
2088
2196
  return {
2089
- "description": f"{head or 'Working-tree change'} ({len(files)} {word} changed uncommitted (vs HEAD))",
2197
+ "description": f"{head or 'Working-tree change'} ({len(files)} {word} changed {against})",
2090
2198
  "files": files,
2091
2199
  }
2092
2200
 
2093
2201
 
2202
+ def branch_code_change(root, base, commit, request):
2203
+ code_change_base = request.get("codeChangeBase")
2204
+ if not isinstance(code_change_base, dict):
2205
+ return None
2206
+ sha, ref = code_change_base.get("sha"), code_change_base.get("ref")
2207
+ if not (
2208
+ isinstance(sha, str)
2209
+ and SHA.fullmatch(sha)
2210
+ and isinstance(ref, str)
2211
+ and BRANCH_NAME.fullmatch(ref)
2212
+ and quiet_git(root, "rev-parse", "--verify", "-q", f"{commit}^2") == sha
2213
+ ):
2214
+ return None
2215
+ return snapshot_code_change(
2216
+ root, sha, commit, replay_name(request["args"]), f"vs {ref}", base
2217
+ )
2218
+
2219
+
2094
2220
  def write_code_change(change):
2095
2221
  handle, path = tempfile.mkstemp(
2096
2222
  prefix="bitfab-code-change-",
@@ -2127,6 +2253,7 @@ def execute():
2127
2253
  None
2128
2254
  if explicit_change or base is None
2129
2255
  else snapshot_code_change(root, base, commit, replay_name(request["args"]))
2256
+ or branch_code_change(root, base, commit, request)
2130
2257
  )
2131
2258
  run_environment = dict(environment)
2132
2259
  if change:
@@ -5,7 +5,7 @@ import { ExportResultCode } from "@opentelemetry/core";
5
5
  import { resourceFromAttributes } from "@opentelemetry/resources";
6
6
  import { AlwaysOnSampler, BasicTracerProvider, BatchSpanProcessor } from "@opentelemetry/sdk-trace-base";
7
7
  //#region src/version.generated.ts
8
- const __version__ = "0.65.8";
8
+ const __version__ = "0.65.10";
9
9
  const __packageName__ = "@bitfab/sdk";
10
10
  //#endregion
11
11
  //#region src/traceCompletion.ts
@@ -2160,4 +2160,4 @@ function experimentIdWireFields(experimentId) {
2160
2160
  //#endregion
2161
2161
  export { readEnv as C, MixedTracingError as S, getReplayContext as _, http_exports as a, gitRunner as b, recordCallerTraceMetadata as c, DECLARED_NODE_FIELD as d, ROOT_TRACE_FUNCTION_KEY_FIELD as f, MAX_COMPRESSIBLE_SPAN_CARRIER_BYTES as g, makeSpanOrigin as h, flushTraces as i, retireTraceMetadata as l, isFrameworkInstrumentation as m, SPAN_TYPES as n, unrefTimer as o, SimulationPlan as p, experimentIdWireFields as r, callerTraceMetadata as s, HttpClient as t, CONTENT_OFF_KEY as u, replayContextReady as v, __version__ as w, BitfabError as x, runWithReplayContext as y };
2162
2162
 
2163
- //# sourceMappingURL=http-C9d2uNjI.js.map
2163
+ //# sourceMappingURL=http-BsQE3J6v.js.map