flywheel-bootstrap 0.1.9.202601311405__py3-none-any.whl → 0.1.9.202602021053__py3-none-any.whl
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.
- bootstrap/git_ops.py +30 -2
- bootstrap/orchestrator.py +63 -12
- bootstrap/payload.py +17 -0
- {flywheel_bootstrap-0.1.9.202601311405.dist-info → flywheel_bootstrap-0.1.9.202602021053.dist-info}/METADATA +1 -1
- {flywheel_bootstrap-0.1.9.202601311405.dist-info → flywheel_bootstrap-0.1.9.202602021053.dist-info}/RECORD +7 -7
- {flywheel_bootstrap-0.1.9.202601311405.dist-info → flywheel_bootstrap-0.1.9.202602021053.dist-info}/WHEEL +0 -0
- {flywheel_bootstrap-0.1.9.202601311405.dist-info → flywheel_bootstrap-0.1.9.202602021053.dist-info}/entry_points.txt +0 -0
bootstrap/git_ops.py
CHANGED
|
@@ -122,6 +122,24 @@ def setup_git_credentials(config: GitConfig) -> bool:
|
|
|
122
122
|
return False
|
|
123
123
|
|
|
124
124
|
|
|
125
|
+
def update_git_auth(config: GitConfig, github_token: str) -> bool:
|
|
126
|
+
"""Update git auth token and remote URL before pushing."""
|
|
127
|
+
config.github_token = github_token
|
|
128
|
+
repo_url = config.repo_context.repo_url
|
|
129
|
+
|
|
130
|
+
if "github.com" in repo_url:
|
|
131
|
+
auth_url = repo_url.replace(
|
|
132
|
+
"https://github.com", f"https://x-access-token:{github_token}@github.com"
|
|
133
|
+
)
|
|
134
|
+
result = _run_git(
|
|
135
|
+
["remote", "set-url", "origin", auth_url], cwd=config.workspace
|
|
136
|
+
)
|
|
137
|
+
if result.returncode != 0:
|
|
138
|
+
config.log("warning", f"Failed to update origin URL: {result.stderr}")
|
|
139
|
+
|
|
140
|
+
return setup_git_credentials(config)
|
|
141
|
+
|
|
142
|
+
|
|
125
143
|
def clone_repository(config: GitConfig) -> bool:
|
|
126
144
|
"""Clone the repository to the workspace.
|
|
127
145
|
|
|
@@ -182,10 +200,20 @@ def setup_branch(config: GitConfig) -> bool:
|
|
|
182
200
|
)
|
|
183
201
|
|
|
184
202
|
if result.returncode == 0 and branch_name in result.stdout:
|
|
185
|
-
# Branch exists,
|
|
203
|
+
# Branch exists, fetch it explicitly (single-branch clones don't fetch other heads)
|
|
186
204
|
config.log("info", f"Checking out existing branch: {branch_name}")
|
|
205
|
+
fetch_result = _run_git(
|
|
206
|
+
["fetch", "origin", branch_name],
|
|
207
|
+
cwd=workspace,
|
|
208
|
+
)
|
|
209
|
+
if fetch_result.returncode != 0:
|
|
210
|
+
config.log(
|
|
211
|
+
"error", f"Failed to fetch branch {branch_name}: {fetch_result.stderr}"
|
|
212
|
+
)
|
|
213
|
+
return False
|
|
214
|
+
|
|
187
215
|
result = _run_git(
|
|
188
|
-
["checkout", "-B", branch_name,
|
|
216
|
+
["checkout", "-B", branch_name, "FETCH_HEAD"],
|
|
189
217
|
cwd=workspace,
|
|
190
218
|
)
|
|
191
219
|
else:
|
bootstrap/orchestrator.py
CHANGED
|
@@ -26,9 +26,19 @@ from bootstrap.constants import (
|
|
|
26
26
|
MAX_ARTIFACT_RETRIES,
|
|
27
27
|
)
|
|
28
28
|
from bootstrap.config_loader import UserConfig, load_codex_config
|
|
29
|
-
from bootstrap.git_ops import
|
|
29
|
+
from bootstrap.git_ops import (
|
|
30
|
+
GitConfig,
|
|
31
|
+
commit_changes,
|
|
32
|
+
initialize_repo,
|
|
33
|
+
push_changes,
|
|
34
|
+
update_git_auth,
|
|
35
|
+
)
|
|
30
36
|
from bootstrap.install import codex_login_status_ok, codex_on_path, ensure_codex
|
|
31
|
-
from bootstrap.payload import
|
|
37
|
+
from bootstrap.payload import (
|
|
38
|
+
BootstrapPayload,
|
|
39
|
+
fetch_bootstrap_payload,
|
|
40
|
+
fetch_github_token,
|
|
41
|
+
)
|
|
32
42
|
from bootstrap.prompts import build_prompt_text
|
|
33
43
|
from bootstrap.runner import (
|
|
34
44
|
CodexEvent,
|
|
@@ -246,27 +256,64 @@ class BootstrapOrchestrator:
|
|
|
246
256
|
if self.git_config is None:
|
|
247
257
|
return
|
|
248
258
|
|
|
259
|
+
commit_message = (
|
|
260
|
+
f"Flywheel experiment run: {self.config.run_id}"
|
|
261
|
+
if exit_code == 0
|
|
262
|
+
else f"[WIP] Flywheel experiment run (failed): {self.config.run_id}"
|
|
263
|
+
)
|
|
264
|
+
|
|
249
265
|
if exit_code != 0:
|
|
250
266
|
self._log(
|
|
251
|
-
f"Git: Codex exited with code {exit_code},
|
|
267
|
+
f"Git: Codex exited with code {exit_code}, committing WIP",
|
|
252
268
|
level="warning",
|
|
253
269
|
)
|
|
254
|
-
|
|
255
|
-
|
|
270
|
+
else:
|
|
271
|
+
self._log("Git: Finalizing repository, committing and pushing changes")
|
|
256
272
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
f"[WIP] Flywheel experiment run (failed): {self.config.run_id}",
|
|
260
|
-
)
|
|
273
|
+
if not commit_changes(self.git_config, commit_message):
|
|
274
|
+
self._log("Git: Failed to commit changes", level="error")
|
|
261
275
|
return
|
|
262
276
|
|
|
263
|
-
self.
|
|
277
|
+
self._refresh_github_token()
|
|
264
278
|
|
|
265
|
-
if
|
|
279
|
+
if push_changes(self.git_config):
|
|
266
280
|
self._log("Git: Changes pushed successfully")
|
|
267
281
|
else:
|
|
268
282
|
self._log("Git: Failed to push changes", level="error")
|
|
269
283
|
|
|
284
|
+
def _refresh_github_token(self) -> None:
|
|
285
|
+
"""Refresh the GitHub token before pushing."""
|
|
286
|
+
if self.git_config is None:
|
|
287
|
+
return
|
|
288
|
+
|
|
289
|
+
try:
|
|
290
|
+
token = fetch_github_token(
|
|
291
|
+
self.config.server_url,
|
|
292
|
+
self.config.run_id,
|
|
293
|
+
self.config.capability_token,
|
|
294
|
+
)
|
|
295
|
+
except Exception as exc:
|
|
296
|
+
self._log(
|
|
297
|
+
f"Git: Failed to refresh GitHub token ({exc}); using existing token",
|
|
298
|
+
level="warning",
|
|
299
|
+
)
|
|
300
|
+
return
|
|
301
|
+
|
|
302
|
+
if not token:
|
|
303
|
+
self._log(
|
|
304
|
+
"Git: Empty GitHub token response; using existing token",
|
|
305
|
+
level="warning",
|
|
306
|
+
)
|
|
307
|
+
return
|
|
308
|
+
|
|
309
|
+
if update_git_auth(self.git_config, token):
|
|
310
|
+
self._log("Git: Refreshed GitHub token")
|
|
311
|
+
else:
|
|
312
|
+
self._log(
|
|
313
|
+
"Git: Failed to update git credentials after token refresh",
|
|
314
|
+
level="warning",
|
|
315
|
+
)
|
|
316
|
+
|
|
270
317
|
def _ensure_codex_authenticated(self, codex_path: Path) -> None:
|
|
271
318
|
"""Fail fast if codex is present but not logged in."""
|
|
272
319
|
if codex_login_status_ok(codex_path):
|
|
@@ -644,13 +691,17 @@ class BootstrapOrchestrator:
|
|
|
644
691
|
self._stop_heartbeats.wait(HEARTBEAT_INTERVAL_SECONDS)
|
|
645
692
|
|
|
646
693
|
def _handle_event(self, event: CodexEvent) -> None:
|
|
694
|
+
extra: dict[str, object] = {}
|
|
695
|
+
if isinstance(event.raw, dict):
|
|
696
|
+
# Preserve structured event data so the server can parse usage.
|
|
697
|
+
extra = {"event": event.raw}
|
|
647
698
|
post_log(
|
|
648
699
|
self.config.server_url,
|
|
649
700
|
self.config.run_id,
|
|
650
701
|
self.config.capability_token,
|
|
651
702
|
level="info",
|
|
652
703
|
message=str(event.raw),
|
|
653
|
-
extra=
|
|
704
|
+
extra=extra,
|
|
654
705
|
)
|
|
655
706
|
if isinstance(event.raw, dict):
|
|
656
707
|
run_id = event.raw.get("run_id")
|
bootstrap/payload.py
CHANGED
|
@@ -87,6 +87,23 @@ def fetch_bootstrap_payload(
|
|
|
87
87
|
)
|
|
88
88
|
|
|
89
89
|
|
|
90
|
+
def fetch_github_token(
|
|
91
|
+
server_url: str,
|
|
92
|
+
run_id: str,
|
|
93
|
+
token: str,
|
|
94
|
+
) -> str:
|
|
95
|
+
"""Retrieve a fresh GitHub installation token for this run."""
|
|
96
|
+
url = f"{server_url.rstrip('/')}/runs/{run_id}/github-token"
|
|
97
|
+
req = urllib.request.Request(url, headers={"X-Run-Token": token}, method="POST")
|
|
98
|
+
payload = _urlopen_json_with_retries(
|
|
99
|
+
req,
|
|
100
|
+
timeout_seconds=30,
|
|
101
|
+
attempts=4,
|
|
102
|
+
base_delay_seconds=0.5,
|
|
103
|
+
)
|
|
104
|
+
return str(payload.get("token", ""))
|
|
105
|
+
|
|
106
|
+
|
|
90
107
|
def _urlopen_json_with_retries(
|
|
91
108
|
req: urllib.request.Request,
|
|
92
109
|
timeout_seconds: int,
|
|
@@ -3,15 +3,15 @@ bootstrap/__main__.py,sha256=F94dlGd3P4icWVZnb16LjrKpBhLPCvjwqyWDqhX3JjE,1338
|
|
|
3
3
|
bootstrap/artifacts.py,sha256=pdwDfxduT-G9VMJiGdm9MHkfUf35TzMGhFTHn135nU4,4188
|
|
4
4
|
bootstrap/config_loader.py,sha256=dgypIOi4a5auGsarIDB5MI6qJO38UWO6RJkRTzvA9Zs,3936
|
|
5
5
|
bootstrap/constants.py,sha256=Q3bNGvKKgQxyP9Hrx6o5GydFHwDq2SRkLnxwRNyM-OY,633
|
|
6
|
-
bootstrap/git_ops.py,sha256=
|
|
6
|
+
bootstrap/git_ops.py,sha256=1_9E-iu2AerTzuT4rXajVkVmBlL5M5Q0gIn0v63YWc4,10274
|
|
7
7
|
bootstrap/install.py,sha256=Es1F0UBOYDhuv7i9veA5Bt-yyTyTZ-dAsHmGc-iRY5k,4166
|
|
8
|
-
bootstrap/orchestrator.py,sha256=
|
|
9
|
-
bootstrap/payload.py,sha256=
|
|
8
|
+
bootstrap/orchestrator.py,sha256=7KzG3fvHBleW-Rrg0S_C0zyK4nUsgSknzOzfFPGmSXw,34878
|
|
9
|
+
bootstrap/payload.py,sha256=3yGYq6W0gCdKBPWNYFjvaiwP70E2_3fAFNN7aTxDZ3Y,3906
|
|
10
10
|
bootstrap/prompts.py,sha256=trtZLytPyliuc50WV3glm6YcEs68tbvi4Iob5cUdbdQ,4664
|
|
11
11
|
bootstrap/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
12
12
|
bootstrap/runner.py,sha256=qFqDnHKxUIOBAfk9cX7EipXz7mJCwTJPeKMQI30x4Do,4509
|
|
13
13
|
bootstrap/telemetry.py,sha256=ONPvKTpc2puCygkakdQW7-Kkrx_yqEzZz-0FMtCQ-RQ,3983
|
|
14
|
-
flywheel_bootstrap-0.1.9.
|
|
15
|
-
flywheel_bootstrap-0.1.9.
|
|
16
|
-
flywheel_bootstrap-0.1.9.
|
|
17
|
-
flywheel_bootstrap-0.1.9.
|
|
14
|
+
flywheel_bootstrap-0.1.9.202602021053.dist-info/METADATA,sha256=KGR_0Z8lqhGCFGUOtZod2nWcG9N1rrvC6GDBaG8BHXQ,4630
|
|
15
|
+
flywheel_bootstrap-0.1.9.202602021053.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
16
|
+
flywheel_bootstrap-0.1.9.202602021053.dist-info/entry_points.txt,sha256=uluR7l4k_RhY5K4nYN5e6uwtceW3_7h_kvpM-csjzrg,63
|
|
17
|
+
flywheel_bootstrap-0.1.9.202602021053.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|