This repository has been archived on 2026-07-08. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
repo-scoping/src/repo_registry/repo_ingestion/metadata.py
2026-04-25 23:04:15 +02:00

87 lines
3.1 KiB
Python

from __future__ import annotations
import json
import tomllib
from dataclasses import dataclass
from pathlib import Path
from urllib.parse import urlparse
@dataclass(frozen=True)
class RepositoryMetadata:
name: str
description: str | None
class RepositoryMetadataExtractor:
def extract(self, source_path: str | Path, url: str) -> RepositoryMetadata:
root = Path(source_path)
pyproject = self._from_pyproject(root)
package = self._from_package_json(root)
readme = self._from_readme(root)
fallback_name = self._name_from_url_or_path(url)
return RepositoryMetadata(
name=pyproject.name or package.name or readme.name or fallback_name,
description=(
pyproject.description
or package.description
or readme.description
),
)
def _from_pyproject(self, root: Path) -> RepositoryMetadata:
path = root / "pyproject.toml"
if not path.exists():
return RepositoryMetadata(name="", description=None)
try:
project = tomllib.loads(path.read_text(encoding="utf-8")).get("project", {})
except (OSError, tomllib.TOMLDecodeError):
return RepositoryMetadata(name="", description=None)
return RepositoryMetadata(
name=str(project.get("name") or ""),
description=project.get("description"),
)
def _from_package_json(self, root: Path) -> RepositoryMetadata:
path = root / "package.json"
if not path.exists():
return RepositoryMetadata(name="", description=None)
try:
package = json.loads(path.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError):
return RepositoryMetadata(name="", description=None)
return RepositoryMetadata(
name=str(package.get("name") or ""),
description=package.get("description"),
)
def _from_readme(self, root: Path) -> RepositoryMetadata:
for readme in sorted(root.glob("README*")):
if not readme.is_file():
continue
try:
lines = readme.read_text(encoding="utf-8", errors="ignore").splitlines()
except OSError:
continue
title = ""
for line in lines:
stripped = line.strip()
cleaned = stripped.strip("#").strip()
if stripped.startswith("#") and cleaned and not title:
title = cleaned
continue
if cleaned:
return RepositoryMetadata(name=title, description=cleaned)
if title:
return RepositoryMetadata(name=title, description=None)
return RepositoryMetadata(name="", description=None)
def _name_from_url_or_path(self, value: str) -> str:
parsed = urlparse(value)
path = parsed.path if parsed.scheme else value
name = Path(path.rstrip("/")).name or "repository"
if name.endswith(".git"):
name = name[:-4]
return name or "repository"