@bitfab/sdk 0.64.16 → 0.64.18
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 +286 -24
- package/dist/{chunk-PAWBCEI7.js → chunk-4JZ6UMDW.js} +2 -2
- package/dist/{chunk-PAWBCEI7.js.map → chunk-4JZ6UMDW.js.map} +1 -1
- package/dist/{chunk-LOIVELPI.js → chunk-6AUI3X66.js} +204 -89
- package/dist/chunk-6AUI3X66.js.map +1 -0
- package/dist/{chunk-I4TFQZCW.js → chunk-7NPHFRQD.js} +16 -2
- package/dist/{chunk-I4TFQZCW.js.map → chunk-7NPHFRQD.js.map} +1 -1
- package/dist/{chunk-NNZAQSTW.js → chunk-ACKEEC7I.js} +2 -2
- package/dist/chunk-NGKTKRCV.js +1905 -0
- package/dist/chunk-NGKTKRCV.js.map +1 -0
- package/dist/{chunk-AXFKZYIY.js → chunk-ZBA4QJWF.js} +11 -6
- package/dist/chunk-ZBA4QJWF.js.map +1 -0
- package/dist/{http-QQ6VVHUZ.js → http-2BXJLZ5N.js} +2 -2
- package/dist/{http-LV7OUBKA.js → http-6VZBSALH.js} +2 -2
- package/dist/index.cjs +6 -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 +6 -1
- package/dist/node.cjs.map +1 -1
- package/dist/node.js +3 -3
- package/dist/{replay-JNKXZPY7.js → replay-OO2DFUAQ.js} +3 -3
- package/dist/replay-Z46UJQVQ.js +28 -0
- package/dist/replay-Z46UJQVQ.js.map +1 -0
- package/dist/replayCli.js +3 -2
- package/dist/replayCli.js.map +1 -1
- package/dist/seedCli.js +3 -2
- package/dist/seedCli.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-AXFKZYIY.js.map +0 -1
- package/dist/chunk-LOIVELPI.js.map +0 -1
- /package/dist/{chunk-NNZAQSTW.js.map → chunk-ACKEEC7I.js.map} +0 -0
- /package/dist/{http-LV7OUBKA.js.map → http-2BXJLZ5N.js.map} +0 -0
- /package/dist/{http-QQ6VVHUZ.js.map → http-6VZBSALH.js.map} +0 -0
- /package/dist/{replay-JNKXZPY7.js.map → replay-OO2DFUAQ.js.map} +0 -0
package/cloudReplay.py
CHANGED
|
@@ -36,6 +36,16 @@ SDK_LANGUAGE_ENV = "BITFAB_SDK_LANGUAGE"
|
|
|
36
36
|
CHECK_COMMAND_ENV = "BITFAB_REPLAY_CHECK"
|
|
37
37
|
EXECUTION_TARGET_ENV = "BITFAB_REPLAY_EXECUTION_TARGET"
|
|
38
38
|
HOSTED_EXECUTION_TARGET = "hosted"
|
|
39
|
+
API_KEY_ENV = "BITFAB_REPLAY_BITFAB_API_KEY"
|
|
40
|
+
PRINT_API_KEY_FLAG = "--print-api-key"
|
|
41
|
+
API_KEY_LINE = "@@bitfab:api-key "
|
|
42
|
+
KEY_INPUT = "key_id"
|
|
43
|
+
KEY_SECRET_PREFIX = "BITFAB_REPLAY_KEY_"
|
|
44
|
+
KEY_SECRET_REFERENCE = (
|
|
45
|
+
"${{ secrets[format('" + KEY_SECRET_PREFIX + "{0}', inputs." + KEY_INPUT + ")] }}"
|
|
46
|
+
)
|
|
47
|
+
API_KEY_PROBE_SECONDS = 120
|
|
48
|
+
RUN_NAME = "Bitfab replay ${{ inputs.execution_id || github.ref_name }}"
|
|
39
49
|
REQUEST_VERSION = 4
|
|
40
50
|
PLAIN_REQUEST_VERSION = 3
|
|
41
51
|
OLDEST_DISPATCHED_VERSION = 2
|
|
@@ -95,7 +105,7 @@ ENVIRONMENT_SETTING = re.compile(
|
|
|
95
105
|
PUSH_TRIGGER = re.compile(
|
|
96
106
|
r"^[ \t]*\"?(?:on\"?[ \t]*:.*\bpush\b|push\"?[ \t]*:)", re.MULTILINE
|
|
97
107
|
)
|
|
98
|
-
DISPATCH_INPUTS = ("execution_id", "request")
|
|
108
|
+
DISPATCH_INPUTS = ("execution_id", "request", KEY_INPUT)
|
|
99
109
|
BRANCH_NAME = re.compile(r"[A-Za-z0-9][A-Za-z0-9._/-]*")
|
|
100
110
|
ENV_NAME = re.compile(r"[A-Za-z_][A-Za-z0-9_]*")
|
|
101
111
|
RESERVED_ENV_PREFIXES = ("GITHUB_", "RUNNER_", "ACTIONS_", "BITFAB_REPLAY_")
|
|
@@ -204,6 +214,14 @@ values --cloud-env sets. version and id are optional: version names the request
|
|
|
204
214
|
format (4 when env is set, otherwise 3; 2 is still accepted), and id is the
|
|
205
215
|
execution UUID, which must match an execution_id input when the workflow has one.
|
|
206
216
|
|
|
217
|
+
The replay runs with the Bitfab API key it would use on your machine, so the
|
|
218
|
+
experiment is recorded as yours. Before dispatch, the replay's registry is loaded
|
|
219
|
+
locally to read its client's key, which the GitHub CLI stores as a GitHub secret
|
|
220
|
+
named for this execution and deletes once the replay starts. Without the GitHub
|
|
221
|
+
CLI, without permission to manage the repository's Actions secrets, with no key
|
|
222
|
+
resolved locally, or with a workflow that declares no key_id input, the run uses
|
|
223
|
+
the workflow's BITFAB_API_KEY secret instead.
|
|
224
|
+
|
|
207
225
|
The runner's job exits with the replay's exit code, so the Actions run fails when
|
|
208
226
|
the replay does. Traces that errored fail it only under --fail-on-error.
|
|
209
227
|
|
|
@@ -216,7 +234,10 @@ Set up once:
|
|
|
216
234
|
--cloud-init [--secret NAME ...] [--environment NAME] [--run COMMAND]
|
|
217
235
|
[--runs-on LABEL] [--secret-prefix PREFIX] [--check COMMAND]
|
|
218
236
|
Writes .github/workflows/bitfab-replay.yml, the only file cloud replay keeps
|
|
219
|
-
in the repository, and lists what is left to do.
|
|
237
|
+
in the repository, and lists what is left to do. On an existing workflow it
|
|
238
|
+
adds only the lines a newer SDK needs and lists what it changed.
|
|
239
|
+
--cloud-update [same options]
|
|
240
|
+
The same command as --cloud-init, for bringing an existing setup up to date.
|
|
220
241
|
--cloud-secrets --env-file FILE [NAME ...] [--environment NAME] [--dry-run]
|
|
221
242
|
Copies local values into the GitHub secrets the workflow reads.
|
|
222
243
|
|
|
@@ -1189,6 +1210,7 @@ def print_outcome(found):
|
|
|
1189
1210
|
|
|
1190
1211
|
|
|
1191
1212
|
def cleanup(root, record):
|
|
1213
|
+
remove_key(record)
|
|
1192
1214
|
if record.get("cleaned"):
|
|
1193
1215
|
return
|
|
1194
1216
|
if record.get("snapshot") is False:
|
|
@@ -1335,10 +1357,110 @@ def submit(root, repo, workflow, parsed, path):
|
|
|
1335
1357
|
) from error
|
|
1336
1358
|
record["state"] = "dispatch_unknown"
|
|
1337
1359
|
save(path, record)
|
|
1338
|
-
|
|
1339
|
-
|
|
1360
|
+
text = within(root, f"{WORKFLOW_DIRECTORY}/{workflow}").read_text()
|
|
1361
|
+
declared = declared_inputs(text)
|
|
1362
|
+
key_inputs = store_key(root, record, declared, text)
|
|
1363
|
+
save(path, record)
|
|
1364
|
+
return dispatch(
|
|
1365
|
+
record,
|
|
1366
|
+
{"execution_id": execution_id, "request": "-", **key_inputs},
|
|
1367
|
+
declared,
|
|
1368
|
+
)
|
|
1369
|
+
|
|
1370
|
+
|
|
1371
|
+
def local_api_key(root, request):
|
|
1372
|
+
try:
|
|
1373
|
+
output = command(
|
|
1374
|
+
[*replay_command(), *request["args"], PRINT_API_KEY_FLAG],
|
|
1375
|
+
cwd=within(root, request["cwd"]),
|
|
1376
|
+
timeout=API_KEY_PROBE_SECONDS,
|
|
1377
|
+
input="",
|
|
1378
|
+
)
|
|
1379
|
+
except (ValueError, RuntimeError, OSError, subprocess.SubprocessError):
|
|
1380
|
+
return None
|
|
1381
|
+
for line in reversed(output.splitlines()):
|
|
1382
|
+
if not line.startswith(API_KEY_LINE):
|
|
1383
|
+
continue
|
|
1384
|
+
try:
|
|
1385
|
+
key = json.loads(line.removeprefix(API_KEY_LINE)).get("apiKey")
|
|
1386
|
+
except (ValueError, AttributeError):
|
|
1387
|
+
return None
|
|
1388
|
+
return key if isinstance(key, str) and key.strip() else None
|
|
1389
|
+
return None
|
|
1390
|
+
|
|
1391
|
+
|
|
1392
|
+
def store_key(root, record, declared, workflow_text):
|
|
1393
|
+
shared = "so the run uses the workflow's BITFAB_API_KEY secret"
|
|
1394
|
+
if KEY_INPUT not in declared:
|
|
1395
|
+
progress(
|
|
1396
|
+
f"The workflow takes no {KEY_INPUT} input, {shared}. To replay with your own Bitfab API key, run bitfab-replay --cloud-init, which adds the {KEY_INPUT} input and {API_KEY_ENV} to the workflow"
|
|
1397
|
+
)
|
|
1398
|
+
return {}
|
|
1399
|
+
if shutil.which("gh") is None:
|
|
1400
|
+
progress(
|
|
1401
|
+
f"Replaying with your own Bitfab API key needs the GitHub CLI (https://cli.github.com) to store it as a GitHub secret, {shared}"
|
|
1402
|
+
)
|
|
1403
|
+
return {}
|
|
1404
|
+
key = local_api_key(root, record["request"])
|
|
1405
|
+
if key is None:
|
|
1406
|
+
progress(
|
|
1407
|
+
f"The replay's Bitfab client resolved no API key on this machine, {shared}"
|
|
1408
|
+
)
|
|
1409
|
+
return {}
|
|
1410
|
+
key_id = record["id"].replace("-", "").upper()
|
|
1411
|
+
name = KEY_SECRET_PREFIX + key_id
|
|
1412
|
+
configured = ENVIRONMENT_SETTING.search(workflow_text)
|
|
1413
|
+
environment = configured[1] if configured else None
|
|
1414
|
+
try:
|
|
1415
|
+
command(
|
|
1416
|
+
[
|
|
1417
|
+
"gh",
|
|
1418
|
+
"secret",
|
|
1419
|
+
"set",
|
|
1420
|
+
name,
|
|
1421
|
+
"--repo",
|
|
1422
|
+
record["repository"],
|
|
1423
|
+
*(["--env", environment] if environment else []),
|
|
1424
|
+
],
|
|
1425
|
+
input=key,
|
|
1426
|
+
env={**os.environ, "GH_TOKEN": github_access()["token"]},
|
|
1427
|
+
)
|
|
1428
|
+
except (ValueError, RuntimeError, OSError, subprocess.SubprocessError):
|
|
1429
|
+
progress(
|
|
1430
|
+
f"Could not store your Bitfab API key as the GitHub secret {name}, which needs permission to manage the repository's Actions secrets, {shared}"
|
|
1431
|
+
)
|
|
1432
|
+
return {}
|
|
1433
|
+
record["keySecret"] = {"name": name, "environment": environment}
|
|
1434
|
+
progress(
|
|
1435
|
+
f"Replaying with the Bitfab API key from this machine, stored as the GitHub secret {name} until the replay starts"
|
|
1340
1436
|
)
|
|
1341
|
-
return
|
|
1437
|
+
return {KEY_INPUT: key_id}
|
|
1438
|
+
|
|
1439
|
+
|
|
1440
|
+
def remove_key(record):
|
|
1441
|
+
secret = record.get("keySecret")
|
|
1442
|
+
if not secret or secret.get("removed"):
|
|
1443
|
+
return
|
|
1444
|
+
try:
|
|
1445
|
+
command(
|
|
1446
|
+
[
|
|
1447
|
+
"gh",
|
|
1448
|
+
"secret",
|
|
1449
|
+
"delete",
|
|
1450
|
+
secret["name"],
|
|
1451
|
+
"--repo",
|
|
1452
|
+
record["repository"],
|
|
1453
|
+
*(["--env", secret["environment"]] if secret["environment"] else []),
|
|
1454
|
+
],
|
|
1455
|
+
env={**os.environ, "GH_TOKEN": github_access()["token"]},
|
|
1456
|
+
)
|
|
1457
|
+
except (ValueError, RuntimeError, OSError, subprocess.SubprocessError) as error:
|
|
1458
|
+
if getattr(error, "http_status", None) != 404:
|
|
1459
|
+
progress(
|
|
1460
|
+
f"Could not delete the GitHub secret {secret['name']}; delete it at {secrets_page(record['repository'], secret['environment'])}"
|
|
1461
|
+
)
|
|
1462
|
+
return
|
|
1463
|
+
secret["removed"] = True
|
|
1342
1464
|
|
|
1343
1465
|
|
|
1344
1466
|
def declared_inputs(text):
|
|
@@ -1446,7 +1568,8 @@ def submit_as_is(root, repo, workflow, parsed, path):
|
|
|
1446
1568
|
preflight(
|
|
1447
1569
|
repo, workflow, pushes=False, full_output=request.get("fullOutput") is True
|
|
1448
1570
|
)
|
|
1449
|
-
|
|
1571
|
+
text = branch_workflow(repo, workflow, sha)
|
|
1572
|
+
declared = declared_inputs(text)
|
|
1450
1573
|
if "request" not in declared:
|
|
1451
1574
|
raise ValueError(
|
|
1452
1575
|
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}"
|
|
@@ -1466,9 +1589,11 @@ def submit_as_is(root, repo, workflow, parsed, path):
|
|
|
1466
1589
|
progress(
|
|
1467
1590
|
f"Replaying {branch} at {sha[:12]} as it is on GitHub, without local changes. Execution {execution_id}; resume with --cloud-watch {execution_id}"
|
|
1468
1591
|
)
|
|
1592
|
+
key_inputs = store_key(root, record, declared, text)
|
|
1593
|
+
save(path, record)
|
|
1469
1594
|
return dispatch(
|
|
1470
1595
|
record,
|
|
1471
|
-
{"execution_id": execution_id, "request": json.dumps(request)},
|
|
1596
|
+
{"execution_id": execution_id, "request": json.dumps(request), **key_inputs},
|
|
1472
1597
|
declared,
|
|
1473
1598
|
run_details=True,
|
|
1474
1599
|
)
|
|
@@ -1477,10 +1602,11 @@ def submit_as_is(root, repo, workflow, parsed, path):
|
|
|
1477
1602
|
def report_steps(record, shown):
|
|
1478
1603
|
job = replay_job(record)
|
|
1479
1604
|
for step in (job or {}).get("steps", []):
|
|
1480
|
-
if (
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1605
|
+
if step.get("status") not in ("in_progress", "completed"):
|
|
1606
|
+
continue
|
|
1607
|
+
if "--cloud-execute" in (step.get("name") or ""):
|
|
1608
|
+
remove_key(record)
|
|
1609
|
+
if step.get("name") not in shown:
|
|
1484
1610
|
shown.add(step.get("name"))
|
|
1485
1611
|
progress(step.get("name"))
|
|
1486
1612
|
|
|
@@ -1701,6 +1827,11 @@ def execute():
|
|
|
1701
1827
|
request = runner_request(root, commit)
|
|
1702
1828
|
directory = within(root, request["cwd"])
|
|
1703
1829
|
check_secrets(root)
|
|
1830
|
+
if os.environ.get(API_KEY_ENV, "").strip():
|
|
1831
|
+
print(
|
|
1832
|
+
"Replaying with the Bitfab API key of the person who started this run",
|
|
1833
|
+
flush=True,
|
|
1834
|
+
)
|
|
1704
1835
|
environment = request.get("env") or {}
|
|
1705
1836
|
if environment:
|
|
1706
1837
|
workflow_environment_conflicts(root, environment)
|
|
@@ -2376,12 +2507,13 @@ def workflow_document(steps, run, directory, env, *, runs_on, environment):
|
|
|
2376
2507
|
]
|
|
2377
2508
|
return {
|
|
2378
2509
|
"name": "Bitfab replay",
|
|
2379
|
-
"run-name":
|
|
2510
|
+
"run-name": RUN_NAME,
|
|
2380
2511
|
"on": {
|
|
2381
2512
|
"workflow_dispatch": {
|
|
2382
2513
|
"inputs": {
|
|
2383
2514
|
"execution_id": {"type": "string", "required": False},
|
|
2384
2515
|
"request": {"type": "string", "required": False},
|
|
2516
|
+
KEY_INPUT: {"type": "string", "required": False},
|
|
2385
2517
|
}
|
|
2386
2518
|
}
|
|
2387
2519
|
},
|
|
@@ -2390,10 +2522,145 @@ def workflow_document(steps, run, directory, env, *, runs_on, environment):
|
|
|
2390
2522
|
}
|
|
2391
2523
|
|
|
2392
2524
|
|
|
2393
|
-
def
|
|
2525
|
+
def indentation(line):
|
|
2526
|
+
return len(line) - len(line.lstrip(" \t"))
|
|
2527
|
+
|
|
2528
|
+
|
|
2529
|
+
def block_end(lines, start, indent):
|
|
2530
|
+
end = start + 1
|
|
2531
|
+
while end < len(lines) and (
|
|
2532
|
+
not lines[end].strip() or indentation(lines[end]) > indent
|
|
2533
|
+
):
|
|
2534
|
+
end += 1
|
|
2535
|
+
while end > start + 1 and not lines[end - 1].strip():
|
|
2536
|
+
end -= 1
|
|
2537
|
+
return end
|
|
2538
|
+
|
|
2539
|
+
|
|
2540
|
+
def add_input(lines, name):
|
|
2541
|
+
for index, line in enumerate(lines):
|
|
2542
|
+
if not re.fullmatch(r'[ \t]*"?request"?[ \t]*:[ \t]*', line):
|
|
2543
|
+
continue
|
|
2544
|
+
indent = indentation(line)
|
|
2545
|
+
child = " " * (
|
|
2546
|
+
indentation(lines[index + 1])
|
|
2547
|
+
if block_end(lines, index, indent) > index + 1
|
|
2548
|
+
else indent + 2
|
|
2549
|
+
)
|
|
2550
|
+
end = block_end(lines, index, indent - 1)
|
|
2551
|
+
lines[end:end] = [
|
|
2552
|
+
f"{line[:indent]}{name}:",
|
|
2553
|
+
f"{child}type: string",
|
|
2554
|
+
f"{child}required: false",
|
|
2555
|
+
]
|
|
2556
|
+
return True
|
|
2557
|
+
return False
|
|
2558
|
+
|
|
2559
|
+
|
|
2560
|
+
def add_run_name(lines):
|
|
2561
|
+
named = next(
|
|
2562
|
+
(
|
|
2563
|
+
index
|
|
2564
|
+
for index, line in enumerate(lines)
|
|
2565
|
+
if re.match(r'"?name"?[ \t]*:', line)
|
|
2566
|
+
),
|
|
2567
|
+
-1,
|
|
2568
|
+
)
|
|
2569
|
+
lines.insert(named + 1, f"run-name: {json.dumps(RUN_NAME)}")
|
|
2570
|
+
|
|
2571
|
+
|
|
2572
|
+
def without_yaml_comment(line):
|
|
2573
|
+
return re.sub(r"(^|\s)#.*", "", line)
|
|
2574
|
+
|
|
2575
|
+
|
|
2576
|
+
def replay_step_key(lines):
|
|
2577
|
+
for index, line in enumerate(lines):
|
|
2578
|
+
if "--cloud-execute" not in without_yaml_comment(line):
|
|
2579
|
+
continue
|
|
2580
|
+
for above in range(index, -1, -1):
|
|
2581
|
+
match = re.match(r"([ \t]*)(- +)?run[ \t]*:", lines[above])
|
|
2582
|
+
if match and indentation(lines[above]) <= indentation(line):
|
|
2583
|
+
return above, len(match[1]) + len(match[2] or "")
|
|
2584
|
+
return None
|
|
2585
|
+
|
|
2586
|
+
|
|
2587
|
+
def add_key_env(lines):
|
|
2588
|
+
found = replay_step_key(lines)
|
|
2589
|
+
if found is None:
|
|
2590
|
+
return False
|
|
2591
|
+
run, key = found
|
|
2592
|
+
start = run
|
|
2593
|
+
while start > 0 and not re.match(r"[ \t]*- ", lines[start]):
|
|
2594
|
+
start -= 1
|
|
2595
|
+
end = block_end(lines, run, key - 1)
|
|
2596
|
+
entry = f'{API_KEY_ENV}: "{KEY_SECRET_REFERENCE}"'
|
|
2597
|
+
for index in range(start, end):
|
|
2598
|
+
line = lines[index]
|
|
2599
|
+
if not re.fullmatch(r"[ \t]*(- +)?env[ \t]*:[ \t]*", line):
|
|
2600
|
+
continue
|
|
2601
|
+
if len(line) - len(line.lstrip(" \t-")) != key:
|
|
2602
|
+
continue
|
|
2603
|
+
entries = block_end(lines, index, key)
|
|
2604
|
+
child = indentation(lines[index + 1]) if entries > index + 1 else key + 2
|
|
2605
|
+
lines.insert(entries, " " * child + entry)
|
|
2606
|
+
return True
|
|
2607
|
+
lines[end:end] = [" " * key + "env:", " " * (key + 2) + entry]
|
|
2608
|
+
return True
|
|
2609
|
+
|
|
2610
|
+
|
|
2611
|
+
def update_workflow(root, path):
|
|
2612
|
+
target = within(root, path)
|
|
2613
|
+
text = target.read_text()
|
|
2614
|
+
lines = text.split("\n")
|
|
2615
|
+
updated, by_hand = [], []
|
|
2616
|
+
declared = declared_inputs(text)
|
|
2617
|
+
for name in ("execution_id", KEY_INPUT):
|
|
2618
|
+
if name in declared:
|
|
2619
|
+
continue
|
|
2620
|
+
if add_input(lines, name):
|
|
2621
|
+
updated.append(f"Added the {name} input")
|
|
2622
|
+
else:
|
|
2623
|
+
by_hand.append(
|
|
2624
|
+
f"{name} (type: string, required: false) under on.workflow_dispatch.inputs"
|
|
2625
|
+
)
|
|
2626
|
+
if not re.search(r'^"?run-name"?[ \t]*:', text, re.MULTILINE):
|
|
2627
|
+
add_run_name(lines)
|
|
2628
|
+
updated.append("Named each run after its execution")
|
|
2629
|
+
if API_KEY_ENV not in text:
|
|
2630
|
+
if add_key_env(lines):
|
|
2631
|
+
updated.append(f"Added {API_KEY_ENV} to the replay step's env")
|
|
2632
|
+
else:
|
|
2633
|
+
by_hand.append(
|
|
2634
|
+
f'{API_KEY_ENV}: "{KEY_SECRET_REFERENCE}" in the replay step\'s env'
|
|
2635
|
+
)
|
|
2636
|
+
if updated:
|
|
2637
|
+
target.write_text("\n".join(lines))
|
|
2638
|
+
steps = []
|
|
2639
|
+
if updated:
|
|
2640
|
+
steps.append(
|
|
2641
|
+
f"Commit {path}. A --cloud replay runs the workflow in its own snapshot, so the next one uses it; --cloud-ref replays need it on the branch they replay"
|
|
2642
|
+
)
|
|
2643
|
+
if by_hand:
|
|
2644
|
+
steps.append(
|
|
2645
|
+
"Cloud replay could not place these lines, so add them by hand: "
|
|
2646
|
+
+ "; ".join(by_hand)
|
|
2647
|
+
)
|
|
2648
|
+
if not steps:
|
|
2649
|
+
steps.append(
|
|
2650
|
+
"Already set up. Edit the workflow directly to change its install steps, secrets, runner, or Environment"
|
|
2651
|
+
)
|
|
2652
|
+
return {
|
|
2653
|
+
"files": [path] if updated else [],
|
|
2654
|
+
"workflow": path,
|
|
2655
|
+
"updated": updated,
|
|
2656
|
+
"next": steps,
|
|
2657
|
+
}
|
|
2658
|
+
|
|
2659
|
+
|
|
2660
|
+
def initialize(argv, name="--cloud-init"):
|
|
2394
2661
|
parser = argparse.ArgumentParser(
|
|
2395
|
-
prog="bitfab-replay
|
|
2396
|
-
description="Write .github/workflows/bitfab-replay.yml, the only file cloud replay keeps in the repository, and list what is left to do. It detects the runtime, package manager, install command, and replay command from the directory you run it in.
|
|
2662
|
+
prog=f"bitfab-replay {name}",
|
|
2663
|
+
description="Write .github/workflows/bitfab-replay.yml, the only file cloud replay keeps in the repository, and list what is left to do. It detects the runtime, package manager, install command, and replay command from the directory you run it in. On an existing workflow it only adds what a newer SDK needs (the execution_id and key_id inputs, the run name, and BITFAB_REPLAY_BITFAB_API_KEY) and lists what it changed; edit it directly for anything else.",
|
|
2397
2664
|
)
|
|
2398
2665
|
parser.add_argument(
|
|
2399
2666
|
"--secret",
|
|
@@ -2425,13 +2692,7 @@ def initialize(argv):
|
|
|
2425
2692
|
refuse_old_setup(root)
|
|
2426
2693
|
existing = find_workflow(root)
|
|
2427
2694
|
if existing is not None:
|
|
2428
|
-
return {
|
|
2429
|
-
"files": [],
|
|
2430
|
-
"workflow": f"{WORKFLOW_DIRECTORY}/{existing}",
|
|
2431
|
-
"next": [
|
|
2432
|
-
"Already set up. Edit the workflow directly to change its install steps, secrets, runner, or Environment"
|
|
2433
|
-
],
|
|
2434
|
-
}
|
|
2695
|
+
return update_workflow(root, f"{WORKFLOW_DIRECTORY}/{existing}")
|
|
2435
2696
|
prefix = args.secret_prefix
|
|
2436
2697
|
if prefix and not re.fullmatch(r"[A-Z][A-Z0-9_]*_", prefix):
|
|
2437
2698
|
raise ValueError(
|
|
@@ -2450,6 +2711,7 @@ def initialize(argv):
|
|
|
2450
2711
|
'Pass --run with the command that starts your registry program on the runner, such as --run "go run ./cmd/registry"'
|
|
2451
2712
|
)
|
|
2452
2713
|
env = {name: "${{ secrets." + prefix + name + " }}" for name in names}
|
|
2714
|
+
env[API_KEY_ENV] = KEY_SECRET_REFERENCE
|
|
2453
2715
|
if args.check:
|
|
2454
2716
|
env[CHECK_COMMAND_ENV] = args.check
|
|
2455
2717
|
path = f"{WORKFLOW_DIRECTORY}/{WORKFLOW}"
|
|
@@ -2506,8 +2768,8 @@ def print_json(value):
|
|
|
2506
2768
|
def main():
|
|
2507
2769
|
argv = sys.argv[1:]
|
|
2508
2770
|
try:
|
|
2509
|
-
if argv[:1]
|
|
2510
|
-
print_json(initialize(argv[1:]))
|
|
2771
|
+
if argv[:1] in (["--cloud-init"], ["--cloud-update"]):
|
|
2772
|
+
print_json(initialize(argv[1:], argv[0]))
|
|
2511
2773
|
return 0
|
|
2512
2774
|
if argv[:1] == ["--cloud-secrets"]:
|
|
2513
2775
|
print_json(configure_secrets(argv[1:]))
|
|
@@ -189,7 +189,7 @@ function encodeRequestBody(body) {
|
|
|
189
189
|
}
|
|
190
190
|
|
|
191
191
|
// src/version.generated.ts
|
|
192
|
-
var __version__ = "0.64.
|
|
192
|
+
var __version__ = "0.64.18";
|
|
193
193
|
var __packageName__ = "@bitfab/sdk";
|
|
194
194
|
|
|
195
195
|
// src/constants.ts
|
|
@@ -2959,4 +2959,4 @@ export {
|
|
|
2959
2959
|
HttpClient,
|
|
2960
2960
|
experimentIdWireFields
|
|
2961
2961
|
};
|
|
2962
|
-
//# sourceMappingURL=chunk-
|
|
2962
|
+
//# sourceMappingURL=chunk-4JZ6UMDW.js.map
|