diff --git a/devops/vast/self_destruct.py b/devops/vast/self_destruct.py index 8b7b177..e7dc20b 100644 --- a/devops/vast/self_destruct.py +++ b/devops/vast/self_destruct.py @@ -102,8 +102,15 @@ def push_results( for i in range(1, attempts + 1): fetched = _run(["git", "fetch", "origin", branch], cwd=repo) if fetched.returncode == 0: - # Rebase our disjoint per-run folder onto the current branch tip. - rebased = _run(["git", "rebase", "--autostash", "FETCH_HEAD"], cwd=repo) + # Replay only the tip results commit onto the current branch tip. + # Boxes often train from merge commits (merged PRs); rebasing the + # full history onto the orphan `results` branch fails trying to + # recreate those merges. Per-run folders are disjoint, so only the + # tip commit needs to move. + rebased = _run( + ["git", "rebase", "--autostash", "--onto", "FETCH_HEAD", "HEAD~1"], + cwd=repo, + ) if rebased.returncode != 0: _run(["git", "rebase", "--abort"], cwd=repo) _log(f"rebase failed (attempt {i}): {rebased.stderr.strip()}", log) diff --git a/tests/test_infra_safeguards.py b/tests/test_infra_safeguards.py index f1b04a2..48fab0f 100644 --- a/tests/test_infra_safeguards.py +++ b/tests/test_infra_safeguards.py @@ -584,6 +584,33 @@ def fake_run(args, cwd=None): ) in calls +def test_self_destruct_rebases_only_tip_results_commit(tmp_path, monkeypatch): + calls = [] + + def fake_run(args, cwd=None): + calls.append(args) + if args[:3] == ["git", "diff", "--cached"]: + return SimpleNamespace(returncode=1, stdout="", stderr="") + return SimpleNamespace(returncode=0, stdout="", stderr="") + + monkeypatch.setattr("devops.vast.self_destruct._run", fake_run) + + assert push_results( + branch="results", + run_name="test", + instance_id="1", + repo=tmp_path, + ) + assert [ + "git", + "rebase", + "--autostash", + "--onto", + "FETCH_HEAD", + "HEAD~1", + ] in calls + + def test_failed_results_push_preserves_box_for_recovery(tmp_path, monkeypatch): messages = [] destroyed = []