diff --git a/studio/backend/tests/test_llama_cpp_update.py b/studio/backend/tests/test_llama_cpp_update.py index 83ea07a066..86614606a1 100644 --- a/studio/backend/tests/test_llama_cpp_update.py +++ b/studio/backend/tests/test_llama_cpp_update.py @@ -393,9 +393,64 @@ def test_start_update_source_build_installs_prebuilt(monkeypatch, tmp_path): assert "--llama-tag" in cmd and "latest" in cmd assert cmd[cmd.index("--rocm-gfx") + 1] == "gfx110x" assert "--simple-policy" not in cmd and "--cpu-fallback" not in cmd - # No pin: source-build detection and the unpinned apply share the same - # "latest" resolver, so they already agree. - assert "--published-release-tag" not in cmd + # Pin to the release the host-aware resolver already picked, so a release + # published between resolve and the installer's own "latest" re-resolve + # cannot swap in an unconfirmed build (matches the marker path's pin). + assert "--published-release-tag" in cmd + assert cmd[cmd.index("--published-release-tag") + 1] == "b9585" + + +def test_start_update_source_build_pins_resolver_release_tag(monkeypatch, tmp_path): + # The source-build apply must pin the installer to the release the host-aware + # resolver picked (res release_tag), not the display tag. For a fork-wrapper + # release the two differ ("v1.0" release vs "b9457" display); only the real + # release tag is a valid --published-release-tag and post-install anchor. + # Confirming the displayed tag must still proceed and pin the real release. + install_dir = tmp_path / "llama.cpp" + binary = install_dir / "build" / "bin" / "llama-server" + binary.parent.mkdir(parents = True) + binary.write_text("stub") # no marker -> source-build path + monkeypatch.delenv("UNSLOTH_LLAMA_CPP_PATH", raising = False) + monkeypatch.setattr(upd, "_find_binary", lambda: str(binary)) + monkeypatch.setattr(upd, "_installer_script", lambda: tmp_path / "install_llama_prebuilt.py") + monkeypatch.setattr(upd, "_installed_build_number", lambda b: 9000) # behind b9457 + _prebuilt( + monkeypatch, + repo = "unslothai/llama.cpp", + release_tag = "v1.0", + llama_tag = "b9457", + asset = "llama-b9457-bin-linux-x64.tar.gz", + ) + + captured = {} + + class _Proc: + returncode = 0 + stdout = "installed" + stderr = "" + + def _fake_run(cmd, **kwargs): + return _Proc() + + def _on_start(cmd): + captured["cmd"] = cmd + # Installer writes the marker for the pinned release (real tag v1.0). + _write_install(install_dir, "b9457", release_tag = "v1.0") + + monkeypatch.setattr(upd.subprocess, "run", _fake_run) + _patch_installer_popen(monkeypatch, on_start = _on_start) + + res = upd.start_update(expected_tag = "b9457") # the displayed/confirmed tag + assert res["started"] is True, res + deadline = time.time() + 10 + while time.time() < deadline: + if upd.get_update_status()["job"]["state"] in ("success", "error"): + break + time.sleep(0.05) + cmd = captured["cmd"] + assert "--published-release-tag" in cmd + assert cmd[cmd.index("--published-release-tag") + 1] == "v1.0" + assert upd.get_update_status()["job"]["state"] == "success" def test_start_update_happy_path(monkeypatch, tmp_path): diff --git a/studio/backend/utils/llama_cpp_update.py b/studio/backend/utils/llama_cpp_update.py index fe13f16da5..dd80d8ef7e 100644 --- a/studio/backend/utils/llama_cpp_update.py +++ b/studio/backend/utils/llama_cpp_update.py @@ -712,9 +712,15 @@ def start_update(expected_tag: Optional[str] = None) -> dict: repo = (res or {}).get("repo") or DEFAULT_PUBLISHED_REPO from_tag = None asset = (res or {}).get("asset") - # No pin: source-build detection resolves via --resolve-prebuilt latest, - # the same resolver the unpinned apply uses, so the two already agree. - pin_release_tag = None + # Pin the installer to the exact release the host-aware resolver already + # picked (res release_tag, which is the real GitHub release tag and has + # any macOS walk-back already applied), so a release published between + # this resolve and the installer's own "latest" re-resolve cannot swap in + # an unconfirmed build. The pinned tag is host-compatible by construction, + # so pinning does not disable a needed walk-back (unlike the marker path); + # it also arms the post-install tag check in _run_update. Falls back to + # unpinned only if the resolver reported no release tag. + pin_release_tag = (res or {}).get("release_tag") or None resolved_tag = src.get("latest_tag") # Install exactly the build the caller confirmed. A release published in the