diff --git a/frappe/exceptions.py b/frappe/exceptions.py index 267f5410af..9b63b23787 100644 --- a/frappe/exceptions.py +++ b/frappe/exceptions.py @@ -110,4 +110,5 @@ class DocumentAlreadyRestored(Exception): pass class InvalidAuthorizationHeader(CSRFTokenError): pass class InvalidAuthorizationPrefix(CSRFTokenError): pass class InvalidAuthorizationToken(CSRFTokenError): pass -class InvalidDatabaseFile(ValidationError): pass \ No newline at end of file +class InvalidDatabaseFile(ValidationError): pass +class ExecutableNotFound(FileNotFoundError): pass diff --git a/frappe/utils/backups.py b/frappe/utils/backups.py index b11c73c2b2..3ae300d3c4 100644 --- a/frappe/utils/backups.py +++ b/frappe/utils/backups.py @@ -2,11 +2,12 @@ # MIT License. See license.txt # imports - standard imports +import gzip import os from calendar import timegm from datetime import datetime from glob import glob -import gzip +from shutil import which # imports - third party imports import click @@ -14,7 +15,7 @@ import click # imports - module imports import frappe from frappe import _, conf -from frappe.utils import get_url, now, now_datetime, get_file_size +from frappe.utils import get_file_size, get_url, now, now_datetime # backup variable for backwards compatibility verbose = False @@ -344,6 +345,20 @@ class BackupGenerator: import frappe.utils from frappe.utils.change_log import get_app_branch + db_exc = { + "mariadb": ("mysqldump", which("mysqldump")), + "postgres": ("pg_dump", which("pg_dump")), + }[self.db_type] + gzip_exc = which("gzip") + + if not (gzip_exc and db_exc[1]): + _exc = "gzip" if not gzip_exc else db_exc[0] + frappe.throw( + f"{_exc} not found in PATH! This is required to take a backup.", + exc=frappe.ExecutableNotFound + ) + db_exc = db_exc[0] + database_header_content = [ f"Backup generated by Frappe {frappe.__version__} on branch {get_app_branch('frappe') or 'N/A'}", "", @@ -384,8 +399,8 @@ class BackupGenerator: ) cmd_string = ( - "pg_dump postgres://{user}:{password}@{db_host}:{db_port}/{db_name}" - " {include} {exclude} | gzip >> {backup_path_db}" + "{db_exc} postgres://{user}:{password}@{db_host}:{db_port}/{db_name}" + " {include} {exclude} | {gzip} >> {backup_path_db}" ) else: @@ -400,20 +415,22 @@ class BackupGenerator: ) cmd_string = ( - "mysqldump --single-transaction --quick --lock-tables=false -u {user}" + "{db_exc} --single-transaction --quick --lock-tables=false -u {user}" " -p{password} {db_name} -h {db_host} -P {db_port} {include} {exclude}" - " | gzip >> {backup_path_db}" + " | {gzip} >> {backup_path_db}" ) command = cmd_string.format( user=args.user, password=args.password, + db_exc=db_exc, db_host=args.db_host, db_port=args.db_port, db_name=args.db_name, backup_path_db=args.backup_path_db, exclude=args.get("exclude", ""), include=args.get("include", ""), + gzip=gzip_exc, ) if self.verbose: