Fix case repo sync + auto-create Gitea repos + add sync indicator
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m30s
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m30s
- auto-sync-cases.sh: fix broken directory scan (was looking for
status subdirs that don't exist), fix env var word-splitting bug,
add safe.directory handling and error logging
- cases.py: auto-create Gitea repo on case_create, fix
documents/original → documents/originals naming mismatch
- app.py: add GET /api/cases/{case_number}/git-status endpoint
- web-ui: add SyncIndicator component in case header showing
sync status (synced/pending/no remote) with last commit time
- pyproject.toml: add httpx dependency
- CLAUDE.md: update Paperclip wakeup API docs
- settings page: switch tag input from Select to free-text with datalist
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3,14 +3,107 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
from uuid import UUID
|
||||
|
||||
import httpx
|
||||
|
||||
from legal_mcp import config
|
||||
from legal_mcp.services import audit, db, practice_area as pa
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
GITEA_ORG = "cases"
|
||||
|
||||
|
||||
def _gitea_host() -> str:
|
||||
return os.environ.get("GITEA_HOST", "https://gitea.nautilus.marcusgroup.org")
|
||||
|
||||
|
||||
def _gitea_token() -> str:
|
||||
return os.environ.get("GITEA_ACCESS_TOKEN") or os.environ.get("GITEA_TOKEN", "")
|
||||
|
||||
|
||||
async def _setup_gitea_remote(case_number: str, title: str, case_dir: Path) -> bool:
|
||||
"""Create Gitea repo and configure git remote. Best-effort — returns False on failure."""
|
||||
token = _gitea_token()
|
||||
if not token:
|
||||
logger.info("No GITEA_TOKEN — skipping Gitea repo creation for %s", case_number)
|
||||
return False
|
||||
|
||||
try:
|
||||
async with httpx.AsyncClient(verify=False, timeout=30) as client:
|
||||
resp = await client.post(
|
||||
f"{_gitea_host()}/api/v1/orgs/{GITEA_ORG}/repos",
|
||||
headers={"Authorization": f"token {token}"},
|
||||
json={
|
||||
"name": case_number,
|
||||
"description": f"ערר {case_number} — {title}"[:255],
|
||||
"private": True,
|
||||
"auto_init": False,
|
||||
},
|
||||
)
|
||||
if resp.status_code == 409:
|
||||
resp2 = await client.get(
|
||||
f"{_gitea_host()}/api/v1/repos/{GITEA_ORG}/{case_number}",
|
||||
headers={"Authorization": f"token {token}"},
|
||||
)
|
||||
resp2.raise_for_status()
|
||||
repo = resp2.json()
|
||||
else:
|
||||
resp.raise_for_status()
|
||||
repo = resp.json()
|
||||
|
||||
clone_url = repo.get("clone_url", "")
|
||||
if not clone_url:
|
||||
return False
|
||||
|
||||
auth_url = clone_url.replace("https://", f"https://chaim:{token}@")
|
||||
|
||||
git_env = {
|
||||
"GIT_AUTHOR_NAME": "Ezer Mishpati",
|
||||
"GIT_AUTHOR_EMAIL": "legal@local",
|
||||
"GIT_COMMITTER_NAME": "Ezer Mishpati",
|
||||
"GIT_COMMITTER_EMAIL": "legal@local",
|
||||
"PATH": os.environ.get("PATH", "/usr/bin:/bin"),
|
||||
}
|
||||
|
||||
# Add or update remote
|
||||
result = subprocess.run(
|
||||
["git", "remote", "get-url", "origin"],
|
||||
cwd=case_dir, capture_output=True, text=True,
|
||||
)
|
||||
if result.returncode == 0:
|
||||
subprocess.run(
|
||||
["git", "remote", "set-url", "origin", auth_url],
|
||||
cwd=case_dir, capture_output=True, env=git_env,
|
||||
)
|
||||
else:
|
||||
subprocess.run(
|
||||
["git", "remote", "add", "origin", auth_url],
|
||||
cwd=case_dir, capture_output=True, env=git_env,
|
||||
)
|
||||
|
||||
# Push
|
||||
push = subprocess.run(
|
||||
["git", "push", "-u", "origin", "HEAD"],
|
||||
cwd=case_dir, capture_output=True, text=True, env=git_env,
|
||||
)
|
||||
if push.returncode != 0:
|
||||
logger.warning("Gitea push failed for %s: %s", case_number, push.stderr)
|
||||
return False
|
||||
|
||||
logger.info("Gitea repo created and pushed for %s", case_number)
|
||||
return True
|
||||
|
||||
except Exception as exc:
|
||||
logger.warning("Gitea setup failed for %s: %s", case_number, exc)
|
||||
return False
|
||||
|
||||
|
||||
async def case_create(
|
||||
case_number: str,
|
||||
@@ -92,7 +185,7 @@ async def case_create(
|
||||
case_dir.mkdir(parents=True, exist_ok=True)
|
||||
docs_dir = case_dir / "documents"
|
||||
docs_dir.mkdir(exist_ok=True)
|
||||
(docs_dir / "original").mkdir(exist_ok=True)
|
||||
(docs_dir / "originals").mkdir(exist_ok=True)
|
||||
(docs_dir / "extracted").mkdir(exist_ok=True)
|
||||
(docs_dir / "proofread").mkdir(exist_ok=True)
|
||||
(docs_dir / "backup").mkdir(exist_ok=True)
|
||||
@@ -121,6 +214,12 @@ async def case_create(
|
||||
except Exception:
|
||||
pass # git not available — non-critical
|
||||
|
||||
# Create Gitea repo and configure remote (best-effort)
|
||||
try:
|
||||
await _setup_gitea_remote(case_number, title, case_dir)
|
||||
except Exception:
|
||||
pass # Gitea not available — non-critical
|
||||
|
||||
return json.dumps(case, default=str, ensure_ascii=False, indent=2)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user