fix: allow guarded production Flow v2 shadow rebuild
This commit is contained in:
@@ -1,18 +1,84 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Rebuild additive BLIF Flow v2 projection tables in safe shadow mode."""
|
||||
"""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)
|
||||
|
||||
from app.blif_flow_v2_projection_service import rebuild_blif_flow_v2_projection
|
||||
|
||||
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__":
|
||||
print(json.dumps(rebuild_blif_flow_v2_projection(), indent=2, sort_keys=True))
|
||||
raise SystemExit(main())
|
||||
|
||||
Reference in New Issue
Block a user