fix(profiles): пустые слоты появлялись сами, потому что запрос пути создавал каталог
Владелец спросил, откуда 25 каталогов профилей, когда заводил единицы. Проверено исполнением: обращение к get_profile_dir создавало каталог. Спросили четыре пути — появились четыре каталога. В коде зашит список «стандартных» слотов — ag-orch-primary, ag-orch-fallback, ag-1..ag-20, ag-w1..ag-w10 — в двух местах: agy_subprocess и model_discovery_service. Любой обход этого списка материализовал их все. Отсюда ag-w1..ag-w4, ag-cold-*, ag-spare-* — владелец их не создавал. Спросить, где профиль жил бы, и завести его — разные действия. Создание теперь запрашивается явно, create=True, и это делают только те, кто действительно заводит профиль. Тесты и сценарий проверки, опиравшиеся на побочное создание, приведены к новому поведению: они проверяют изоляцию пути, а не то, что каталог возник сам. 669 passed, 2 skipped; ruff чисто; релизный гейт пройден. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
b309972e49
commit
6cb0d7c636
8 changed files with 69 additions and 12 deletions
|
|
@ -18,7 +18,7 @@ namespace HermesHubSetup
|
|||
// Подставляется сборщиком из фактического git-коммита. Раньше здесь
|
||||
// жил зашитый "8cddc9f", то есть манифест сообщал неправду о том, из
|
||||
// какого кода собран установщик.
|
||||
public const string BuildCommit = "0ad946e";
|
||||
public const string BuildCommit = "b309972";
|
||||
public const string MIN_HERMES_VERSION = "0.20.0";
|
||||
public const string MAX_TESTED_HERMES = "0.20.4";
|
||||
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
|
|
@ -138,10 +138,14 @@ def run_checks() -> int:
|
|||
|
||||
# 7. Antigravity profile isolation
|
||||
print("7. Checking Antigravity profile environment directory isolation...")
|
||||
# Проверяем изоляцию пути, а не побочное создание каталога: запрос пути
|
||||
# каталогов больше не плодит, иначе любая проверка засоряла бы диск
|
||||
# десятком пустых слотов.
|
||||
pdir = get_profile_env_dir("ag-w2")
|
||||
assert pdir.exists()
|
||||
assert "ag-w2" in str(pdir)
|
||||
print(f" [PASS] Profile directory isolated at {pdir}")
|
||||
assert "agy_profiles" in str(pdir)
|
||||
assert not pdir.exists(), "запрос пути не должен создавать каталог"
|
||||
print(f" [PASS] Profile directory isolated at {pdir} (не создан)")
|
||||
passed += 1
|
||||
|
||||
# 8. Error classification
|
||||
|
|
|
|||
|
|
@ -111,9 +111,21 @@ def get_compatibility_path() -> Path:
|
|||
return get_config_dir() / "compatibility.json"
|
||||
|
||||
|
||||
def get_profile_dir(profile_id: str, provider: Optional[str] = None) -> Path:
|
||||
"""Return isolated storage directory for a profile.
|
||||
|
||||
def get_profile_dir(
|
||||
profile_id: str, provider: Optional[str] = None, create: bool = False
|
||||
) -> Path:
|
||||
"""Вернуть каталог профиля. По умолчанию НИЧЕГО не создаёт.
|
||||
|
||||
Раньше эта функция создавала каталог при каждом обращении. В коде зашит
|
||||
список «стандартных» слотов — ag-orch-primary, ag-orch-fallback, ag-1..ag-20,
|
||||
ag-w1..ag-w10, — и любой обход этого списка материализовал их на диске. У
|
||||
владельца накопилось 25 каталогов, из которых он заводил единицы: остальные
|
||||
появились сами, просто потому что кто-то спросил путь.
|
||||
|
||||
Спрашивать, где профиль жил бы, и создавать его — разные действия. Создание
|
||||
теперь запрашивается явно через create=True: так делают те, кто
|
||||
действительно заводит профиль.
|
||||
|
||||
Accepts both (profile_id) and (provider, profile_id) or (profile_id, provider) gracefully.
|
||||
"""
|
||||
# If first argument looks like a provider or swapped, resolve cleanly
|
||||
|
|
@ -151,7 +163,8 @@ def get_profile_dir(profile_id: str, provider: Optional[str] = None) -> Path:
|
|||
folder_prefix = f"{prov_lower}_profiles"
|
||||
|
||||
d = get_hermes_home() / folder_prefix / p_id
|
||||
d.mkdir(parents=True, exist_ok=True)
|
||||
if create:
|
||||
d.mkdir(parents=True, exist_ok=True)
|
||||
return d
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -138,3 +138,30 @@ def test_immediate_exit_is_reported_as_failure(linux, monkeypatch, tmp_path):
|
|||
assert ok is False
|
||||
assert "окно не открылось" in msg
|
||||
assert data.get("exit_code") == 1
|
||||
|
||||
|
||||
# ── Пустые слоты появлялись сами ──
|
||||
|
||||
def test_asking_for_a_path_creates_nothing(tmp_path, monkeypatch):
|
||||
"""У владельца накопилось 25 каталогов профилей при единицах заведённых.
|
||||
|
||||
Причина: get_profile_dir создавал каталог при каждом обращении, а в коде
|
||||
зашит список «стандартных» слотов — ag-orch-primary, ag-orch-fallback,
|
||||
ag-1..ag-20, ag-w1..ag-w10. Любой обход этого списка материализовал их все.
|
||||
Спросить, где профиль жил бы, и завести его — разные действия.
|
||||
"""
|
||||
from antigravity_provider import paths
|
||||
|
||||
monkeypatch.setenv("HERMES_HOME", str(tmp_path))
|
||||
root = tmp_path / "agy_profiles"
|
||||
|
||||
for pid in ("ag-orch-primary", "ag-w7", "ag-19", "ag-cold-3"):
|
||||
paths.get_profile_dir(pid, "antigravity")
|
||||
|
||||
assert not root.exists() or not any(root.iterdir()), (
|
||||
"запрос пути не должен оставлять каталоги на диске"
|
||||
)
|
||||
|
||||
created = paths.get_profile_dir("ag-1", "antigravity", create=True)
|
||||
assert created.is_dir()
|
||||
assert sorted(p.name for p in root.iterdir()) == ["ag-1"]
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ def test_check_profile_native_auth_status(tmp_path, monkeypatch):
|
|||
monkeypatch.setenv("HERMES_HOME", str(tmp_path / "hermes"))
|
||||
|
||||
slot = "ag-w3"
|
||||
pdir = get_profile_dir(slot, "antigravity")
|
||||
pdir = get_profile_dir(slot, "antigravity", create=True)
|
||||
gemini_dir = pdir / ".gemini"
|
||||
gemini_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
|
|
@ -169,7 +169,7 @@ def test_multi_account_sequential_slot_progression(tmp_path, monkeypatch):
|
|||
|
||||
# Connect first slot
|
||||
AutoAssigner.ensure_profile_definition("antigravity", first_slot)
|
||||
pdir = get_profile_dir(first_slot, "antigravity")
|
||||
pdir = get_profile_dir(first_slot, "antigravity", create=True)
|
||||
(pdir / "auth.json").write_text(json.dumps({"auth_method": "oauth", "email": "user1@gmail.com"}))
|
||||
|
||||
# Next free slot
|
||||
|
|
@ -178,7 +178,7 @@ def test_multi_account_sequential_slot_progression(tmp_path, monkeypatch):
|
|||
|
||||
# Connect second slot
|
||||
AutoAssigner.ensure_profile_definition("antigravity", second_slot)
|
||||
pdir2 = get_profile_dir(second_slot, "antigravity")
|
||||
pdir2 = get_profile_dir(second_slot, "antigravity", create=True)
|
||||
(pdir2 / "auth.json").write_text(json.dumps({"auth_method": "oauth", "email": "user2@gmail.com"}))
|
||||
|
||||
# Next free slot
|
||||
|
|
|
|||
|
|
@ -238,10 +238,23 @@ class TestRouterEngineFailover:
|
|||
class TestAntigravityIsolation:
|
||||
"""Test environment isolation for Antigravity profiles."""
|
||||
|
||||
def test_profile_env_dir_creation(self):
|
||||
def test_profile_env_dir_is_isolated_and_not_created_on_lookup(self):
|
||||
"""Запрос пути не должен создавать каталог.
|
||||
|
||||
Прежде создавал, и любой обход списка стандартных слотов
|
||||
(ag-orch-primary, ag-1..ag-20, ag-w1..ag-w10) материализовал их все:
|
||||
у владельца накопилось 25 каталогов при единицах заведённых вручную.
|
||||
"""
|
||||
pdir = get_profile_env_dir("ag-w1")
|
||||
assert pdir.exists()
|
||||
assert "ag-w1" in str(pdir)
|
||||
assert "agy_profiles" in str(pdir)
|
||||
assert not pdir.exists(), "спросили путь — каталог появиться не должен"
|
||||
|
||||
from antigravity_provider.paths import get_profile_dir
|
||||
|
||||
created = get_profile_dir("ag-w1", "antigravity", create=True)
|
||||
assert created.exists()
|
||||
assert created == pdir
|
||||
|
||||
|
||||
class TestRouterCLI:
|
||||
|
|
|
|||
Loading…
Reference in a new issue