fix(checkpoints): profile rename finds the helpers its sibling calls

checkpoint_manager_profile_rename.py (merged from main) reaches for
_store_has_head/_ref_tip/_list_project_refs/_unlink_quiet, which main
extracted from checkpoint_manager after this branch diverged; a profile
rename with checkpoints raised AttributeError. Add the same helpers here.

Also: tests follow this branch's contracts (model_metadata_http.get instead
of a requests module, no agent.ssl_guard, backup excludes PM trees too,
_safe_restore_plan), and run_tests.sh forwards SSL_CERT_FILE/DIR so network
tests verify TLS with the pinned interpreter on hosts without a compiled-in
bundle path.
This commit is contained in:
ethernet
2026-09-19 02:39:23 -04:00
parent ae87819213
commit 30bf9ab47c
7 changed files with 38 additions and 8 deletions

View File

@@ -139,11 +139,15 @@ done
#
# These are test-infrastructure knobs, not credentials — same class as the
# HERMES_RUN_SLOW_PET_TESTS / HERMES_E2E_BROWSER opt-ins already forwarded.
# SSL_CERT_FILE/DIR are trust-store locations: the pinned interpreter's
# OpenSSL has no compiled-in bundle path on NixOS, so network tests (PM
# downloads, channel reads) need the host's pointer to verify TLS.
# Keep this an explicit allowlist (no HERMES_TEST_* glob) so the "no
# credential can leak" property stays auditable at a glance.
TEST_ENV=()
for _test_var in HERMES_TEST_IMAGE HERMES_TEST_WORKERS HERMES_TEST_PATHS \
HERMES_TEST_FILE_TIMEOUT HERMES_TEST_FILE_RETRIES HERMES_TEST_SLICE; do
HERMES_TEST_FILE_TIMEOUT HERMES_TEST_FILE_RETRIES HERMES_TEST_SLICE \
SSL_CERT_FILE SSL_CERT_DIR; do
if [ -n "${!_test_var:-}" ]; then
TEST_ENV+=("$_test_var=${!_test_var}")
fi

View File

@@ -59,7 +59,7 @@ def test_astra_900k_opt_in_preserves_live_limits_and_wire_contract(monkeypatch,
monkeypatch.setenv("HERMES_HOME", str(tmp_path))
monkeypatch.setattr(metadata, "_codex_oauth_context_cache", {})
monkeypatch.setattr(metadata.requests, "get", lambda *args, **kwargs: SimpleNamespace(
monkeypatch.setattr(metadata.model_metadata_http, "get", lambda *args, **kwargs: SimpleNamespace(
status_code=200,
json=lambda: {"models": [{"slug": "gpt-6-astra", "context_window": advertised}]},
))

View File

@@ -144,7 +144,6 @@ def test_missing_key_banner_is_classified_without_hiding_initialization(tmp_path
monkeypatch.setattr(agent_init, "_explicit_client_kwargs",
lambda *a: {"api_key": "dummy-key", "base_url": agent.base_url})
monkeypatch.setattr(agent_init, "_apply_openai_header_policy", lambda *a: None)
monkeypatch.setattr("agent.ssl_guard.verify_ca_bundle", lambda: None)
agent_init._init_openai_client(agent, "dummy-key", agent.base_url, None, 30)
output = capsys.readouterr().out
assert ("API key appears invalid or missing" in output) is (suppress is not True)

View File

@@ -303,8 +303,7 @@ def test_catalog_requests_use_ungated_client_version(monkeypatch):
monkeypatch.setitem(sys.modules, "httpx", _FakeHttpx)
codex_models._fetch_models_from_api(access_token="tok")
monkeypatch.setattr(model_metadata, "requests", _FakeRequests)
monkeypatch.setattr(model_metadata, "_ensure_requests", lambda: None)
monkeypatch.setattr(model_metadata.model_metadata_http, "get", _FakeRequests.get)
monkeypatch.setattr(model_metadata, "_codex_oauth_context_cache", {})
model_metadata._fetch_codex_oauth_context_lengths_with_source("tok")

View File

@@ -68,7 +68,7 @@ def test_rename_preserves_profile_local_checkpoint_history(profile_env, tmp_path
assert str(new_workdir.resolve()) in project_paths
assert str(workdir.resolve()) not in project_paths
plan = manager.safe_restore_plan(str(new_workdir), checkpoint_hash)
plan = manager._safe_restore_plan(str(new_workdir), checkpoint_hash)
assert plan["success"] is True
assert plan["restore"] == ["note.txt"]
assert plan["skipped"] == []

View File

@@ -1702,11 +1702,12 @@ class TestCloneAllExcludesRuntimeTrees:
def test_runtime_trio_is_one_constant_shared_with_backup(self):
"""backup's exclusion list and the clone-all root gate must be built from the same
constant; two literals drifting apart is how the models/ copy of #111718 crept in."""
constant; two literals drifting apart is how the models/ copy of #111718 crept in.
backup additionally drops PM's regenerable trees; it never drops less."""
from hermes_cli import backup, profiles
from hermes_constants import LOCAL_RUNTIME_ROOT_DIRS
assert LOCAL_RUNTIME_ROOT_DIRS == frozenset(self.RUNTIME_TREES)
assert backup._EXCLUDED_ROOT_DIRS is LOCAL_RUNTIME_ROOT_DIRS
assert LOCAL_RUNTIME_ROOT_DIRS <= backup._EXCLUDED_ROOT_DIRS
assert LOCAL_RUNTIME_ROOT_DIRS <= profiles._CLONE_ALL_DEFAULT_EXCLUDE_ROOT
def test_clone_all_from_default_skips_runtime_trees_but_keeps_the_rest(self, profile_env):

View File

@@ -222,6 +222,10 @@ def _store_path(base: Optional[Path] = None) -> Path:
return (base or _resolve_checkpoint_base()) / _STORE_DIRNAME
def _store_has_head(store: Path) -> bool:
return (store / "HEAD").exists()
def _index_path(store: Path, dir_hash: str) -> Path:
return store / _INDEXES_DIRNAME / dir_hash
@@ -405,6 +409,29 @@ def _run_git(
return False, "", str(exc)
def _git_out(args: List[str], store: Path, working_dir: str, rc: Optional[Set[int]] = None) -> str:
"""stdout of a successful git call, else ``""``."""
ok, out, _ = _run_git(args, store, working_dir, allowed_returncodes=rc)
return out if ok else ""
def _ref_tip(store: Path, working_dir: str, ref: str) -> Optional[str]:
"""Commit sha at ``ref``, or None when the ref does not exist yet."""
return _git_out(["rev-parse", "--verify", ref + "^{commit}"], store, working_dir, {128}) or None
def _list_project_refs(store: Path, working_dir: str) -> List[str]:
out = _git_out(["for-each-ref", "--format=%(refname)", _REFS_PREFIX], store, working_dir, {128})
return [r for r in out.splitlines() if r.strip()]
def _unlink_quiet(path: Path) -> None:
try:
path.unlink(missing_ok=True)
except OSError:
pass
# ---------------------------------------------------------------------------
# Store initialisation + legacy migration
# ---------------------------------------------------------------------------