fix(hub): ошибка доступа к release/ не роняет claim — публикации нет (I-1, #22)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Andrey Epifancev 2026-10-14 22:35:33 +04:00
parent 7c922382e6
commit 3a4fed94ff
3 changed files with 28 additions and 4 deletions

View File

@ -113,7 +113,11 @@ def h_claim(hub, conn, m, q, body):
if cmd or time.monotonic() >= deadline:
break
time.sleep(1)
rel = release.current(hub.release_dir)
try: # команда уже выдана: публикация не должна превращать ответ в 500 (I-1)
rel = release.current(hub.release_dir)
except Exception: # noqa: BLE001
log.exception("release: чтение публикации при claim")
rel = None
return 200, {"command": cmd, "settings_version": settings.get(conn, "settings_version"),
"agent_release": {"version": rel["version"], "sha256": rel["sha256"]} if rel else None}

View File

@ -62,12 +62,12 @@ def publish(release_dir, source) -> dict:
def current(release_dir) -> dict | None:
"""Опубликованная версия по манифесту; нет или битый манифест — None (агенту нечего обновлять)."""
path = Path(release_dir) / "manifest.json"
if not path.exists():
return None
try:
m = json.loads(path.read_text(encoding="utf-8"))
return {"version": int(m["version"]), "sha256": str(m["sha256"]), "published_at": m.get("published_at")}
except (OSError, ValueError, KeyError, TypeError) as e:
except (FileNotFoundError, NotADirectoryError):
return None
except (OSError, ValueError, KeyError, TypeError) as e: # нет прав, битый JSON: публикации нет, мост работает
log.warning("release: манифест %s не читается (%s) — публикации нет", path, e)
return None

View File

@ -1,4 +1,5 @@
import json
import os
import threading
import urllib.error
import urllib.request
@ -462,3 +463,22 @@ def test_hint_without_launcher(hub, tmp_path):
def test_agent_view_without_release(hub):
v = hub["client"].get("/agent")
assert v["release"] is None and v["update_state"] is None and v["launcher_version"] is None and v["update_hint"] is None
def test_claim_survives_unreadable_release(hub):
rd = hub["hub"].release_dir
rd.mkdir()
(rd / "manifest.json").write_text('{"version": 6, "sha256": "x"}', encoding="utf-8")
os.chmod(rd, 0)
try:
b = hub["client"].post("/batches", {"title": "т", "author": "claude",
"commands": [{"type": "ensure_fields", "params": {"key": "A-1", "duedate": "2026-10-23"}}]})
r = hub["agent"].post("/agent/claim", {"agent_version": 5})
assert r["command"] is not None and r["agent_release"] is None # публикация не роняет выдачу команды
assert hub["client"].get("/agent")["release"] is None
with pytest.raises(HubError) as e:
hub["agent"].get("/agent/release")
assert e.value.status == 404
assert b["id"]
finally:
os.chmod(rd, 0o700)