diff --git a/src/jira_hub/api.py b/src/jira_hub/api.py index bc70509..ac4729c 100644 --- a/src/jira_hub/api.py +++ b/src/jira_hub/api.py @@ -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} diff --git a/src/jira_hub/release.py b/src/jira_hub/release.py index 8ce1569..a0d093a 100644 --- a/src/jira_hub/release.py +++ b/src/jira_hub/release.py @@ -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 diff --git a/tests/test_api.py b/tests/test_api.py index 5459c72..13c6a08 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -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)