mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-06-28 07:35:27 -04:00
fix(cookbook): locate cookbook_state.json via DATA_DIR, not hardcoded /app/data (#3332)
Three call sites hardcoded Path("/app/data/cookbook_state.json"), which only
exists in Docker; on a native run the real path is <repo>/data, so the state
file looked missing and cookbook serve-state was silently ignored. Two others
used os.environ.get("DATA_DIR", "data") (a relative fallback, since DATA_DIR is
never set as an env var). Route all five through core.constants.DATA_DIR so the
path is consistent and absolute on both Docker and native.
Part of #3331.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
committed by
GitHub
parent
9c90f62657
commit
505d8bae5a
@@ -17,6 +17,7 @@ from fastapi.responses import StreamingResponse
|
|||||||
|
|
||||||
from src.auth_helpers import require_authenticated_request, require_user
|
from src.auth_helpers import require_authenticated_request, require_user
|
||||||
from src.tool_implementations import do_manage_notes
|
from src.tool_implementations import do_manage_notes
|
||||||
|
from core.constants import DATA_DIR
|
||||||
|
|
||||||
|
|
||||||
COOKBOOK_READ_SCOPES = {"cookbook:read", "cookbook:launch"}
|
COOKBOOK_READ_SCOPES = {"cookbook:read", "cookbook:launch"}
|
||||||
@@ -425,7 +426,7 @@ def setup_codex_routes(
|
|||||||
def _read_cookbook_state() -> dict:
|
def _read_cookbook_state() -> dict:
|
||||||
from pathlib import Path as _Path
|
from pathlib import Path as _Path
|
||||||
import os as _os, json as _json
|
import os as _os, json as _json
|
||||||
p = _Path(_os.environ.get("DATA_DIR", "data")) / "cookbook_state.json"
|
p = _Path(DATA_DIR) / "cookbook_state.json"
|
||||||
if not p.exists():
|
if not p.exists():
|
||||||
return {}
|
return {}
|
||||||
try:
|
try:
|
||||||
@@ -733,7 +734,7 @@ def setup_codex_routes(
|
|||||||
import time as _t, json as _json
|
import time as _t, json as _json
|
||||||
from core.atomic_io import atomic_write_json
|
from core.atomic_io import atomic_write_json
|
||||||
from pathlib import Path as _Path
|
from pathlib import Path as _Path
|
||||||
cookbook_state_path = _Path("/app/data/cookbook_state.json")
|
cookbook_state_path = _Path(DATA_DIR) / "cookbook_state.json"
|
||||||
try:
|
try:
|
||||||
state = _json.loads(cookbook_state_path.read_text(encoding="utf-8"))
|
state = _json.loads(cookbook_state_path.read_text(encoding="utf-8"))
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ from core.platform_compat import (
|
|||||||
get_wsl_windows_user_profile,
|
get_wsl_windows_user_profile,
|
||||||
)
|
)
|
||||||
from routes.shell_routes import TMUX_LOG_DIR
|
from routes.shell_routes import TMUX_LOG_DIR
|
||||||
|
from core.constants import DATA_DIR
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -60,7 +61,7 @@ _HF_TOKEN_STATUS_SNIPPET = (
|
|||||||
|
|
||||||
def setup_cookbook_routes() -> APIRouter:
|
def setup_cookbook_routes() -> APIRouter:
|
||||||
router = APIRouter(tags=["cookbook"])
|
router = APIRouter(tags=["cookbook"])
|
||||||
_cookbook_state_path = Path(os.environ.get("DATA_DIR", "data")) / "cookbook_state.json"
|
_cookbook_state_path = Path(DATA_DIR) / "cookbook_state.json"
|
||||||
|
|
||||||
def _mask_secret(value: str) -> str:
|
def _mask_secret(value: str) -> str:
|
||||||
if not value:
|
if not value:
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ from typing import Tuple
|
|||||||
|
|
||||||
from src.auth_helpers import owner_filter
|
from src.auth_helpers import owner_filter
|
||||||
from core.platform_compat import IS_WINDOWS, find_bash
|
from core.platform_compat import IS_WINDOWS, find_bash
|
||||||
from core.constants import internal_api_base
|
from core.constants import DATA_DIR, internal_api_base
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -2043,7 +2043,7 @@ async def action_cookbook_serve(
|
|||||||
except Exception:
|
except Exception:
|
||||||
end_after_min = 0
|
end_after_min = 0
|
||||||
|
|
||||||
state_path = Path("/app/data/cookbook_state.json")
|
state_path = Path(DATA_DIR) / "cookbook_state.json"
|
||||||
try:
|
try:
|
||||||
state = json.loads(state_path.read_text(encoding="utf-8")) if state_path.exists() else {}
|
state = json.loads(state_path.read_text(encoding="utf-8")) if state_path.exists() else {}
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import time
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
from core.constants import internal_api_base
|
from core.constants import DATA_DIR, internal_api_base
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -130,7 +130,7 @@ async def _stop_serve(session_id: str, remote_host: str = "", ssh_port: str = ""
|
|||||||
|
|
||||||
|
|
||||||
async def _tick() -> None:
|
async def _tick() -> None:
|
||||||
state_path = Path("/app/data/cookbook_state.json")
|
state_path = Path(DATA_DIR) / "cookbook_state.json"
|
||||||
if not state_path.exists():
|
if not state_path.exists():
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
"""Guard: cookbook_state.json must be located via DATA_DIR, not hardcoded /app/data
|
||||||
|
(which breaks native runs) or a relative os.environ fallback."""
|
||||||
|
import pathlib
|
||||||
|
|
||||||
|
ROOT = pathlib.Path(__file__).resolve().parent.parent
|
||||||
|
FILES = [
|
||||||
|
"src/cookbook_serve_lifecycle.py",
|
||||||
|
"src/builtin_actions.py",
|
||||||
|
"routes/codex_routes.py",
|
||||||
|
"routes/cookbook_routes.py",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def test_no_hardcoded_app_data_cookbook_state():
|
||||||
|
for rel in FILES:
|
||||||
|
text = (ROOT / rel).read_text(encoding="utf-8")
|
||||||
|
for ln in text.splitlines():
|
||||||
|
if ln.strip().startswith("#"):
|
||||||
|
continue
|
||||||
|
assert "/app/data/cookbook_state" not in ln, f"{rel}: hardcoded /app/data: {ln.strip()}"
|
||||||
|
assert 'os.environ.get("DATA_DIR"' not in ln, f"{rel}: relative DATA_DIR env fallback: {ln.strip()}"
|
||||||
|
|
||||||
|
|
||||||
|
def test_cookbook_state_uses_datadir_constant():
|
||||||
|
# Each file that references cookbook_state.json should import the DATA_DIR constant.
|
||||||
|
for rel in FILES:
|
||||||
|
text = (ROOT / rel).read_text(encoding="utf-8")
|
||||||
|
if "cookbook_state.json" in text:
|
||||||
|
assert "from core.constants import DATA_DIR" in text, f"{rel}: missing DATA_DIR import"
|
||||||
Reference in New Issue
Block a user