feat(gateway/make): PR6 T6.14 — migrate-paths target + lifespan warning
`app/gateway/app.py:_check_path_migration_pending` runs at lifespan
startup and emits a single WARNING log when `{base_dir}/users/` still
has content — the operator's cue to run the new migration script. The
warning is best-effort (silent on permission errors) so it can never
escalate into a gateway boot failure; reads from the legacy tree keep
working via the `user_id` branch of `Paths.thread_dir` until the
migration runs.
The root Makefile gains a `migrate-paths` target that wraps
`scripts/migrate_paths_to_workspace.py`. Two opt-in env knobs:
- `DRY_RUN=1` — pass `--dry-run` (preview only, no writes)
- `DEFAULT_WORKSPACE=…` — claim un-assigned users under this workspace id
(defaults to `legacy_workspace` to align with PR5's orphan-row bucket).
`help` documents the new target.
3 tests cover the warning's three states (legacy dir with content, dir
missing, dir empty).
This commit is contained in:
@@ -175,6 +175,34 @@ async def _migrate_orphaned_threads(store, admin_user_id: str) -> int:
|
||||
return migrated
|
||||
|
||||
|
||||
def _check_path_migration_pending(app: FastAPI) -> None:
|
||||
"""Warn the operator if the PR4 legacy user-isolation layout still has content.
|
||||
|
||||
PR6 routes every new write into ``{base_dir}/workspaces/{wid}/...`` via
|
||||
``Paths``. Pre-PR6 installations have data at
|
||||
``{base_dir}/users/{uid}/...`` that needs ``make migrate-paths`` to lift
|
||||
it under a workspace. We emit a warning at boot rather than crashing so
|
||||
the gateway keeps serving (reads from the legacy tree still work via the
|
||||
user_id branch of ``Paths.thread_dir``), but with a loud signal to run
|
||||
the migration script.
|
||||
"""
|
||||
from deerflow.config.paths import get_paths
|
||||
|
||||
legacy_users = get_paths().base_dir / "users"
|
||||
if not legacy_users.exists():
|
||||
return
|
||||
try:
|
||||
has_content = any(legacy_users.iterdir())
|
||||
except OSError:
|
||||
# Permission or transient FS issue — don't escalate; lifespan must succeed.
|
||||
return
|
||||
if has_content:
|
||||
logger.warning(
|
||||
"Legacy per-user layout detected at %s. Run `make migrate-paths` to lift it under the per-workspace layout (PR6).",
|
||||
legacy_users,
|
||||
)
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
|
||||
"""Application lifespan handler."""
|
||||
@@ -191,6 +219,8 @@ async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
|
||||
config = get_gateway_config()
|
||||
logger.info(f"Starting API Gateway on {config.host}:{config.port}")
|
||||
|
||||
_check_path_migration_pending(app)
|
||||
|
||||
# Initialize LangGraph runtime components (StreamBridge, RunManager, checkpointer, store)
|
||||
async with langgraph_runtime(app):
|
||||
logger.info("LangGraph runtime initialised")
|
||||
|
||||
Reference in New Issue
Block a user