85 lines
2.9 KiB
Python
85 lines
2.9 KiB
Python
#!/usr/bin/env python3
|
|
"""Rebuild additive BLIF Flow v2 projection tables with explicit safeguards."""
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
import os
|
|
import sys
|
|
from pathlib import Path
|
|
from typing import Sequence
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
sys.path.insert(0, str(ROOT))
|
|
os.chdir(ROOT)
|
|
|
|
|
|
def build_parser() -> argparse.ArgumentParser:
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument(
|
|
"--production-shadow", action="store_true",
|
|
help="explicitly authorize projection-only shadow writes to database clientflow",
|
|
)
|
|
return parser
|
|
|
|
|
|
def validate_execution(*, production_shadow: bool, mode: str, database: str) -> None:
|
|
"""Validate CLI intent independently of DATABASE_URL inference."""
|
|
normalized_mode = str(mode or "").strip().lower()
|
|
if production_shadow:
|
|
if normalized_mode != "shadow":
|
|
raise RuntimeError("--production-shadow requires BLIF_FLOW_V2_MODE=shadow")
|
|
if database != "clientflow":
|
|
raise RuntimeError(
|
|
f"--production-shadow requires database 'clientflow', found {database!r}"
|
|
)
|
|
return
|
|
if database == "clientflow":
|
|
raise RuntimeError("production database requires explicit --production-shadow opt-in")
|
|
|
|
|
|
def main(argv: Sequence[str] | None = None) -> int:
|
|
# argparse handles --help and exits before any application/DB import below.
|
|
args = build_parser().parse_args(argv)
|
|
|
|
from sqlalchemy import text
|
|
from app.blif_flow_v2_projection_service import rebuild_blif_flow_v2_projection
|
|
from app.config import settings
|
|
from app.db import engine
|
|
|
|
mode = str(settings.blif_flow_v2_mode or "").strip().lower()
|
|
with engine.connect() as conn:
|
|
identity = conn.execute(text(
|
|
"SELECT current_database(), current_user, current_setting('transaction_read_only')"
|
|
)).one()
|
|
database, user, transaction_read_only = identity
|
|
validate_execution(
|
|
production_shadow=args.production_shadow, mode=mode, database=database,
|
|
)
|
|
print(json.dumps({
|
|
"database": database, "user": user, "blif_flow_v2_mode": mode,
|
|
"transaction_read_only": transaction_read_only,
|
|
"production_shadow_opt_in": args.production_shadow,
|
|
}, sort_keys=True), flush=True)
|
|
|
|
if args.production_shadow:
|
|
result = rebuild_blif_flow_v2_projection(
|
|
mode="shadow",
|
|
# Production is deliberately scoped to this invocation; the
|
|
# module-level default allowlist remains test-only.
|
|
allowed_databases=frozenset({"clientflow"}),
|
|
derive_expected_database="clientflow",
|
|
derive_expected_user=user,
|
|
expected_opportunity_count=None,
|
|
require_opportunities=True,
|
|
)
|
|
else:
|
|
result = rebuild_blif_flow_v2_projection()
|
|
print(json.dumps(result, indent=2, sort_keys=True))
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|