Plain is headed towards 1.0! Subscribe for development updates →

  1import os
  2import time
  3from pathlib import Path
  4
  5import click
  6
  7from .core import DatabaseBackups
  8
  9
 10@click.group("backups")
 11def cli():
 12    """Local database backups"""
 13    pass
 14
 15
 16@cli.command("list")
 17def list_backups():
 18    backups_handler = DatabaseBackups()
 19    backups = backups_handler.find_backups()
 20    if not backups:
 21        click.secho("No backups found", fg="yellow")
 22        return
 23
 24    for backup in backups:
 25        click.secho(
 26            f"{backup.name} ({backup.updated_at().strftime('%Y-%m-%d %H:%M:%S')})",
 27            bold=True,
 28        )
 29
 30        for backup_file in backup.iter_files():
 31            size = os.path.getsize(backup_file)
 32            click.echo(f"- {backup_file.name} ({size / 1024 / 1024:.2f} MB)")
 33
 34        click.echo()
 35
 36
 37@cli.command("create")
 38@click.option("--pg-dump", default="pg_dump", envvar="PG_DUMP")
 39@click.argument("backup_name", default="")
 40def create_backup(backup_name, pg_dump):
 41    backups_handler = DatabaseBackups()
 42
 43    if not backup_name:
 44        backup_name = f"backup_{time.strftime('%Y%m%d_%H%M%S')}"
 45
 46    try:
 47        backup_dir = backups_handler.create(
 48            backup_name,
 49            pg_dump=pg_dump,
 50        )
 51    except Exception as e:
 52        click.secho(str(e), fg="red")
 53        exit(1)
 54
 55    click.secho(f"Backup created in {backup_dir.relative_to(Path.cwd())}", fg="green")
 56
 57
 58@cli.command("restore")
 59@click.option("--latest", is_flag=True)
 60@click.option("--pg-restore", default="pg_restore", envvar="PG_RESTORE")
 61@click.argument("backup_name", default="")
 62def restore_backup(backup_name, latest, pg_restore):
 63    backups_handler = DatabaseBackups()
 64
 65    if backup_name and latest:
 66        click.secho("Only one of --latest or backup_name is allowed", fg="red")
 67        exit(1)
 68
 69    if not backup_name and not latest:
 70        click.secho("Backup name or --latest is required", fg="red")
 71        exit(1)
 72
 73    if not backup_name and latest:
 74        backup_name = backups_handler.find_backups()[0].name
 75
 76    click.secho(f"Restoring backup {backup_name}...", bold=True)
 77
 78    try:
 79        backups_handler.restore(
 80            backup_name,
 81            pg_restore=pg_restore,
 82        )
 83    except Exception as e:
 84        click.secho(str(e), fg="red")
 85        exit(1)
 86    click.echo(f"Backup {backup_name} restored successfully.")
 87
 88
 89@cli.command("delete")
 90@click.argument("backup_name")
 91def delete_backup(backup_name):
 92    backups_handler = DatabaseBackups()
 93    try:
 94        backups_handler.delete(backup_name)
 95    except Exception as e:
 96        click.secho(str(e), fg="red")
 97        return
 98    click.secho(f"Backup {backup_name} deleted", fg="green")
 99
100
101@cli.command("clear")
102@click.confirmation_option(prompt="Are you sure you want to delete all backups?")
103def clear_backups():
104    backups_handler = DatabaseBackups()
105    backups = backups_handler.find_backups()
106    for backup in backups:
107        backup.delete()
108    click.secho("All backups deleted", fg="green")