7 Commits
3.2.0 ... 3.2.2

Author SHA1 Message Date
8b9087cdf6 Version bump 2023-05-11 20:21:00 +02:00
3c155e62a2 Version bump 2023-05-11 20:19:56 +02:00
5a4a26f9a7 Fix minor bugs and improve exception handling 2023-05-11 20:18:42 +02:00
ca0e3d133f Fix entry_point in setup.cfg 2023-05-05 19:41:32 +02:00
cc788735dd Fix README.md 2023-05-05 19:30:53 +02:00
631ffa85d3 Remove broken desktop notifications 2023-05-05 19:23:21 +02:00
fe2d66c24c Remove data_files from setup.cfg 2023-05-05 19:12:41 +02:00
5 changed files with 44 additions and 64 deletions

View File

@ -16,9 +16,8 @@ makedepends=('git'
'python-wheel') 'python-wheel')
depends=('python' depends=('python'
'rsync' 'rsync'
'python-dotenv' 'python-dotenv')
'python-dbus' optdepends=('python-systemd: use systemd log')
'python-systemd')
install=${pkgname}.install install=${pkgname}.install
source=(git+https://github.com/Fuxino/${pkgname}.git) source=(git+https://github.com/Fuxino/${pkgname}.git)
sha256sums=('SKIP') sha256sums=('SKIP')

View File

@ -4,7 +4,8 @@ A simple backup script
## Description ## Description
simple_backup is a Python script that allows you to backup your files. simple_backup is a Python script that allows you to backup your files.
Parameters like input files/directories, output directory etc. can be specified in a configuration file, or on the command line. Run: Parameters like input files/directories, output directory etc. can be specified in a configuration file, or on the command line.
Run:
```bash ```bash
simple_backup -h simple_backup -h
@ -26,12 +27,21 @@ To install the program, first clone the repository:
git clone https://github.com/Fuxino/simple_backup.git git clone https://github.com/Fuxino/simple_backup.git
``` ```
Install tools required to build and install the package:
```bash
pip install --upgrade build installer wheel
```
Then run: Then run:
```bash ```bash
cd simple_backup cd simple_backup
python -m build --wheel python -m build --wheel
python -m installer dist/*.whl python -m installer dist/*.whl
```
For Arch Linux, a PKGBUILD that automates this process is provided. For Arch Linux, a PKGBUILD that automates this process is provided.
After installing, copy simple_backup.conf (if you used the PKGBUILD on Arch, it will be in /etc/simple_backup/) to $HOME/.config/simple_backup and edit is as needed.

View File

@ -29,7 +29,5 @@ install_requires =
[options.entry_points] [options.entry_points]
console_scripts = console_scripts =
simple_backup = simple_backup:simple_backup simple_backup = simple_backup.simple_backup:simple_backup
[options.data_files]
../etc/simple_backup = simple_backup/simple_backup.conf

View File

@ -1,3 +1,3 @@
"""Init.""" """Init."""
__version__ = '3.1.2' __version__ = '3.2.2'

View File

@ -12,13 +12,12 @@ from timeit import default_timer
from subprocess import Popen, PIPE, STDOUT from subprocess import Popen, PIPE, STDOUT
from datetime import datetime from datetime import datetime
from tempfile import mkstemp from tempfile import mkstemp
import dbus
from dotenv import load_dotenv from dotenv import load_dotenv
try: try:
from systemd import journal from systemd import journal
except ImportError: except ImportError:
pass journal = None
load_dotenv() load_dotenv()
@ -39,14 +38,12 @@ c_format = logging.Formatter('%(name)s - %(levelname)s - %(message)s')
c_handler.setFormatter(c_format) c_handler.setFormatter(c_format)
logger.addHandler(c_handler) logger.addHandler(c_handler)
try: if journal:
j_handler = journal.JournalHandler() j_handler = journal.JournalHandler()
j_handler.setLevel(logging.INFO) j_handler.setLevel(logging.INFO)
j_format = logging.Formatter('%(levelname)s - %(message)s') j_format = logging.Formatter('%(levelname)s - %(message)s')
j_handler.setFormatter(j_format) j_handler.setFormatter(j_format)
logger.addHandler(j_handler) logger.addHandler(j_handler)
except NameError:
pass
def timing(_logger): def timing(_logger):
@ -92,17 +89,6 @@ class Backup:
return False return False
for i in self.inputs:
if os.path.islink(i):
try:
i_new = os.readlink(i)
logger.info(f'Input {i} is a symbolic link referencing {i_new}. Copying {i_new} instead')
self.inputs.remove(i)
self.inputs.append(i_new)
except Exception:
logger.warning(f'Input {i} is a link and cannot be read. Skipping')
self.inputs.remove(i)
if self.output is None: if self.output is None:
logger.critical('No output path specified. Use -o argument or specify output path in configuration file') logger.critical('No output path specified. Use -o argument or specify output path in configuration file')
@ -130,9 +116,7 @@ class Backup:
def remove_old_backups(self): def remove_old_backups(self):
try: try:
dirs = os.listdir(f'{self.output}/simple_backup') dirs = os.listdir(f'{self.output}/simple_backup')
except Exception: except FileNotFoundError:
logger.info('No older backups to remove')
return return
if dirs.count('last_backup') > 0: if dirs.count('last_backup') > 0:
@ -149,8 +133,10 @@ class Backup:
try: try:
rmtree(f'{self.output}/simple_backup/{dirs[i]}') rmtree(f'{self.output}/simple_backup/{dirs[i]}')
count += 1 count += 1
except Exception: except FileNotFoundError:
logger.error(f'Error while removing backup {dirs[i]}') logger.error(f'Error while removing backup {dirs[i]}. Directory not found')
except PermissionError:
logger.error(f'Error while removing backup {dirs[i]}. Permission denied')
if count == 1: if count == 1:
logger.info(f'Removed {count} backup') logger.info(f'Removed {count} backup')
@ -159,15 +145,14 @@ class Backup:
def find_last_backup(self): def find_last_backup(self):
if os.path.islink(f'{self.output}/simple_backup/last_backup'): if os.path.islink(f'{self.output}/simple_backup/last_backup'):
try: link = os.readlink(f'{self.output}/simple_backup/last_backup')
self._last_backup = os.readlink(f'{self.output}/simple_backup/last_backup')
except Exception: if os.path.isdir(link):
logger.warning('Previous backup could not be read') self._last_backup = link
else:
logger.info('No previous backups available')
else: else:
logger.info('No previous backups available') logger.info('No previous backups available')
if not os.path.isdir(self._last_backup):
logger.warning('Previous backup could not be read')
# Function to read configuration file # Function to read configuration file
@timing(logger) @timing(logger)
@ -180,8 +165,11 @@ class Backup:
if os.path.islink(f'{self.output}/simple_backup/last_backup'): if os.path.islink(f'{self.output}/simple_backup/last_backup'):
try: try:
os.remove(f'{self.output}/simple_backup/last_backup') os.remove(f'{self.output}/simple_backup/last_backup')
except Exception: except FileNotFoundError:
logger.error('Failed to remove last_backup link') logger.error('Failed to remove last_backup link. File not found')
self._err_flag = True
except PermissionError:
logger.error('Failed to remove last_backup link. Permission denied')
self._err_flag = True self._err_flag = True
_, self._inputs_path = mkstemp(prefix='tmp_inputs', text=True) _, self._inputs_path = mkstemp(prefix='tmp_inputs', text=True)
@ -220,9 +208,12 @@ class Backup:
logger.info(f'rsync: {output[-2]}') logger.info(f'rsync: {output[-2]}')
try: try:
os.symlink(self._output_dir, f'{self.output}/simple_backup/last_backup') os.symlink(self._output_dir, f'{self.output}/simple_backup/last_backup', target_is_directory=True)
except Exception: except FileExistsError:
logger.error('Failed to create last_backup link') logger.error('Failed to create last_backup link. Link already exists')
self._err_flag = True
except PermissionError:
logger.error('Failed to create last_backup link. Permission denied')
self._err_flag = True self._err_flag = True
if self.keep != -1: if self.keep != -1:
@ -236,10 +227,6 @@ class Backup:
if self._err_flag: if self._err_flag:
logger.warning('Some errors occurred (check log for details)') logger.warning('Some errors occurred (check log for details)')
return 1
return 0
def _parse_arguments(): def _parse_arguments():
parser = argparse.ArgumentParser(prog='simple_backup', parser = argparse.ArgumentParser(prog='simple_backup',
@ -304,21 +291,7 @@ def simple_backup():
backup = Backup(inputs, output, exclude, keep, backup_options) backup = Backup(inputs, output, exclude, keep, backup_options)
if backup.check_params(): if backup.check_params():
try: backup.run()
obj = dbus.SessionBus().get_object("org.freedesktop.Notifications", "/org/freedesktop/Notifications")
obj = dbus.Interface(obj, "org.freedesktop.Notifications")
obj.Notify("simple_backup", 0, "", "Starting backup...", "", [], {"urgency": 1}, 10000)
except dbus.exceptions.DBusException:
obj = None
status = backup.run()
if obj is not None:
if status == 0:
obj.Notify("simple_backup", 0, "", "Backup finished.", "", [], {"urgency": 1}, 10000)
else:
obj.Notify("simple_backup", 0, "", "Backup finished. Some errors occurred.",
"", [], {"urgency": 1}, 10000)
return 0 return 0