From 0fcef9914b5275243f83b492ec7ca847a71f2773 Mon Sep 17 00:00:00 2001 From: samuel-w Date: Mon, 20 Jul 2020 03:34:23 -0500 Subject: [PATCH 01/12] Add command line backup creation --- src/vorta/application.py | 33 ++++++++++++++++++++++++++++++--- src/vorta/borg/create.py | 1 + src/vorta/utils.py | 9 ++++++++- 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/src/vorta/application.py b/src/vorta/application.py index 6dd8cf9b6..58001f35c 100644 --- a/src/vorta/application.py +++ b/src/vorta/application.py @@ -4,6 +4,7 @@ from PyQt5 import QtCore from PyQt5.QtWidgets import QMessageBox +from vorta.borg.borg_thread import BorgThread from vorta.borg.create import BorgCreateThread from vorta.borg.version import BorgVersionThread from vorta.config import TEMP_DIR @@ -48,12 +49,29 @@ def __init__(self, args_raw, single_app=False): # Prepare system tray icon self.tray = TrayMenu(self) - args = parse_args() - if getattr(args, 'daemonize', False): + self.args = parse_args() + if getattr(self.args, 'daemonize', False) or getattr(self.args, 'create', False): pass elif SettingsModel.get(key='foreground').value: self.open_main_window_action() + if getattr(self.args, 'create', False): + self.completedProfiles = [] + print(self.args.profiles) + if self.args.profiles: + for profile_name in self.args.profiles: + print(profile_name) + profile = BackupProfileModel.get_or_none(name=profile_name) + if profile is not None: + # Wait a bit in case something is running + while BorgThread.is_running(): + time.sleep(0.1) + self.create_backup_action(profile_id=profile.id, from_cmdline=True) + else: + print(f"Invalid profile name {profile_name}") + else: + print("Test") + self.backup_started_event.connect(self.backup_started_event_response) self.backup_finished_event.connect(self.backup_finished_event_response) self.backup_cancelled_event.connect(self.backup_cancelled_event_response) @@ -61,6 +79,13 @@ def __init__(self, args_raw, single_app=False): self.set_borg_details_action() self.installEventFilter(self) + def exit_checker(self, result): + """Exit when all profiles have been run""" + self.completedProfiles.append(result['params']['profile_name']) + if self.args.profiles == self.completedProfiles: + os._exit(0) + + def eventFilter(self, source, event): if event.type() == QtCore.QEvent.ApplicationPaletteChange and type(source) == MainWindow: self.main_window.set_icons() @@ -71,7 +96,7 @@ def eventFilter(self, source, event): self.tray.set_tray_icon() return False - def create_backup_action(self, profile_id=None): + def create_backup_action(self, profile_id=None, from_cmdline=False): if not profile_id: profile_id = self.main_window.current_profile.id @@ -79,6 +104,8 @@ def create_backup_action(self, profile_id=None): msg = BorgCreateThread.prepare(profile) if msg['ok']: thread = BorgCreateThread(msg['cmd'], msg, parent=self) + if from_cmdline: + thread.result.connect(self.exit_checker) thread.start() else: notifier = VortaNotifications.pick() diff --git a/src/vorta/borg/create.py b/src/vorta/borg/create.py index 82250713e..b79b93fef 100644 --- a/src/vorta/borg/create.py +++ b/src/vorta/borg/create.py @@ -43,6 +43,7 @@ def started_event(self): def finished_event(self, result): self.app.backup_finished_event.emit(result) + self.result.emit(result) self.pre_post_backup_cmd(self.params, cmd='post_backup_cmd', returncode=result['returncode']) @classmethod diff --git a/src/vorta/utils.py b/src/vorta/utils.py index 121578344..eb916fcd9 100644 --- a/src/vorta/utils.py +++ b/src/vorta/utils.py @@ -96,7 +96,7 @@ def get_private_keys(): def pretty_bytes(size): """from https://stackoverflow.com/questions/12523586/ python-format-size-application-converting-b-to-kb-mb-gb-tb/37423778""" - if type(size) != int: + if not isinstance(size, int): return '' power = 1000 # GiB is base 2**10, GB is base 10**3. n = 0 @@ -192,6 +192,13 @@ def parse_args(): parser.add_argument('--daemonize', '-d', action='store_true', help="Fork to background and don't open window on startup.") + parser.add_argument('--create', + action='store_true', + help='Create a backup using the given profile or profiles') + parser.add_argument('--profile', '-p', + dest='profiles', + action='append', + help='One or more profile names') return parser.parse_known_args()[0] From 3bad83bd23669315217042b935bf8ceffda99cd1 Mon Sep 17 00:00:00 2001 From: samuel-w Date: Mon, 20 Jul 2020 03:37:31 -0500 Subject: [PATCH 02/12] Exit check against valid profiles only --- src/vorta/application.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vorta/application.py b/src/vorta/application.py index 58001f35c..99063c711 100644 --- a/src/vorta/application.py +++ b/src/vorta/application.py @@ -57,12 +57,14 @@ def __init__(self, args_raw, single_app=False): if getattr(self.args, 'create', False): self.completedProfiles = [] + self.validProfiles = [] print(self.args.profiles) if self.args.profiles: for profile_name in self.args.profiles: print(profile_name) profile = BackupProfileModel.get_or_none(name=profile_name) if profile is not None: + self.validProfiles.append(profile_name) # Wait a bit in case something is running while BorgThread.is_running(): time.sleep(0.1) @@ -82,7 +84,7 @@ def __init__(self, args_raw, single_app=False): def exit_checker(self, result): """Exit when all profiles have been run""" self.completedProfiles.append(result['params']['profile_name']) - if self.args.profiles == self.completedProfiles: + if self.validProfiles == self.completedProfiles: os._exit(0) From 1cfefb96235b6dbcbe110479b6001be6e17caebb Mon Sep 17 00:00:00 2001 From: samuel-w Date: Mon, 20 Jul 2020 15:48:51 -0500 Subject: [PATCH 03/12] Simplify command line syntax --- src/vorta/application.py | 30 +++++++++++++----------------- src/vorta/utils.py | 13 ++++++++----- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/src/vorta/application.py b/src/vorta/application.py index 99063c711..238fc4a4b 100644 --- a/src/vorta/application.py +++ b/src/vorta/application.py @@ -50,29 +50,25 @@ def __init__(self, args_raw, single_app=False): self.tray = TrayMenu(self) self.args = parse_args() - if getattr(self.args, 'daemonize', False) or getattr(self.args, 'create', False): + if getattr(self.args, 'daemonize', False) or self.args.profiles: pass elif SettingsModel.get(key='foreground').value: self.open_main_window_action() - if getattr(self.args, 'create', False): + if self.args.profiles: self.completedProfiles = [] self.validProfiles = [] - print(self.args.profiles) - if self.args.profiles: - for profile_name in self.args.profiles: - print(profile_name) - profile = BackupProfileModel.get_or_none(name=profile_name) - if profile is not None: - self.validProfiles.append(profile_name) - # Wait a bit in case something is running - while BorgThread.is_running(): - time.sleep(0.1) - self.create_backup_action(profile_id=profile.id, from_cmdline=True) - else: - print(f"Invalid profile name {profile_name}") - else: - print("Test") + for profile_name in self.args.profiles: + print(profile_name) + profile = BackupProfileModel.get_or_none(name=profile_name) + if profile is not None: + self.validProfiles.append(profile_name) + # Wait a bit in case something is running + while BorgThread.is_running(): + time.sleep(0.1) + self.create_backup_action(profile_id=profile.id, from_cmdline=True) + else: + print(f"Invalid profile name {profile_name}") self.backup_started_event.connect(self.backup_started_event_response) self.backup_finished_event.connect(self.backup_finished_event_response) diff --git a/src/vorta/utils.py b/src/vorta/utils.py index eb916fcd9..c6ddd31b0 100644 --- a/src/vorta/utils.py +++ b/src/vorta/utils.py @@ -193,14 +193,17 @@ def parse_args(): action='store_true', help="Fork to background and don't open window on startup.") parser.add_argument('--create', - action='store_true', - help='Create a backup using the given profile or profiles') - parser.add_argument('--profile', '-p', + nargs='?', dest='profiles', action='append', - help='One or more profile names') + help='Create a backup using the given profile or profiles') - return parser.parse_known_args()[0] + args = parser.parse_known_args()[0] + + if args.profiles == [None]: + parser.error("Include one or more profile names") + else: + return args def slugify(value): From cff01e018b21e37789352e4a295e70d837e4fc6d Mon Sep 17 00:00:00 2001 From: samuel-w Date: Tue, 21 Jul 2020 14:17:50 -0500 Subject: [PATCH 04/12] Change syntax, use logger instead of print --- src/vorta/application.py | 14 +++++++++----- src/vorta/utils.py | 12 ++++-------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/vorta/application.py b/src/vorta/application.py index 238fc4a4b..6a64e86ee 100644 --- a/src/vorta/application.py +++ b/src/vorta/application.py @@ -1,6 +1,9 @@ +import logging import os import sys import sip +import time + from PyQt5 import QtCore from PyQt5.QtWidgets import QMessageBox @@ -17,6 +20,8 @@ from vorta.views.main_window import MainWindow from vorta.notifications import VortaNotifications +logger = logging.getLogger(__name__) + APP_ID = os.path.join(TEMP_DIR, "socket") @@ -50,16 +55,15 @@ def __init__(self, args_raw, single_app=False): self.tray = TrayMenu(self) self.args = parse_args() - if getattr(self.args, 'daemonize', False) or self.args.profiles: + if getattr(self.args, 'daemonize', False) or self.args.profile: pass elif SettingsModel.get(key='foreground').value: self.open_main_window_action() - if self.args.profiles: + if self.args.profile: self.completedProfiles = [] self.validProfiles = [] - for profile_name in self.args.profiles: - print(profile_name) + for profile_name in self.args.profile: profile = BackupProfileModel.get_or_none(name=profile_name) if profile is not None: self.validProfiles.append(profile_name) @@ -68,7 +72,7 @@ def __init__(self, args_raw, single_app=False): time.sleep(0.1) self.create_backup_action(profile_id=profile.id, from_cmdline=True) else: - print(f"Invalid profile name {profile_name}") + logger.warning(f"Invalid profile name {profile_name}") self.backup_started_event.connect(self.backup_started_event_response) self.backup_finished_event.connect(self.backup_finished_event_response) diff --git a/src/vorta/utils.py b/src/vorta/utils.py index c6ddd31b0..2f88e7628 100644 --- a/src/vorta/utils.py +++ b/src/vorta/utils.py @@ -193,17 +193,13 @@ def parse_args(): action='store_true', help="Fork to background and don't open window on startup.") parser.add_argument('--create', - nargs='?', - dest='profiles', - action='append', - help='Create a backup using the given profile or profiles') + nargs='+', + dest='profile', + help='Create a backup in the background using the given profile(s). Will automatically close once complete') args = parser.parse_known_args()[0] - if args.profiles == [None]: - parser.error("Include one or more profile names") - else: - return args + return args def slugify(value): From 80a2b4ab8fa40f149ceab6af10180f69354bd59a Mon Sep 17 00:00:00 2001 From: samuel-w Date: Tue, 21 Jul 2020 14:20:24 -0500 Subject: [PATCH 05/12] Lint --- src/vorta/application.py | 3 +-- src/vorta/utils.py | 9 +++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/vorta/application.py b/src/vorta/application.py index 6a64e86ee..2b7befa6a 100644 --- a/src/vorta/application.py +++ b/src/vorta/application.py @@ -87,9 +87,8 @@ def exit_checker(self, result): if self.validProfiles == self.completedProfiles: os._exit(0) - def eventFilter(self, source, event): - if event.type() == QtCore.QEvent.ApplicationPaletteChange and type(source) == MainWindow: + if event.type() == QtCore.QEvent.ApplicationPaletteChange and isinstance(source, MainWindow): self.main_window.set_icons() self.main_window.repoTab.set_icons() self.main_window.archiveTab.set_icons() diff --git a/src/vorta/utils.py b/src/vorta/utils.py index 2f88e7628..ea29d8853 100644 --- a/src/vorta/utils.py +++ b/src/vorta/utils.py @@ -192,10 +192,11 @@ def parse_args(): parser.add_argument('--daemonize', '-d', action='store_true', help="Fork to background and don't open window on startup.") - parser.add_argument('--create', - nargs='+', - dest='profile', - help='Create a backup in the background using the given profile(s). Will automatically close once complete') + parser.add_argument( + '--create', + nargs='+', + dest='profile', + help='Create a backup in the background using the given profile(s). Will automatically close once complete') args = parser.parse_known_args()[0] From cd8a35da243237a20cb2c86b2708658a0836677d Mon Sep 17 00:00:00 2001 From: samuel-w Date: Wed, 22 Jul 2020 00:37:21 -0500 Subject: [PATCH 06/12] Print profile name when completed --- src/vorta/application.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vorta/application.py b/src/vorta/application.py index 2b7befa6a..77df2e914 100644 --- a/src/vorta/application.py +++ b/src/vorta/application.py @@ -83,7 +83,9 @@ def __init__(self, args_raw, single_app=False): def exit_checker(self, result): """Exit when all profiles have been run""" - self.completedProfiles.append(result['params']['profile_name']) + profile_name = result['params']['profile_name'] + logger.info(f"Backup complete for {profile_name}") + self.completedProfiles.append(profile_name) if self.validProfiles == self.completedProfiles: os._exit(0) From ee57fdaf81f8804d0e06027acfdb3988be8420e8 Mon Sep 17 00:00:00 2001 From: samuel-w Date: Sat, 25 Jul 2020 22:20:10 -0500 Subject: [PATCH 07/12] Remove unneeded variable --- src/vorta/utils.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/vorta/utils.py b/src/vorta/utils.py index ea29d8853..b13fc89f7 100644 --- a/src/vorta/utils.py +++ b/src/vorta/utils.py @@ -198,9 +198,7 @@ def parse_args(): dest='profile', help='Create a backup in the background using the given profile(s). Will automatically close once complete') - args = parser.parse_known_args()[0] - - return args + return parser.parse_known_args()[0] def slugify(value): From 35ad8a2e233356da72e35276671b52b287bb8e15 Mon Sep 17 00:00:00 2001 From: samuel-w Date: Sat, 25 Jul 2020 22:29:53 -0500 Subject: [PATCH 08/12] Make args not self --- src/vorta/application.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vorta/application.py b/src/vorta/application.py index 77df2e914..9d371321b 100644 --- a/src/vorta/application.py +++ b/src/vorta/application.py @@ -54,16 +54,16 @@ def __init__(self, args_raw, single_app=False): # Prepare system tray icon self.tray = TrayMenu(self) - self.args = parse_args() - if getattr(self.args, 'daemonize', False) or self.args.profile: + args = parse_args() + if getattr(args, 'daemonize', False) or args.profile: pass elif SettingsModel.get(key='foreground').value: self.open_main_window_action() - if self.args.profile: + if args.profile: self.completedProfiles = [] self.validProfiles = [] - for profile_name in self.args.profile: + for profile_name in args.profile: profile = BackupProfileModel.get_or_none(name=profile_name) if profile is not None: self.validProfiles.append(profile_name) From da34c30450c5be4ce7b527bd1fd3b2c337c8c66a Mon Sep 17 00:00:00 2001 From: samuel-w Date: Sat, 25 Jul 2020 22:37:24 -0500 Subject: [PATCH 09/12] Prevent backing up with no repo --- src/vorta/application.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vorta/application.py b/src/vorta/application.py index 9d371321b..e119e333e 100644 --- a/src/vorta/application.py +++ b/src/vorta/application.py @@ -66,6 +66,9 @@ def __init__(self, args_raw, single_app=False): for profile_name in args.profile: profile = BackupProfileModel.get_or_none(name=profile_name) if profile is not None: + if profile.repo is None: + logger.warning(f"Add a repository to {profile_name}") + continue self.validProfiles.append(profile_name) # Wait a bit in case something is running while BorgThread.is_running(): From 36e4f7f2e4580033993c2ee1654204171815bc59 Mon Sep 17 00:00:00 2001 From: samuel-w Date: Mon, 12 Oct 2020 02:10:15 -0500 Subject: [PATCH 10/12] Grammar --- src/vorta/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vorta/utils.py b/src/vorta/utils.py index 1be709544..c8edc8fbc 100644 --- a/src/vorta/utils.py +++ b/src/vorta/utils.py @@ -171,7 +171,7 @@ def parse_args(): '--create', nargs='+', dest='profile', - help='Create a backup in the background using the given profile(s). Will automatically close once complete') + help='Create a backup in the background using the given profile(s). Will automatically close once complete.') return parser.parse_known_args()[0] From d864a4f7434ff295581f6b269c1768085fdd693a Mon Sep 17 00:00:00 2001 From: samuel-w Date: Sat, 21 Nov 2020 15:52:35 -0600 Subject: [PATCH 11/12] Run backups on exisiting thread. Does not exit when creating new Vorta instance. --- src/vorta/__main__.py | 2 +- src/vorta/application.py | 70 ++++++++++++++++++++++------------------ src/vorta/utils.py | 2 +- 3 files changed, 40 insertions(+), 34 deletions(-) diff --git a/src/vorta/__main__.py b/src/vorta/__main__.py index 11af719c2..f768a127e 100644 --- a/src/vorta/__main__.py +++ b/src/vorta/__main__.py @@ -34,7 +34,7 @@ def main(): # Init app after database is available from vorta.application import VortaApp - app = VortaApp(sys.argv, single_app=True) + app = VortaApp(sys.argv, single_app=args.profile is None) app.updater = get_updater() sys.exit(app.exec_()) diff --git a/src/vorta/application.py b/src/vorta/application.py index 00321628f..fa899d304 100644 --- a/src/vorta/application.py +++ b/src/vorta/application.py @@ -2,6 +2,7 @@ import os import sys import time +import ast from PyQt5 import QtCore from PyQt5.QtWidgets import QMessageBox @@ -41,10 +42,16 @@ class VortaApp(QtSingleApplication): def __init__(self, args_raw, single_app=False): super().__init__(APP_ID, args_raw) - if self.isRunning() and single_app: - self.sendMessage("open main window") - print('An instance of Vorta is already running. Opening main window.') - sys.exit() + args = parse_args() + if self.isRunning(): + if single_app: + self.sendMessage("open main window") + print('An instance of Vorta is already running. Opening main window.') + sys.exit() + elif args.profile: + self.sendMessage(f"create {args.profile}") + print('Creating backups using existing Vorta instance.') + sys.exit() init_translations(self) @@ -56,28 +63,13 @@ def __init__(self, args_raw, single_app=False): self.tray = TrayMenu(self) self.main_window = MainWindow(self) - args = parse_args() - if getattr(args, 'daemonize', False) or args.profile: + if getattr(args, 'daemonize', False): pass elif SettingsModel.get(key='foreground').value: self.open_main_window_action() - if args.profile: - self.completedProfiles = [] - self.validProfiles = [] - for profile_name in args.profile: - profile = BackupProfileModel.get_or_none(name=profile_name) - if profile is not None: - if profile.repo is None: - logger.warning(f"Add a repository to {profile_name}") - continue - self.validProfiles.append(profile_name) - # Wait a bit in case something is running - while BorgThread.is_running(): - time.sleep(0.1) - self.create_backup_action(profile_id=profile.id, from_cmdline=True) - else: - logger.warning(f"Invalid profile name {profile_name}") + if args.profile is not None: + self.create_backups_cmdline(args.profile) self.backup_started_event.connect(self.backup_started_event_response) self.backup_finished_event.connect(self.backup_finished_event_response) @@ -86,13 +78,22 @@ def __init__(self, args_raw, single_app=False): self.set_borg_details_action() self.installEventFilter(self) - def exit_checker(self, result): - """Exit when all profiles have been run""" - profile_name = result['params']['profile_name'] - logger.info(f"Backup complete for {profile_name}") - self.completedProfiles.append(profile_name) - if self.validProfiles == self.completedProfiles: - os._exit(0) + def create_backups_cmdline(self, profiles): + self.completedProfiles = [] + self.validProfiles = [] + for profile_name in profiles: + profile = BackupProfileModel.get_or_none(name=profile_name) + if profile is not None: + if profile.repo is None: + logger.warning(f"Add a repository to {profile_name}") + continue + self.validProfiles.append(profile_name) + # Wait a bit in case something is running + while BorgThread.is_running(): + time.sleep(0.1) + self.create_backup_action(profile_id=profile.id) + else: + logger.warning(f"Invalid profile name {profile_name}") def eventFilter(self, source, event): if event.type() == QtCore.QEvent.ApplicationPaletteChange and isinstance(source, MainWindow): @@ -104,7 +105,7 @@ def eventFilter(self, source, event): self.tray.set_tray_icon() return False - def create_backup_action(self, profile_id=None, from_cmdline=False): + def create_backup_action(self, profile_id=None): if not profile_id: profile_id = self.main_window.current_profile.id @@ -112,8 +113,6 @@ def create_backup_action(self, profile_id=None, from_cmdline=False): msg = BorgCreateThread.prepare(profile) if msg['ok']: thread = BorgCreateThread(msg['cmd'], msg, parent=self) - if from_cmdline: - thread.result.connect(self.exit_checker) thread.start() else: notifier = VortaNotifications.pick() @@ -143,6 +142,13 @@ def backup_cancelled_event_response(self): def message_received_event_response(self, message): if message == "open main window": self.open_main_window_action() + elif message.startswith("create"): + message = message[7:] # Remove create + profiles = ast.literal_eval(message) # Safely parse string array + if BorgThread.is_running(): + logger.warning("Cannot run while backups are already running") + else: + self.create_backups_cmdline(profiles) def set_borg_details_action(self): params = BorgVersionThread.prepare() diff --git a/src/vorta/utils.py b/src/vorta/utils.py index 73e9bda71..a594b4eae 100644 --- a/src/vorta/utils.py +++ b/src/vorta/utils.py @@ -241,7 +241,7 @@ def parse_args(): '--create', nargs='+', dest='profile', - help='Create a backup in the background using the given profile(s). Will automatically close once complete.') + help='Create a backup in the background using the given profile(s).') return parser.parse_known_args()[0] From b4fd9c483fd8c44e069e6a03ce243bb245067126 Mon Sep 17 00:00:00 2001 From: samuel-w Date: Tue, 24 Nov 2020 02:05:37 -0600 Subject: [PATCH 12/12] Make it require running instance --- src/vorta/application.py | 5 ++--- src/vorta/utils.py | 3 ++- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vorta/application.py b/src/vorta/application.py index fa899d304..cd4d09478 100644 --- a/src/vorta/application.py +++ b/src/vorta/application.py @@ -52,6 +52,8 @@ def __init__(self, args_raw, single_app=False): self.sendMessage(f"create {args.profile}") print('Creating backups using existing Vorta instance.') sys.exit() + elif args.profile: + sys.exit('Vorta must already be running for --create to work') init_translations(self) @@ -68,9 +70,6 @@ def __init__(self, args_raw, single_app=False): elif SettingsModel.get(key='foreground').value: self.open_main_window_action() - if args.profile is not None: - self.create_backups_cmdline(args.profile) - self.backup_started_event.connect(self.backup_started_event_response) self.backup_finished_event.connect(self.backup_finished_event_response) self.backup_cancelled_event.connect(self.backup_cancelled_event_response) diff --git a/src/vorta/utils.py b/src/vorta/utils.py index a594b4eae..eaa940f5c 100644 --- a/src/vorta/utils.py +++ b/src/vorta/utils.py @@ -241,7 +241,8 @@ def parse_args(): '--create', nargs='+', dest='profile', - help='Create a backup in the background using the given profile(s).') + help='Create a backup in the background using the given profile(s). ' + 'Vorta must already be running for this to work.') return parser.parse_known_args()[0]