feat(repos): multi-machine path support via host_paths
Adds a JSONB column `host_paths` to managed_repos mapping
hostname → absolute local path. Fixes the consistency-checker
failure when the same repo lives at different paths on different
machines (e.g. /home/worsch/marki-docx on the workstation vs
/home/tegwick/marki-docx on custodiancore).
Changes:
- Migration g4b5c6d7e8f9: adds host_paths JSONB (default {})
- Model: host_paths Mapped[dict] column
- Schemas: host_paths in RepoRead; new RepoPathRegister schema
- Router: POST /repos/{slug}/paths/ — merges one host entry
- consistency_check.py: resolve_repo_path() prefers host_paths
[hostname] over local_path; --repo-path CLI override added
- MCP: update_repo_path(slug, path, host?) tool
- Makefile: register-path target; REPO_PATH passthrough on
check-consistency and fix-consistency targets
- TOOLS.md: documents update_repo_path
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,7 @@ import uuid
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import DateTime, ForeignKey, String, Text
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
from sqlalchemy.dialects.postgresql import JSONB, UUID
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from api.models.base import Base, TimestampMixin, new_uuid
|
||||
@@ -20,6 +20,7 @@ class ManagedRepo(Base, TimestampMixin):
|
||||
slug: Mapped[str] = mapped_column(String(100), unique=True, nullable=False, index=True)
|
||||
name: Mapped[str] = mapped_column(String(200), nullable=False)
|
||||
local_path: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
host_paths: Mapped[dict] = mapped_column(JSONB, nullable=False, default=dict, server_default="{}")
|
||||
remote_url: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
description: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
status: Mapped[str] = mapped_column(String(20), nullable=False, default="active")
|
||||
|
||||
@@ -15,6 +15,7 @@ from api.schemas.managed_repo import (
|
||||
DispatchWorkstream,
|
||||
RepoCreate,
|
||||
RepoDispatch,
|
||||
RepoPathRegister,
|
||||
RepoRead,
|
||||
RepoUpdate,
|
||||
)
|
||||
@@ -89,6 +90,26 @@ async def update_repo(
|
||||
return repo
|
||||
|
||||
|
||||
@router.post("/{slug}/paths/", response_model=RepoRead)
|
||||
async def register_host_path(
|
||||
slug: str,
|
||||
body: RepoPathRegister,
|
||||
session: AsyncSession = Depends(get_session),
|
||||
) -> ManagedRepo:
|
||||
"""Register or update the local path for a specific host.
|
||||
|
||||
Merges {"host": path} into host_paths without overwriting other entries.
|
||||
Use this when a repo lives at a different absolute path on different machines.
|
||||
"""
|
||||
repo = await _get_repo_by_slug(slug, session)
|
||||
updated = dict(repo.host_paths or {})
|
||||
updated[body.host] = body.path
|
||||
repo.host_paths = updated
|
||||
await session.commit()
|
||||
await session.refresh(repo)
|
||||
return repo
|
||||
|
||||
|
||||
@router.patch("/{slug}/archive", response_model=RepoRead)
|
||||
async def archive_repo(
|
||||
slug: str,
|
||||
|
||||
@@ -24,6 +24,12 @@ class RepoUpdate(BaseModel):
|
||||
last_state_synced_at: datetime | None = None
|
||||
|
||||
|
||||
class RepoPathRegister(BaseModel):
|
||||
"""Register a machine-local path for a repo on a specific host."""
|
||||
host: str
|
||||
path: str
|
||||
|
||||
|
||||
class RepoRead(BaseModel):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
id: uuid.UUID
|
||||
@@ -32,6 +38,7 @@ class RepoRead(BaseModel):
|
||||
slug: str
|
||||
name: str
|
||||
local_path: str | None = None
|
||||
host_paths: dict = {}
|
||||
remote_url: str | None = None
|
||||
description: str | None = None
|
||||
status: str
|
||||
|
||||
Reference in New Issue
Block a user