Pulse/scripts/release_control/repo_file_io.py
2026-03-18 16:06:30 +00:00

73 lines
2.1 KiB
Python

#!/usr/bin/env python3
"""Helpers for reading repo files from either the working tree or the git index."""
from __future__ import annotations
import json
import os
from pathlib import Path
import subprocess
from typing import Any, Iterable
REPO_ROOT = Path(__file__).resolve().parents[2]
DEFAULT_REPO_ROOT = REPO_ROOT
def git_env() -> dict[str, str]:
env = os.environ.copy()
if REPO_ROOT != DEFAULT_REPO_ROOT:
env.pop("GIT_INDEX_FILE", None)
return env
def repo_relative_path(path: str | Path) -> str:
candidate = Path(path)
if candidate.is_absolute():
candidate = candidate.relative_to(REPO_ROOT)
return candidate.as_posix()
def read_repo_text(path: str | Path, *, staged: bool = False, strict_staged: bool = False) -> str:
rel = repo_relative_path(path)
if staged:
try:
result = subprocess.run(
["git", "show", f":{rel}"],
cwd=REPO_ROOT,
check=True,
capture_output=True,
text=True,
env=git_env(),
)
return result.stdout
except subprocess.CalledProcessError:
if strict_staged:
raise FileNotFoundError(f"missing staged index entry for {rel}") from None
return (REPO_ROOT / rel).read_text(encoding="utf-8")
def staged_path_exists(path: str | Path) -> bool:
rel = repo_relative_path(path)
result = subprocess.run(
["git", "cat-file", "-e", f":{rel}"],
cwd=REPO_ROOT,
check=False,
capture_output=True,
text=True,
env=git_env(),
)
return result.returncode == 0
def missing_staged_repo_paths(paths: Iterable[str | Path]) -> list[str]:
missing: list[str] = []
for path in paths:
rel = repo_relative_path(path)
if not staged_path_exists(rel):
missing.append(rel)
return missing
def load_repo_json(path: str | Path, *, staged: bool = False, strict_staged: bool = False) -> dict[str, Any]:
return json.loads(read_repo_text(path, staged=staged, strict_staged=strict_staged))