mirror of
https://github.com/allaunthefox/Research-Stack.git
synced 2026-07-31 03:05:21 +00:00
- Replaced database-1-instance-1.cghu8yqogqwo.us-east-1.rds.amazonaws.com with 'localhost' as default. - Removed AWS IAM generate-db-auth-token CLI subprocessing and boto3 token generation blocks from rds_connect.py, ingest_flexure_joints.py, pist_route_repair.py, and ene-api-wrapper.sh. - Purged AWS DEFAULT_REGION and AWS_REGION configurations where applicable. - Updated Rust rds_probe to use standard PG environment variables. Build: 0 jobs, 0 errors (lake build)
137 lines
4.3 KiB
Python
137 lines
4.3 KiB
Python
#!/usr/bin/env python3
|
|
"""Sync filesystem wiki Markdown files to ENE RDS and emit a JSON receipt."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
import os
|
|
import sys
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
from rds_connect import connect_rds
|
|
from shim.utils import sha256_text, utc_now
|
|
|
|
STACK_ROOT = Path(os.environ.get("STACK_ROOT", "/home/allaun/Research Stack"))
|
|
WIKI_ROOT = Path(os.environ.get("WIKI_ROOT", str(STACK_ROOT / "6-Documentation" / "wiki")))
|
|
|
|
HOST = os.environ.get("RDS_HOST", "localhost")
|
|
DB = os.environ.get("RDS_DB", os.environ.get("RDS_DBNAME", "postgres"))
|
|
|
|
|
|
def title_from_slug(slug: str) -> str:
|
|
return slug.replace("-", " ").replace("_", " ").title()
|
|
|
|
|
|
def iter_markdown_files(root: Path) -> list[Path]:
|
|
if not root.exists():
|
|
raise FileNotFoundError(f"wiki root does not exist: {root}")
|
|
return sorted(path for path in root.rglob("*.md") if path.is_file())
|
|
|
|
|
|
def sync_files(conn, files: list[Path], dry_run: bool) -> tuple[int, int, list[dict[str, str]]]:
|
|
indexed = 0
|
|
skipped = 0
|
|
failures: list[dict[str, str]] = []
|
|
cur = conn.cursor()
|
|
try:
|
|
for fpath in files:
|
|
try:
|
|
rel = fpath.relative_to(WIKI_ROOT)
|
|
slug = rel.with_suffix("").as_posix()
|
|
title = title_from_slug(slug)
|
|
content = fpath.read_text(encoding="utf-8", errors="replace")
|
|
|
|
cur.execute("SELECT content FROM ene.wiki_pages WHERE slug = %s", (slug,))
|
|
row = cur.fetchone()
|
|
if row and row[0] == content:
|
|
skipped += 1
|
|
continue
|
|
|
|
if not dry_run:
|
|
cur.execute(
|
|
"""INSERT INTO ene.wiki_pages (slug, title, content, updated_at)
|
|
VALUES (%s, %s, %s, %s)
|
|
ON CONFLICT (slug) DO UPDATE SET
|
|
title = EXCLUDED.title,
|
|
content = EXCLUDED.content,
|
|
updated_at = EXCLUDED.updated_at""",
|
|
(slug, title, content, utc_now()),
|
|
)
|
|
indexed += 1
|
|
except Exception as exc: # noqa: BLE001 - receipt records per-file failure.
|
|
failures.append({"path": str(fpath), "error": str(exc)})
|
|
if dry_run:
|
|
conn.rollback()
|
|
else:
|
|
conn.commit()
|
|
finally:
|
|
cur.close()
|
|
return indexed, skipped, failures
|
|
|
|
|
|
def build_receipt(
|
|
*,
|
|
indexed: int,
|
|
skipped: int,
|
|
failures: list[dict[str, str]],
|
|
dry_run: bool,
|
|
file_count: int,
|
|
) -> dict[str, Any]:
|
|
receipt: dict[str, Any] = {
|
|
"schema": "ene_wiki_to_rds_sync_receipt_v1",
|
|
"version": "1.0.0",
|
|
"generated_at_utc": utc_now(),
|
|
"dry_run": dry_run,
|
|
"wiki_root": str(WIKI_ROOT),
|
|
"rds_host": HOST,
|
|
"rds_db": DB,
|
|
"file_count": file_count,
|
|
"indexed_count": indexed,
|
|
"skipped_count": skipped,
|
|
"failed_count": len(failures),
|
|
"failures": failures[:20],
|
|
}
|
|
preimage = {k: v for k, v in receipt.items() if k != "receipt_hash"}
|
|
receipt["receipt_hash"] = sha256_text(json.dumps(preimage, sort_keys=True))
|
|
return receipt
|
|
|
|
|
|
def write_receipt(receipt: dict[str, Any], path: str | None) -> None:
|
|
encoded = json.dumps(receipt, sort_keys=True)
|
|
print(encoded)
|
|
if path:
|
|
out = Path(path)
|
|
out.parent.mkdir(parents=True, exist_ok=True)
|
|
out.write_text(encoded + "\n", encoding="utf-8")
|
|
|
|
|
|
def parse_args() -> argparse.Namespace:
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("--dry-run", action="store_true")
|
|
parser.add_argument("--receipt-out")
|
|
return parser.parse_args()
|
|
|
|
|
|
def main() -> int:
|
|
args = parse_args()
|
|
files = iter_markdown_files(WIKI_ROOT)
|
|
conn = connect_rds()
|
|
try:
|
|
indexed, skipped, failures = sync_files(conn, files, args.dry_run)
|
|
finally:
|
|
conn.close()
|
|
receipt = build_receipt(
|
|
indexed=indexed,
|
|
skipped=skipped,
|
|
failures=failures,
|
|
dry_run=args.dry_run,
|
|
file_count=len(files),
|
|
)
|
|
write_receipt(receipt, args.receipt_out)
|
|
return 0 if not failures else 2
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|