40 lines
1.3 KiB
Python
40 lines
1.3 KiB
Python
"""Filesystem path helpers for source-checkout and container runtimes."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
from functools import lru_cache
|
|
from pathlib import Path
|
|
|
|
|
|
@lru_cache(maxsize=1)
|
|
def repo_root() -> Path:
|
|
"""Return the Vignette repository root.
|
|
|
|
The API is run both from a source checkout and from Docker, where the code
|
|
lives under /app/apps/api. Keep that layout knowledge here instead of
|
|
repeating fragile ``Path(__file__).parents[...]`` offsets across modules.
|
|
"""
|
|
override = os.getenv("VIGNETTE_REPO_ROOT", "").strip()
|
|
if override:
|
|
return Path(override).expanduser().resolve()
|
|
|
|
current = Path(__file__).resolve()
|
|
for candidate in current.parents:
|
|
if (
|
|
(candidate / "apps" / "api" / "app").is_dir()
|
|
and (candidate / "data").exists()
|
|
):
|
|
return candidate
|
|
for candidate in current.parents:
|
|
if (
|
|
(candidate / "apps" / "api").is_dir()
|
|
and (candidate / "README.md").exists()
|
|
):
|
|
return candidate
|
|
raise RuntimeError("Vignette repository root not found; set VIGNETTE_REPO_ROOT")
|
|
|
|
|
|
def repo_path(*parts: str) -> Path:
|
|
"""Build an absolute path under the repository root."""
|
|
return repo_root().joinpath(*parts)
|