bitfab 0.64.4 → 0.64.5
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 +262 -19
- package/dist/{chunk-XASGXGQN.js → chunk-4IRNG6KB.js} +2 -2
- package/dist/{chunk-XASGXGQN.js.map → chunk-4IRNG6KB.js.map} +1 -1
- package/dist/{chunk-OEHQPSOB.js → chunk-A3CCZ7Q5.js} +3 -3
- package/dist/{chunk-ZPVUSLIU.js → chunk-LRY4Y7HE.js} +2 -2
- package/dist/{chunk-ZPVUSLIU.js.map → chunk-LRY4Y7HE.js.map} +1 -1
- package/dist/{chunk-I6B5R2FP.js → chunk-QF7FLTXB.js} +2 -2
- package/dist/{chunk-UMZDRAAK.js → chunk-YT45ZRGM.js} +6 -6
- package/dist/{http-53RWIBXJ.js → http-6TAXWGVJ.js} +2 -2
- package/dist/{http-Y6T6T2AP.js → http-OGYWTBEP.js} +2 -2
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +3 -3
- package/dist/node.cjs +1 -1
- package/dist/node.cjs.map +1 -1
- package/dist/node.js +3 -3
- package/dist/{replay-SIKHFPKA.js → replay-LOAD2DLO.js} +3 -3
- package/dist/replayCli.js +2 -2
- package/dist/seedCli.js +2 -2
- package/package.json +1 -1
- /package/dist/{chunk-OEHQPSOB.js.map → chunk-A3CCZ7Q5.js.map} +0 -0
- /package/dist/{chunk-I6B5R2FP.js.map → chunk-QF7FLTXB.js.map} +0 -0
- /package/dist/{chunk-UMZDRAAK.js.map → chunk-YT45ZRGM.js.map} +0 -0
- /package/dist/{http-53RWIBXJ.js.map → http-6TAXWGVJ.js.map} +0 -0
- /package/dist/{http-Y6T6T2AP.js.map → http-OGYWTBEP.js.map} +0 -0
- /package/dist/{replay-SIKHFPKA.js.map → replay-LOAD2DLO.js.map} +0 -0
package/cloudReplay.py
CHANGED
|
@@ -34,6 +34,7 @@ REPLAY_COMMAND_ENV = "BITFAB_REPLAY_COMMAND"
|
|
|
34
34
|
SDK_LANGUAGE_ENV = "BITFAB_SDK_LANGUAGE"
|
|
35
35
|
CHECK_COMMAND_ENV = "BITFAB_REPLAY_CHECK"
|
|
36
36
|
REQUEST_VERSION = 3
|
|
37
|
+
OLDEST_DISPATCHED_VERSION = 2
|
|
37
38
|
RESULT_LINE = "bitfab-replay-result "
|
|
38
39
|
RESULT_CHUNK = 4000
|
|
39
40
|
OUTPUT_BEGIN = "bitfab-replay-output-begin"
|
|
@@ -87,14 +88,20 @@ ENVIRONMENT_SETTING = re.compile(
|
|
|
87
88
|
PUSH_TRIGGER = re.compile(
|
|
88
89
|
r"^[ \t]*\"?(?:on\"?[ \t]*:.*\bpush\b|push\"?[ \t]*:)", re.MULTILINE
|
|
89
90
|
)
|
|
90
|
-
|
|
91
|
+
DISPATCH_INPUTS = ("execution_id", "request")
|
|
92
|
+
BRANCH_NAME = re.compile(r"[A-Za-z0-9][A-Za-z0-9._/-]*")
|
|
91
93
|
FOLLOW_FLAGS = (
|
|
92
94
|
"--cloud-status",
|
|
93
95
|
"--cloud-watch",
|
|
94
96
|
"--cloud-cancel",
|
|
95
97
|
"--cloud-cleanup",
|
|
96
98
|
)
|
|
97
|
-
CLOUD_VALUE_FLAGS = (
|
|
99
|
+
CLOUD_VALUE_FLAGS = (
|
|
100
|
+
*FOLLOW_FLAGS,
|
|
101
|
+
"--cloud-request-id",
|
|
102
|
+
"--cloud-timeout",
|
|
103
|
+
"--cloud-ref",
|
|
104
|
+
)
|
|
98
105
|
CLOUD_SWITCHES = ("--cloud", "--cloud-preview", "--cloud-detach", "--cloud-full-output")
|
|
99
106
|
RENAMED_FLAGS = {
|
|
100
107
|
"--cloud-dry-run": "--cloud-preview",
|
|
@@ -144,6 +151,23 @@ Options added by --cloud:
|
|
|
144
151
|
GitHub job log holds.
|
|
145
152
|
--cloud-timeout MIN Stop the replay after MIN minutes (1..7200).
|
|
146
153
|
--cloud-request-id ID Recover a submission whose response was lost.
|
|
154
|
+
--cloud-ref BRANCH Replay BRANCH as it is on GitHub, with no snapshot of your
|
|
155
|
+
working tree, and record the experiment on BRANCH. Meant for
|
|
156
|
+
CI jobs, such as a nightly replay of dev. Needs only GitHub
|
|
157
|
+
access that can run Actions, and a workflow on BRANCH that
|
|
158
|
+
declares a request input.
|
|
159
|
+
|
|
160
|
+
A workflow can also dispatch bitfab-replay.yml itself on any branch other than a
|
|
161
|
+
bitfab-replay/ snapshot branch, passing the replay as JSON in the request input:
|
|
162
|
+
|
|
163
|
+
{"args": ["--registry", "scripts/replay.ts", "classify", "--dataset-ids", "UUID"],
|
|
164
|
+
"cwd": "web", "timeoutMinutes": 60}
|
|
165
|
+
|
|
166
|
+
args are the replay options without --cloud. cwd is the directory to replay from,
|
|
167
|
+
relative to the repository root, and defaults to the root. timeoutMinutes and
|
|
168
|
+
fullOutput (true) match --cloud-timeout and --cloud-full-output. version and id are
|
|
169
|
+
optional: version names the request format (3 now, 2 still accepted), and id is the
|
|
170
|
+
execution UUID, which must match an execution_id input when the workflow has one.
|
|
147
171
|
|
|
148
172
|
Follow a replay by the execution UUID it prints:
|
|
149
173
|
--cloud-watch ID | --cloud-status ID | --cloud-cancel ID | --cloud-cleanup ID
|
|
@@ -611,6 +635,16 @@ def parse(argv):
|
|
|
611
635
|
timeout.isdigit() and 1 <= int(timeout) <= 7200
|
|
612
636
|
):
|
|
613
637
|
raise ValueError("--cloud-timeout must be 1..7200 minutes")
|
|
638
|
+
branch = cloud.get("--cloud-ref")
|
|
639
|
+
if branch is not None and (
|
|
640
|
+
not BRANCH_NAME.fullmatch(branch)
|
|
641
|
+
or branch.startswith(PREFIX)
|
|
642
|
+
or ".." in branch
|
|
643
|
+
or branch.endswith((".", "/", ".lock"))
|
|
644
|
+
):
|
|
645
|
+
raise ValueError(
|
|
646
|
+
f"--cloud-ref takes a branch name such as dev, other than a {PREFIX} snapshot branch"
|
|
647
|
+
)
|
|
614
648
|
operation = "submit"
|
|
615
649
|
execution_id = cloud.get("--cloud-request-id") or str(uuid.uuid4())
|
|
616
650
|
if not UUID.fullmatch(execution_id):
|
|
@@ -797,6 +831,8 @@ def execution_lock(directory, execution_id):
|
|
|
797
831
|
|
|
798
832
|
|
|
799
833
|
def find_run(record):
|
|
834
|
+
if record.get("snapshot") is False:
|
|
835
|
+
return find_branch_run(record)
|
|
800
836
|
query = urlencode(
|
|
801
837
|
{"event": "workflow_dispatch", "branch": record["branch"], "per_page": 100}
|
|
802
838
|
)
|
|
@@ -815,6 +851,31 @@ def find_run(record):
|
|
|
815
851
|
return matches[0] if matches else None
|
|
816
852
|
|
|
817
853
|
|
|
854
|
+
def find_branch_run(record):
|
|
855
|
+
query = urlencode(
|
|
856
|
+
{
|
|
857
|
+
"event": "workflow_dispatch",
|
|
858
|
+
"branch": record["branch"],
|
|
859
|
+
"created": ">=" + record["dispatchedAt"],
|
|
860
|
+
"per_page": 100,
|
|
861
|
+
}
|
|
862
|
+
)
|
|
863
|
+
runs = api(
|
|
864
|
+
record["repository"], f"actions/workflows/{record['workflow']}/runs?{query}"
|
|
865
|
+
)
|
|
866
|
+
matches = [
|
|
867
|
+
run
|
|
868
|
+
for run in runs["workflow_runs"]
|
|
869
|
+
if run["head_branch"] == record["branch"]
|
|
870
|
+
and run.get("created_at", "") >= record["dispatchedAt"]
|
|
871
|
+
]
|
|
872
|
+
if len(matches) > 1:
|
|
873
|
+
raise ValueError(
|
|
874
|
+
f"Several runs were dispatched on {record['branch']} at about the same time, so this execution cannot tell which is its own; find it in the Actions tab"
|
|
875
|
+
)
|
|
876
|
+
return matches[0] if matches else None
|
|
877
|
+
|
|
878
|
+
|
|
818
879
|
def status(record, *, fetch_result=True):
|
|
819
880
|
if record["state"] == "prepared":
|
|
820
881
|
return record
|
|
@@ -826,6 +887,8 @@ def status(record, *, fetch_result=True):
|
|
|
826
887
|
if run is None:
|
|
827
888
|
record["state"] = "dispatch_unknown"
|
|
828
889
|
return record
|
|
890
|
+
if record.get("snapshot") is False and run["head_branch"] == record["branch"]:
|
|
891
|
+
record["sha"] = run["head_sha"]
|
|
829
892
|
if run["head_sha"] != record["sha"] or run["head_branch"] != record["branch"]:
|
|
830
893
|
raise ValueError("GitHub run does not match the recorded execution")
|
|
831
894
|
record.update(
|
|
@@ -955,6 +1018,9 @@ def print_outcome(found):
|
|
|
955
1018
|
def cleanup(root, record):
|
|
956
1019
|
if record.get("cleaned"):
|
|
957
1020
|
return
|
|
1021
|
+
if record.get("snapshot") is False:
|
|
1022
|
+
record["cleaned"] = True
|
|
1023
|
+
return
|
|
958
1024
|
if record.get("state") not in ("completed", "prepared"):
|
|
959
1025
|
raise ValueError(
|
|
960
1026
|
"Cleanup requires a confirmed completed GitHub run; cancel and wait first"
|
|
@@ -983,7 +1049,7 @@ def http_status(error):
|
|
|
983
1049
|
return f" (HTTP {status})" if status else ""
|
|
984
1050
|
|
|
985
1051
|
|
|
986
|
-
def preflight(repo, workflow):
|
|
1052
|
+
def preflight(repo, workflow, *, pushes=True):
|
|
987
1053
|
access = github_access()
|
|
988
1054
|
try:
|
|
989
1055
|
details = api(repo, "")
|
|
@@ -991,7 +1057,7 @@ def preflight(repo, workflow):
|
|
|
991
1057
|
raise ValueError(
|
|
992
1058
|
f"GitHub access from {access['source']} cannot read {repo}{http_status(error)}; use an account or token with write access to it"
|
|
993
1059
|
) from error
|
|
994
|
-
if (details or {}).get("permissions", {}).get("push") is False:
|
|
1060
|
+
if pushes and (details or {}).get("permissions", {}).get("push") is False:
|
|
995
1061
|
raise ValueError(
|
|
996
1062
|
f"GitHub access from {access['source']} cannot push to {repo}; use an account or token with write access to it"
|
|
997
1063
|
)
|
|
@@ -1072,21 +1138,43 @@ def submit(root, repo, workflow, parsed, path):
|
|
|
1072
1138
|
) from error
|
|
1073
1139
|
record["state"] = "dispatch_unknown"
|
|
1074
1140
|
save(path, record)
|
|
1075
|
-
|
|
1076
|
-
if DECLARED_INPUTS.search(
|
|
1141
|
+
declared = declared_inputs(
|
|
1077
1142
|
within(root, f"{WORKFLOW_DIRECTORY}/{workflow}").read_text()
|
|
1078
|
-
)
|
|
1079
|
-
|
|
1143
|
+
)
|
|
1144
|
+
return dispatch(record, {"execution_id": execution_id, "request": "-"}, declared)
|
|
1145
|
+
|
|
1146
|
+
|
|
1147
|
+
def declared_inputs(text):
|
|
1148
|
+
return {
|
|
1149
|
+
name
|
|
1150
|
+
for name in DISPATCH_INPUTS
|
|
1151
|
+
if re.search(rf"^[ \t]*\"?{name}\"?[ \t]*:", text, re.MULTILINE)
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
|
|
1155
|
+
def dispatch(record, inputs, declared, *, run_details=False):
|
|
1156
|
+
payload = {"ref": record["branch"]}
|
|
1157
|
+
sent = {name: value for name, value in inputs.items() if name in declared}
|
|
1158
|
+
if sent:
|
|
1159
|
+
payload["inputs"] = sent
|
|
1160
|
+
path = f"actions/workflows/{record['workflow']}/dispatches"
|
|
1080
1161
|
try:
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1162
|
+
try:
|
|
1163
|
+
response = api(
|
|
1164
|
+
record["repository"],
|
|
1165
|
+
path,
|
|
1166
|
+
method="POST",
|
|
1167
|
+
payload={**payload, "return_run_details": True}
|
|
1168
|
+
if run_details
|
|
1169
|
+
else payload,
|
|
1170
|
+
)
|
|
1171
|
+
except RuntimeError as error:
|
|
1172
|
+
if not run_details or getattr(error, "http_status", None) != 422:
|
|
1173
|
+
raise
|
|
1174
|
+
response = api(record["repository"], path, method="POST", payload=payload)
|
|
1087
1175
|
except RuntimeError as error:
|
|
1088
1176
|
raise ValueError(
|
|
1089
|
-
f"Dispatching {workflow} on {record['branch']} failed{http_status(error)}; the GitHub account needs write access to Actions, and the registered workflow must accept workflow_dispatch. Check the Actions tab, then resume with --cloud-watch {
|
|
1177
|
+
f"Dispatching {record['workflow']} on {record['branch']} failed{http_status(error)}; the GitHub account needs write access to Actions, and the registered workflow must accept workflow_dispatch. Check the Actions tab, then resume with --cloud-watch {record['id']}"
|
|
1090
1178
|
) from error
|
|
1091
1179
|
if isinstance(response, dict) and response.get("workflow_run_id"):
|
|
1092
1180
|
record["runId"] = response["workflow_run_id"]
|
|
@@ -1095,6 +1183,84 @@ def submit(root, repo, workflow, parsed, path):
|
|
|
1095
1183
|
return record
|
|
1096
1184
|
|
|
1097
1185
|
|
|
1186
|
+
def branch_head(repo, branch):
|
|
1187
|
+
try:
|
|
1188
|
+
found = api(repo, f"git/ref/heads/{branch}")
|
|
1189
|
+
except RuntimeError as error:
|
|
1190
|
+
if getattr(error, "http_status", None) == 404:
|
|
1191
|
+
raise ValueError(f"{repo} has no branch {branch}") from error
|
|
1192
|
+
raise
|
|
1193
|
+
sha = ((found or {}).get("object") or {}).get("sha", "")
|
|
1194
|
+
if not SHA.fullmatch(sha):
|
|
1195
|
+
raise ValueError(f"GitHub gave no commit for branch {branch}")
|
|
1196
|
+
return sha
|
|
1197
|
+
|
|
1198
|
+
|
|
1199
|
+
def branch_workflow(repo, workflow, sha):
|
|
1200
|
+
path = f"{WORKFLOW_DIRECTORY}/{workflow}"
|
|
1201
|
+
try:
|
|
1202
|
+
found = api(repo, f"contents/{path}?ref={sha}")
|
|
1203
|
+
except RuntimeError as error:
|
|
1204
|
+
raise ValueError(
|
|
1205
|
+
f"Could not read {path} at {sha[:12]}{http_status(error)}; the branch needs the workflow"
|
|
1206
|
+
) from error
|
|
1207
|
+
return base64.b64decode((found or {}).get("content", "")).decode(errors="replace")
|
|
1208
|
+
|
|
1209
|
+
|
|
1210
|
+
def submit_as_is(root, repo, workflow, parsed, path):
|
|
1211
|
+
execution_id = parsed["id"]
|
|
1212
|
+
branch = parsed["cloud"]["--cloud-ref"]
|
|
1213
|
+
request, _ = replay_request(root, parsed)
|
|
1214
|
+
sha = branch_head(repo, branch)
|
|
1215
|
+
if "--cloud-preview" in parsed["switches"]:
|
|
1216
|
+
return {
|
|
1217
|
+
"preview": True,
|
|
1218
|
+
"repository": repo,
|
|
1219
|
+
"branch": branch,
|
|
1220
|
+
"sha": sha,
|
|
1221
|
+
"request": request,
|
|
1222
|
+
}
|
|
1223
|
+
if path.exists():
|
|
1224
|
+
record = json.loads(path.read_text())
|
|
1225
|
+
if (
|
|
1226
|
+
record["request"] != request
|
|
1227
|
+
or record["repository"].lower() != repo.lower()
|
|
1228
|
+
or record["branch"] != branch
|
|
1229
|
+
):
|
|
1230
|
+
raise ValueError("Execution ID already belongs to a different request")
|
|
1231
|
+
status(record, fetch_result=False)
|
|
1232
|
+
return record
|
|
1233
|
+
preflight(repo, workflow, pushes=False)
|
|
1234
|
+
declared = declared_inputs(branch_workflow(repo, workflow, sha))
|
|
1235
|
+
if "request" not in declared:
|
|
1236
|
+
raise ValueError(
|
|
1237
|
+
f"{WORKFLOW_DIRECTORY}/{workflow} on {branch} declares no request input, so it cannot take a replay; add request (type: string, required: false) under on.workflow_dispatch.inputs and land it on {branch}"
|
|
1238
|
+
)
|
|
1239
|
+
record = {
|
|
1240
|
+
"id": execution_id,
|
|
1241
|
+
"repository": repo,
|
|
1242
|
+
"workflow": workflow,
|
|
1243
|
+
"branch": branch,
|
|
1244
|
+
"snapshot": False,
|
|
1245
|
+
"request": request,
|
|
1246
|
+
"state": "dispatch_unknown",
|
|
1247
|
+
"sha": sha,
|
|
1248
|
+
"dispatchedAt": time.strftime(
|
|
1249
|
+
"%Y-%m-%dT%H:%M:%SZ", time.gmtime(time.time() - 5)
|
|
1250
|
+
),
|
|
1251
|
+
}
|
|
1252
|
+
save(path, record)
|
|
1253
|
+
progress(
|
|
1254
|
+
f"Replaying {branch} at {sha[:12]} as it is on GitHub, without local changes. Execution {execution_id}; resume with --cloud-watch {execution_id}"
|
|
1255
|
+
)
|
|
1256
|
+
return dispatch(
|
|
1257
|
+
record,
|
|
1258
|
+
{"execution_id": execution_id, "request": json.dumps(request)},
|
|
1259
|
+
declared,
|
|
1260
|
+
run_details=True,
|
|
1261
|
+
)
|
|
1262
|
+
|
|
1263
|
+
|
|
1098
1264
|
def report_steps(record, shown):
|
|
1099
1265
|
job = replay_job(record)
|
|
1100
1266
|
for step in (job or {}).get("steps", []):
|
|
@@ -1140,7 +1306,8 @@ def run_cli(argv):
|
|
|
1140
1306
|
path = directory / f"{execution_id}.json"
|
|
1141
1307
|
with execution_lock(directory, execution_id):
|
|
1142
1308
|
if operation == "submit":
|
|
1143
|
-
|
|
1309
|
+
submitter = submit_as_is if "--cloud-ref" in parsed["cloud"] else submit
|
|
1310
|
+
record = submitter(root, repo, workflow, parsed, path)
|
|
1144
1311
|
if record.get("preview"):
|
|
1145
1312
|
return record, None
|
|
1146
1313
|
else:
|
|
@@ -1216,14 +1383,86 @@ def snapshot_request(root, commit):
|
|
|
1216
1383
|
return request
|
|
1217
1384
|
|
|
1218
1385
|
|
|
1386
|
+
def dispatched_inputs():
|
|
1387
|
+
path = os.environ.get("GITHUB_EVENT_PATH")
|
|
1388
|
+
if not path or not Path(path).is_file():
|
|
1389
|
+
return {}
|
|
1390
|
+
inputs = json.loads(Path(path).read_text()).get("inputs")
|
|
1391
|
+
return inputs if isinstance(inputs, dict) else {}
|
|
1392
|
+
|
|
1393
|
+
|
|
1394
|
+
def decode_request(text):
|
|
1395
|
+
try:
|
|
1396
|
+
return json.loads(text)
|
|
1397
|
+
except ValueError:
|
|
1398
|
+
pass
|
|
1399
|
+
try:
|
|
1400
|
+
return json.loads(base64.b64decode(text, validate=True))
|
|
1401
|
+
except ValueError as error:
|
|
1402
|
+
raise ValueError(
|
|
1403
|
+
"The request input is neither JSON nor base64-encoded JSON"
|
|
1404
|
+
) from error
|
|
1405
|
+
|
|
1406
|
+
|
|
1407
|
+
def dispatched_request():
|
|
1408
|
+
branch = os.environ.get("GITHUB_REF_NAME", "")
|
|
1409
|
+
inputs = dispatched_inputs()
|
|
1410
|
+
text = str(inputs.get("request") or "").strip()
|
|
1411
|
+
if text in ("", "-"):
|
|
1412
|
+
raise ValueError(
|
|
1413
|
+
f"A replay of {branch} takes its request from the workflow_dispatch request input, and this run has none; start replays with bitfab-replay --cloud, or --cloud --cloud-ref {branch} to replay the branch as it is"
|
|
1414
|
+
)
|
|
1415
|
+
request = decode_request(text)
|
|
1416
|
+
if not isinstance(request, dict):
|
|
1417
|
+
raise ValueError("The request input must be a JSON object")
|
|
1418
|
+
version = request.get("version", REQUEST_VERSION)
|
|
1419
|
+
if not isinstance(version, int) or version < OLDEST_DISPATCHED_VERSION:
|
|
1420
|
+
raise ValueError(
|
|
1421
|
+
f"Request version {version!r} is not supported; send version {REQUEST_VERSION}, or leave version out"
|
|
1422
|
+
)
|
|
1423
|
+
if version > REQUEST_VERSION:
|
|
1424
|
+
raise ValueError(
|
|
1425
|
+
f"This replay was submitted by a newer SDK than the one {branch} installs; the request is version {version} and this SDK reads up to {REQUEST_VERSION}"
|
|
1426
|
+
)
|
|
1427
|
+
execution_id = request.get("id") or inputs.get("execution_id") or str(uuid.uuid4())
|
|
1428
|
+
if not isinstance(execution_id, str) or not UUID.fullmatch(execution_id):
|
|
1429
|
+
raise ValueError("The request id must be a UUID")
|
|
1430
|
+
if inputs.get("execution_id") and inputs["execution_id"] != execution_id:
|
|
1431
|
+
raise ValueError("The request id does not match the execution_id input")
|
|
1432
|
+
args = request.get("args")
|
|
1433
|
+
validate_arguments(args)
|
|
1434
|
+
if request.get("check") is True and "--dry-run" not in args:
|
|
1435
|
+
args = [*args, "--dry-run"]
|
|
1436
|
+
normalized = {
|
|
1437
|
+
"version": REQUEST_VERSION,
|
|
1438
|
+
"id": execution_id,
|
|
1439
|
+
"cwd": request.get("cwd") or ".",
|
|
1440
|
+
"args": args,
|
|
1441
|
+
}
|
|
1442
|
+
timeout = request.get("timeoutMinutes")
|
|
1443
|
+
if timeout is not None:
|
|
1444
|
+
if not isinstance(timeout, int) or not 1 <= timeout <= 7200:
|
|
1445
|
+
raise ValueError("timeoutMinutes must be 1..7200")
|
|
1446
|
+
normalized["timeoutMinutes"] = timeout
|
|
1447
|
+
if request.get("fullOutput") is True:
|
|
1448
|
+
normalized["fullOutput"] = True
|
|
1449
|
+
return normalized
|
|
1450
|
+
|
|
1451
|
+
|
|
1452
|
+
def runner_request(root, commit):
|
|
1453
|
+
if os.environ.get("GITHUB_REF_NAME", "").startswith(PREFIX):
|
|
1454
|
+
return snapshot_request(root, commit)
|
|
1455
|
+
return dispatched_request()
|
|
1456
|
+
|
|
1457
|
+
|
|
1219
1458
|
def execute():
|
|
1220
1459
|
if os.environ.get("GITHUB_RUN_ATTEMPT") != "1":
|
|
1221
1460
|
raise ValueError("Submit a new replay instead of rerunning an Actions job")
|
|
1222
1461
|
root = root_directory()
|
|
1223
1462
|
commit = os.environ["GITHUB_SHA"]
|
|
1224
1463
|
if git(root, "rev-parse", "HEAD") != commit:
|
|
1225
|
-
raise ValueError("Runner checkout does not match the dispatched
|
|
1226
|
-
request =
|
|
1464
|
+
raise ValueError("Runner checkout does not match the dispatched commit")
|
|
1465
|
+
request = runner_request(root, commit)
|
|
1227
1466
|
directory = within(root, request["cwd"])
|
|
1228
1467
|
check_secrets(root)
|
|
1229
1468
|
names = {option_name(value) for value in request["args"]}
|
|
@@ -1828,7 +2067,11 @@ def workflow_document(steps, run, directory, env, *, runs_on, environment):
|
|
|
1828
2067
|
]
|
|
1829
2068
|
return {
|
|
1830
2069
|
"name": "Bitfab replay",
|
|
1831
|
-
"on":
|
|
2070
|
+
"on": {
|
|
2071
|
+
"workflow_dispatch": {
|
|
2072
|
+
"inputs": {"request": {"type": "string", "required": False}}
|
|
2073
|
+
}
|
|
2074
|
+
},
|
|
1832
2075
|
"permissions": {"contents": "read"},
|
|
1833
2076
|
"jobs": {"replay": job},
|
|
1834
2077
|
}
|
|
@@ -181,7 +181,7 @@ function encodeRequestBody(body) {
|
|
|
181
181
|
}
|
|
182
182
|
|
|
183
183
|
// src/version.generated.ts
|
|
184
|
-
var __version__ = "0.64.
|
|
184
|
+
var __version__ = "0.64.5";
|
|
185
185
|
var __packageName__ = "bitfab";
|
|
186
186
|
|
|
187
187
|
// src/errors.ts
|
|
@@ -2500,4 +2500,4 @@ export {
|
|
|
2500
2500
|
HttpClient,
|
|
2501
2501
|
experimentIdWireFields
|
|
2502
2502
|
};
|
|
2503
|
-
//# sourceMappingURL=chunk-
|
|
2503
|
+
//# sourceMappingURL=chunk-4IRNG6KB.js.map
|