"""CLI for ordinary bhavcopy backups and disjoint restoration. Authority backups and all active-workspace restores fail with coordinated_restore_unavailable until the coordinated transaction is available. Usage:: python -m scripts.backup create --output ~/flint-backup.tar.gz python -m scripts.backup create --output ~/flint-backup.tar.gz --include-ticks python -m scripts.backup create --output ~/flint-backup.tar.gz --include-credentials python -m scripts.backup restore --input ~/flint-backup.tar.gz python -m scripts.backup restore --input ~/flint-backup.tar.gz --target /tmp/restore --force python -m scripts.backup list --dir ~/flint-backups/ """ from __future__ import annotations import argparse import json import sys from pathlib import Path def _cmd_create(args: argparse.Namespace) -> None: """Handle the ``create`` sub-command.""" from flinttrade_core.backup import BackupError, WorkspaceBackup # noqa: PLC0415 bk = WorkspaceBackup() output = Path(args.output).expanduser() try: created = bk.create_backup( output, include_ticks=args.include_ticks, include_credentials=args.include_credentials, ) size_mb = round(created.stat().st_size / (1024 * 1024), 3) print(f"Backup created: {created} ({size_mb} MB)") except BackupError as exc: print(f"Error: {exc.message}", file=sys.stderr) sys.exit(1) def _cmd_restore(args: argparse.Namespace) -> None: """Handle the ``restore`` sub-command.""" from flinttrade_core.backup import BackupError, WorkspaceBackup # noqa: PLC0415 bk = WorkspaceBackup() input_path = Path(args.input).expanduser() target_dir = Path(args.target).expanduser() if args.target else None try: result = bk.restore_backup( input_path, target_dir=target_dir, force=args.force ) print( f"Restored: {result['files_restored']} files " f"({result['total_size_mb']} MB), " f"{result['dbs_restored']} databases" ) except BackupError as exc: print(f"Error: {exc.message}", file=sys.stderr) sys.exit(1) def _cmd_list(args: argparse.Namespace) -> None: """Handle the ``list`` sub-command.""" from flinttrade_core.backup import WorkspaceBackup # noqa: PLC0415 bk = WorkspaceBackup() backup_dir = ( Path(args.dir).expanduser() if args.dir else Path.home() / "flint-backups" ) backups = bk.list_backups(backup_dir) if not backups: print(f"No backups found in {backup_dir}") return for b in backups: if args.json: print(json.dumps(b, default=str)) else: created = b.get("created_at", "unknown") size = b.get("size_mb", "?") files = b.get("file_count", "?") print(f"{b['filename']} {size} MB {files} files created={created}") def main() -> None: """Entry point for the backup CLI.""" parser = argparse.ArgumentParser( prog="python -m scripts.backup", description="FlintTrade workspace backup and restore", ) sub = parser.add_subparsers(dest="command", required=True) # create create_p = sub.add_parser("create", help="Create a new workspace backup") create_p.add_argument( "--output", required=True, help="Destination path for the .tar.gz archive", ) create_p.add_argument( "--include-ticks", action="store_true", default=False, help="Request tick stores; unclassified live stores remain unavailable", ) create_p.add_argument( "--include-credentials", action="store_true", default=False, help=( "Reserved option: credential backup is temporarily unavailable " "(coordinated_restore_unavailable)." ), ) # restore restore_p = sub.add_parser("restore", help="Restore a workspace backup") restore_p.add_argument( "--input", required=True, help="Path to the .tar.gz archive to restore", ) restore_p.add_argument( "--target", default=None, help="Target directory (default: parent of workspace dir)", ) restore_p.add_argument( "--force", action="store_true", default=False, help="Overwrite existing files without error", ) # list list_p = sub.add_parser("list", help="List available backup archives") list_p.add_argument( "--dir", default=None, help="Directory to search (default: ~/flint-backups/)", ) list_p.add_argument( "--json", action="store_true", default=False, help="Output as JSON lines", ) args = parser.parse_args() if args.command == "create": _cmd_create(args) elif args.command == "restore": _cmd_restore(args) elif args.command == "list": _cmd_list(args) if __name__ == "__main__": main()