dar-backup 0.6.19__tar.gz → 0.6.20.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {dar_backup-0.6.19/src/dar_backup → dar_backup-0.6.20.1}/Changelog.md +25 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/PKG-INFO +1 -1
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/doc/dev.md +1 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/pyproject.toml +1 -1
- {dar_backup-0.6.19 → dar_backup-0.6.20.1/src/dar_backup}/Changelog.md +25 -0
- dar_backup-0.6.20.1/src/dar_backup/__about__.py +1 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/src/dar_backup/cleanup.py +22 -22
- dar_backup-0.6.20.1/src/dar_backup/config_settings.py +129 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/src/dar_backup/dar-backup.conf +4 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/src/dar_backup/dar_backup.py +38 -32
- dar_backup-0.6.20.1/src/dar_backup/exceptions.py +3 -0
- dar_backup-0.6.20.1/src/dar_backup/installer.py +58 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/src/dar_backup/manager.py +54 -28
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/src/dar_backup/util.py +153 -14
- dar_backup-0.6.19/src/dar_backup/__about__.py +0 -1
- dar_backup-0.6.19/src/dar_backup/config_settings.py +0 -99
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/.gitignore +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/README.md +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/doc/doc.md +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/src/dar_backup/.darrc +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/src/dar_backup/README.md +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/src/dar_backup/__init__.py +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/src/dar_backup/clean_log.py +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/src/dar_backup/command_runner.py +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/src/dar_backup/dar_backup_systemd.py +0 -0
- /dar_backup-0.6.19/src/dar_backup/installer.py → /dar_backup-0.6.20.1/src/dar_backup/demo.py +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/src/dar_backup/rich_progress.py +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/DataProperty-1.1.0.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/SecretStorage-3.3.3.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/anyio-4.4.0.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/build-1.2.1.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/certifi-2024.7.4.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/cffi-1.16.0.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/chardet-5.2.0.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/charset_normalizer-3.3.2.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/cryptography-43.0.0.dist-info/license_files/LICENSE +0 -0
- {dar_backup-0.6.19/venv/lib/python3.12/site-packages/dar_backup-0.6.19.dist-info → dar_backup-0.6.20.1/venv/lib/python3.12/site-packages/dar_backup-0.6.20.1.dist-info}/licenses/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/filelock-3.15.4.dist-info/licenses/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/hyperlink-21.0.0.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/importlib_metadata-8.2.0.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/iniconfig-2.0.0.dist-info/licenses/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/jaraco.classes-3.4.0.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/jaraco.context-5.3.0.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/jaraco.functools-4.0.2.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/jeepney-0.8.0.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/keyring-25.3.0.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/markdown_it_py-3.0.0.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/mbstrdecoder-1.1.4.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/mdurl-0.1.2.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/more_itertools-10.3.0.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/packaging-24.1.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/pathspec-0.12.1.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/pathvalidate-3.2.3.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/pexpect-4.9.0.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/platformdirs-4.2.2.dist-info/licenses/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/pluggy-1.5.0.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/prettytable-3.16.0.dist-info/licenses/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/psutil-7.0.0.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/ptyprocess-0.7.0.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/pycparser-2.22.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/pygments-2.19.1.dist-info/licenses/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/pytablewriter-1.2.1.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/pytest-8.3.2.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/pytest_cov-6.0.0.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/pytest_timeout-2.3.1.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/python_dateutil-2.9.0.post0.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/python_slugify-8.0.4.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/readme_renderer-44.0.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/requests-2.32.3.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/requests_toolbelt-1.0.0.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/rfc3986-2.0.0.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/rich-14.0.0.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand-2.2.2.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/setuptools/_vendor/backports.tarfile-1.2.0.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata-8.0.0.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/setuptools/_vendor/inflect-7.3.1.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco.collections-5.1.0.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco.context-5.3.0.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco.functools-4.0.1.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco.text-3.12.1.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/setuptools/_vendor/more_itertools-10.3.0.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/setuptools/_vendor/packaging-24.2.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs-4.2.2.dist-info/licenses/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/setuptools/_vendor/tomli-2.0.1.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/setuptools/_vendor/typeguard-4.3.0.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/setuptools/_vendor/typing_extensions-4.12.2.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/vendored/packaging/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/setuptools/_vendor/zipp-3.19.2.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/setuptools-78.1.0.dist-info/licenses/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/shellingham-1.5.4.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/six-1.17.0.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/sniffio-1.3.1.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/tabledata-1.3.4.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/tcolorpy-0.1.7.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/tomli_w-1.0.0.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/tomlkit-0.13.2.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/trove_classifiers-2024.7.2.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/twine-6.0.1.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/typepy-1.3.4.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/virtualenv-20.26.3.dist-info/licenses/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/wcwidth-0.2.13.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/zipp-3.19.2.dist-info/LICENSE +0 -0
- {dar_backup-0.6.19 → dar_backup-0.6.20.1}/venv/lib/python3.12/site-packages/zstandard-0.23.0.dist-info/LICENSE +0 -0
|
@@ -1,6 +1,31 @@
|
|
|
1
1
|
<!-- markdownlint-disable MD024 -->
|
|
2
2
|
# dar-backup Changelog
|
|
3
3
|
|
|
4
|
+
## v2-beta-0.6.20.1 - 2025-05-04
|
|
5
|
+
|
|
6
|
+
Github link: [v2-beta-0.6.20.1](https://github.com/per2jensen/dar-backup/tree/v2-beta-0.6.20.1/v2)
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- FIX: bash/zsh completers fixed to support MANAGER_DB_DIR config if set
|
|
11
|
+
- `cleanup` and `manager` completer now sorts archives by \<backup-definition> and \<archive date> (so not using \<type>)
|
|
12
|
+
|
|
13
|
+
## v2-beta-0.6.20 - 2025-05-03
|
|
14
|
+
|
|
15
|
+
Github link: [v2-beta-0.6.20](https://github.com/per2jensen/dar-backup/tree/v2-beta-0.6.20/v2)
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
|
|
19
|
+
- show_version() moved to util and tests for dar-backup, manager and cleanup
|
|
20
|
+
- startup informational messages now works the same across the scripts
|
|
21
|
+
- Improved ConfigSettings class to handle optional configuration keys
|
|
22
|
+
|
|
23
|
+
-- test cases added
|
|
24
|
+
|
|
25
|
+
- Optional config parameter: MANAGER_DB_DIR, ideally to point to another disk for safe keeping backup catalogs
|
|
26
|
+
|
|
27
|
+
-- test cases added
|
|
28
|
+
|
|
4
29
|
## v2-beta-0.6.19 - 2025-04-21
|
|
5
30
|
|
|
6
31
|
Github link: [v2-beta-0.6.19](https://github.com/per2jensen/dar-backup/tree/v2-beta-0.6.19/v2)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dar-backup
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.20.1
|
|
4
4
|
Summary: A script to do full, differential and incremental backups using dar. Some files are restored from the backups during verification, after which par2 redundancy files are created. The script also has a cleanup feature to remove old backups and par2 files.
|
|
5
5
|
Project-URL: GPG Public Key, https://keys.openpgp.org/search?q=dar-backup@pm.me
|
|
6
6
|
Project-URL: Homepage, https://github.com/per2jensen/dar-backup/tree/main/v2
|
|
@@ -47,7 +47,7 @@ dar-backup = "dar_backup.dar_backup:main"
|
|
|
47
47
|
cleanup = "dar_backup.cleanup:main"
|
|
48
48
|
clean-log = "dar_backup.clean_log:main"
|
|
49
49
|
manager = "dar_backup.manager:main"
|
|
50
|
-
|
|
50
|
+
demo = "dar_backup.demo:main"
|
|
51
51
|
dar-backup-systemd = "dar_backup.dar_backup_systemd:main"
|
|
52
52
|
|
|
53
53
|
|
|
@@ -1,6 +1,31 @@
|
|
|
1
1
|
<!-- markdownlint-disable MD024 -->
|
|
2
2
|
# dar-backup Changelog
|
|
3
3
|
|
|
4
|
+
## v2-beta-0.6.20.1 - 2025-05-04
|
|
5
|
+
|
|
6
|
+
Github link: [v2-beta-0.6.20.1](https://github.com/per2jensen/dar-backup/tree/v2-beta-0.6.20.1/v2)
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- FIX: bash/zsh completers fixed to support MANAGER_DB_DIR config if set
|
|
11
|
+
- `cleanup` and `manager` completer now sorts archives by \<backup-definition> and \<archive date> (so not using \<type>)
|
|
12
|
+
|
|
13
|
+
## v2-beta-0.6.20 - 2025-05-03
|
|
14
|
+
|
|
15
|
+
Github link: [v2-beta-0.6.20](https://github.com/per2jensen/dar-backup/tree/v2-beta-0.6.20/v2)
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
|
|
19
|
+
- show_version() moved to util and tests for dar-backup, manager and cleanup
|
|
20
|
+
- startup informational messages now works the same across the scripts
|
|
21
|
+
- Improved ConfigSettings class to handle optional configuration keys
|
|
22
|
+
|
|
23
|
+
-- test cases added
|
|
24
|
+
|
|
25
|
+
- Optional config parameter: MANAGER_DB_DIR, ideally to point to another disk for safe keeping backup catalogs
|
|
26
|
+
|
|
27
|
+
-- test cases added
|
|
28
|
+
|
|
4
29
|
## v2-beta-0.6.19 - 2025-04-21
|
|
5
30
|
|
|
6
31
|
Github link: [v2-beta-0.6.19](https://github.com/per2jensen/dar-backup/tree/v2-beta-0.6.19/v2)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.6.20.1"
|
|
@@ -20,11 +20,15 @@ import os
|
|
|
20
20
|
import re
|
|
21
21
|
import subprocess
|
|
22
22
|
import sys
|
|
23
|
+
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")))
|
|
24
|
+
|
|
25
|
+
|
|
23
26
|
|
|
24
27
|
from datetime import datetime, timedelta
|
|
25
28
|
from inputimeout import inputimeout, TimeoutOccurred
|
|
26
29
|
from time import time
|
|
27
|
-
from typing import Dict, List, NamedTuple
|
|
30
|
+
from typing import Dict, List, NamedTuple, Tuple
|
|
31
|
+
|
|
28
32
|
|
|
29
33
|
from . import __about__ as about
|
|
30
34
|
from dar_backup.config_settings import ConfigSettings
|
|
@@ -32,6 +36,8 @@ from dar_backup.util import list_backups
|
|
|
32
36
|
from dar_backup.util import setup_logging
|
|
33
37
|
from dar_backup.util import get_logger
|
|
34
38
|
from dar_backup.util import requirements
|
|
39
|
+
from dar_backup.util import show_version
|
|
40
|
+
from dar_backup.util import print_aligned_settings
|
|
35
41
|
from dar_backup.util import backup_definition_completer, list_archive_completer
|
|
36
42
|
|
|
37
43
|
from dar_backup.command_runner import CommandRunner
|
|
@@ -152,21 +158,12 @@ def delete_catalog(catalog_name: str, args: NamedTuple) -> bool:
|
|
|
152
158
|
return False
|
|
153
159
|
|
|
154
160
|
|
|
155
|
-
def show_version():
|
|
156
|
-
script_name = os.path.basename(sys.argv[0])
|
|
157
|
-
print(f"{script_name} {about.__version__}")
|
|
158
|
-
print('''Licensed under GNU GENERAL PUBLIC LICENSE v3, see the supplied file "LICENSE" for details.
|
|
159
|
-
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW, not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
160
|
-
See section 15 and section 16 in the supplied "LICENSE" file.''')
|
|
161
|
-
|
|
162
|
-
|
|
163
161
|
def confirm_full_archive_deletion(archive_name: str, test_mode=False) -> bool:
|
|
164
162
|
try:
|
|
165
163
|
if test_mode:
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
print(f"Simulated confirmation for FULL archive: {confirmation}")
|
|
164
|
+
answer = os.getenv("CLEANUP_TEST_DELETE_FULL", "").lower()
|
|
165
|
+
print(f"Simulated confirmation for FULL archive '{archive_name}': {answer}")
|
|
166
|
+
return answer == "yes"
|
|
170
167
|
else:
|
|
171
168
|
confirmation = inputimeout(
|
|
172
169
|
prompt=f"Are you sure you want to delete the FULL archive '{archive_name}'? (yes/no): ",
|
|
@@ -220,21 +217,24 @@ def main():
|
|
|
220
217
|
command_logger = get_logger(command_output_logger = True)
|
|
221
218
|
runner = CommandRunner(logger=logger, command_logger=command_logger)
|
|
222
219
|
|
|
223
|
-
|
|
224
|
-
|
|
220
|
+
start_msgs: List[Tuple[str, str]] = []
|
|
221
|
+
|
|
222
|
+
start_msgs.append(("cleanup.py:", about.__version__))
|
|
225
223
|
|
|
226
224
|
logger.info(f"START TIME: {start_time}")
|
|
227
225
|
logger.debug(f"`args`:\n{args}")
|
|
228
226
|
logger.debug(f"`config_settings`:\n{config_settings}")
|
|
229
227
|
|
|
230
228
|
file_dir = os.path.normpath(os.path.dirname(__file__))
|
|
231
|
-
args.verbose and (
|
|
232
|
-
|
|
233
|
-
args.verbose and (
|
|
234
|
-
|
|
235
|
-
args.verbose and (
|
|
236
|
-
args.verbose and (
|
|
237
|
-
|
|
229
|
+
args.verbose and start_msgs.append(("Script directory:", file_dir))
|
|
230
|
+
start_msgs.append(("Config file:", args.config_file))
|
|
231
|
+
args.verbose and start_msgs.append(("Backup dir:", config_settings.backup_dir))
|
|
232
|
+
start_msgs.append(("Logfile:", config_settings.logfile_location))
|
|
233
|
+
args.verbose and start_msgs.append(("--alternate-archive-dir:", args.alternate_archive_dir))
|
|
234
|
+
args.verbose and start_msgs.append(("--cleanup-specific-archives:", args.cleanup_specific_archives))
|
|
235
|
+
|
|
236
|
+
dangerous_keywords = ["--cleanup", "_FULL_"] # TODO: add more dangerous keywords
|
|
237
|
+
print_aligned_settings(start_msgs, highlight_keywords=dangerous_keywords)
|
|
238
238
|
|
|
239
239
|
# run PREREQ scripts
|
|
240
240
|
requirements('PREREQ', config_settings)
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import configparser
|
|
2
|
+
from dataclasses import dataclass, field, fields
|
|
3
|
+
from os.path import expandvars, expanduser
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from dar_backup.exceptions import ConfigSettingsError
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class ConfigSettings:
|
|
10
|
+
"""
|
|
11
|
+
Parses and holds configuration values from a dar-backup.conf file.
|
|
12
|
+
|
|
13
|
+
Required fields are defined as dataclass attributes and must be present in the config file.
|
|
14
|
+
Optional fields can be declared in the OPTIONAL_CONFIG_FIELDS list. If a key is present in the
|
|
15
|
+
config file, the field is set to its parsed value; otherwise, it defaults to None.
|
|
16
|
+
|
|
17
|
+
The __repr__ method will only include optional fields if their value is not None,
|
|
18
|
+
keeping debug output clean and focused on explicitly configured values.
|
|
19
|
+
|
|
20
|
+
OPTIONAL_CONFIG_FIELDS = [
|
|
21
|
+
{
|
|
22
|
+
"section": "DIRECTORIES",
|
|
23
|
+
"key": "MANAGER_DB_DIR",
|
|
24
|
+
"attr": "manager_db_dir",
|
|
25
|
+
"type": str,
|
|
26
|
+
"default": None,
|
|
27
|
+
}
|
|
28
|
+
]
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
config_file: str
|
|
32
|
+
|
|
33
|
+
logfile_location: str = field(init=False)
|
|
34
|
+
max_size_verification_mb: int = field(init=False)
|
|
35
|
+
min_size_verification_mb: int = field(init=False)
|
|
36
|
+
no_files_verification: int = field(init=False)
|
|
37
|
+
command_timeout_secs: int = field(init=False)
|
|
38
|
+
backup_dir: str = field(init=False)
|
|
39
|
+
test_restore_dir: str = field(init=False)
|
|
40
|
+
backup_d_dir: str = field(init=False)
|
|
41
|
+
diff_age: int = field(init=False)
|
|
42
|
+
incr_age: int = field(init=False)
|
|
43
|
+
error_correction_percent: int = field(init=False)
|
|
44
|
+
par2_enabled: bool = field(init=False)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
OPTIONAL_CONFIG_FIELDS = [
|
|
48
|
+
{
|
|
49
|
+
"section": "DIRECTORIES",
|
|
50
|
+
"key": "MANAGER_DB_DIR",
|
|
51
|
+
"attr": "manager_db_dir",
|
|
52
|
+
"type": str,
|
|
53
|
+
"default": None,
|
|
54
|
+
},
|
|
55
|
+
# Add more optional fields here
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
def __post_init__(self):
|
|
59
|
+
if not self.config_file:
|
|
60
|
+
raise ConfigSettingsError("`config_file` must be specified.")
|
|
61
|
+
|
|
62
|
+
try:
|
|
63
|
+
self.config = configparser.ConfigParser()
|
|
64
|
+
loaded_files = self.config.read(self.config_file)
|
|
65
|
+
if not loaded_files:
|
|
66
|
+
raise RuntimeError(f"Configuration file not found or unreadable: '{self.config_file}'")
|
|
67
|
+
|
|
68
|
+
self.logfile_location = self.config['MISC']['LOGFILE_LOCATION']
|
|
69
|
+
self.max_size_verification_mb = int(self.config['MISC']['MAX_SIZE_VERIFICATION_MB'])
|
|
70
|
+
self.min_size_verification_mb = int(self.config['MISC']['MIN_SIZE_VERIFICATION_MB'])
|
|
71
|
+
self.no_files_verification = int(self.config['MISC']['NO_FILES_VERIFICATION'])
|
|
72
|
+
self.command_timeout_secs = int(self.config['MISC']['COMMAND_TIMEOUT_SECS'])
|
|
73
|
+
self.backup_dir = self.config['DIRECTORIES']['BACKUP_DIR']
|
|
74
|
+
self.test_restore_dir = self.config['DIRECTORIES']['TEST_RESTORE_DIR']
|
|
75
|
+
self.backup_d_dir = self.config['DIRECTORIES']['BACKUP.D_DIR']
|
|
76
|
+
self.diff_age = int(self.config['AGE']['DIFF_AGE'])
|
|
77
|
+
self.incr_age = int(self.config['AGE']['INCR_AGE'])
|
|
78
|
+
self.error_correction_percent = int(self.config['PAR2']['ERROR_CORRECTION_PERCENT'])
|
|
79
|
+
|
|
80
|
+
val = self.config['PAR2']['ENABLED'].strip().lower()
|
|
81
|
+
if val in ('true', '1', 'yes'):
|
|
82
|
+
self.par2_enabled = True
|
|
83
|
+
elif val in ('false', '0', 'no'):
|
|
84
|
+
self.par2_enabled = False
|
|
85
|
+
else:
|
|
86
|
+
raise ConfigSettingsError(f"Invalid boolean value for 'ENABLED' in [PAR2]: '{val}'")
|
|
87
|
+
|
|
88
|
+
# Load optional fields
|
|
89
|
+
for opt in self.OPTIONAL_CONFIG_FIELDS:
|
|
90
|
+
if self.config.has_option(opt['section'], opt['key']):
|
|
91
|
+
raw_value = self.config.get(opt['section'], opt['key'])
|
|
92
|
+
try:
|
|
93
|
+
value = opt['type'](raw_value.strip())
|
|
94
|
+
setattr(self, opt['attr'], value)
|
|
95
|
+
except Exception as e:
|
|
96
|
+
raise ConfigSettingsError(
|
|
97
|
+
f"Failed to parse optional config '{opt['section']}::{opt['key']}': {e}"
|
|
98
|
+
)
|
|
99
|
+
else:
|
|
100
|
+
setattr(self, opt['attr'], opt.get('default', None))
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
# Expand paths in all string fields that exist
|
|
104
|
+
for field in fields(self):
|
|
105
|
+
if hasattr(self, field.name):
|
|
106
|
+
value = getattr(self, field.name)
|
|
107
|
+
if isinstance(value, str):
|
|
108
|
+
setattr(self, field.name, expanduser(expandvars(value)))
|
|
109
|
+
|
|
110
|
+
except RuntimeError as e:
|
|
111
|
+
raise ConfigSettingsError(f"RuntimeError: {e}")
|
|
112
|
+
except KeyError as e:
|
|
113
|
+
raise ConfigSettingsError(f"Missing mandatory configuration key: {e}")
|
|
114
|
+
except ValueError as e:
|
|
115
|
+
raise ConfigSettingsError(f"Invalid value in config: {e}")
|
|
116
|
+
except Exception as e:
|
|
117
|
+
raise ConfigSettingsError(f"Unexpected error during config initialization: {e}")
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def __repr__(self):
|
|
122
|
+
safe_fields = [
|
|
123
|
+
f"{field.name}={getattr(self, field.name)!r}"
|
|
124
|
+
for field in fields(self)
|
|
125
|
+
if hasattr(self, field.name) and getattr(self, field.name) is not None
|
|
126
|
+
]
|
|
127
|
+
return f"<ConfigSettings({', '.join(safe_fields)})>"
|
|
128
|
+
|
|
129
|
+
|
|
@@ -16,8 +16,12 @@ COMMAND_TIMEOUT_SECS = 86400
|
|
|
16
16
|
BACKUP_DIR = ~/dar-backup/backups
|
|
17
17
|
BACKUP.D_DIR = ~/.config/dar-backup/backup.d/
|
|
18
18
|
TEST_RESTORE_DIR = ~/dar-backup/restore/
|
|
19
|
+
# Optional parameter
|
|
20
|
+
# If you want to store the catalog database away from the BACKUP_DIR, use the MANAGER_DB_DIR variable.
|
|
21
|
+
#MANAGER_DB_DIR = /some/where/else/
|
|
19
22
|
|
|
20
23
|
[AGE]
|
|
24
|
+
# DIFF and INCR backups are kept for a configured number of days, then deleted by the `cleanuo`
|
|
21
25
|
# age settings are in days
|
|
22
26
|
DIFF_AGE = 100
|
|
23
27
|
INCR_AGE = 40
|
|
@@ -37,8 +37,10 @@ from sys import stderr
|
|
|
37
37
|
from sys import argv
|
|
38
38
|
from sys import version_info
|
|
39
39
|
from time import time
|
|
40
|
+
from rich.console import Console
|
|
41
|
+
from rich.text import Text
|
|
40
42
|
from threading import Event
|
|
41
|
-
from typing import List
|
|
43
|
+
from typing import List, Tuple
|
|
42
44
|
|
|
43
45
|
from . import __about__ as about
|
|
44
46
|
from dar_backup.config_settings import ConfigSettings
|
|
@@ -48,7 +50,9 @@ from dar_backup.util import get_logger
|
|
|
48
50
|
from dar_backup.util import BackupError
|
|
49
51
|
from dar_backup.util import RestoreError
|
|
50
52
|
from dar_backup.util import requirements
|
|
53
|
+
from dar_backup.util import show_version
|
|
51
54
|
from dar_backup.util import get_binary_info
|
|
55
|
+
from dar_backup.util import print_aligned_settings
|
|
52
56
|
from dar_backup.util import backup_definition_completer, list_archive_completer
|
|
53
57
|
|
|
54
58
|
from dar_backup.command_runner import CommandRunner
|
|
@@ -647,14 +651,6 @@ def filter_darrc_file(darrc_path):
|
|
|
647
651
|
|
|
648
652
|
|
|
649
653
|
|
|
650
|
-
def show_version():
|
|
651
|
-
script_name = os.path.basename(argv[0])
|
|
652
|
-
print(f"{script_name} {about.__version__}")
|
|
653
|
-
print(f"dar-backup.py source code is here: https://github.com/per2jensen/dar-backup")
|
|
654
|
-
print('''Licensed under GNU GENERAL PUBLIC LICENSE v3, see the supplied file "LICENSE" for details.
|
|
655
|
-
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW, not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
656
|
-
See section 15 and section 16 in the supplied "LICENSE" file.''')
|
|
657
|
-
|
|
658
654
|
|
|
659
655
|
def show_examples():
|
|
660
656
|
examples = """
|
|
@@ -846,35 +842,40 @@ def main():
|
|
|
846
842
|
args.darrc = filter_darrc_file(args.darrc)
|
|
847
843
|
logger.debug(f"Filtered .darrc file: {args.darrc}")
|
|
848
844
|
|
|
845
|
+
start_msgs: List[Tuple[str, str]] = []
|
|
846
|
+
|
|
849
847
|
start_time=int(time())
|
|
850
|
-
|
|
851
|
-
logger.info(f"dar-backup.py started, version: {about.__version__}")
|
|
848
|
+
start_msgs.append(('dar-backup.py:', about.__version__))
|
|
852
849
|
logger.info(f"START TIME: {start_time}")
|
|
853
|
-
logger.debug(f"`
|
|
854
|
-
logger.debug(f"`
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
850
|
+
logger.debug(f"{'`Args`:\n'}{args}")
|
|
851
|
+
logger.debug(f"{'`Config_settings`:\n'}{config_settings}")
|
|
852
|
+
dar_properties = get_binary_info(command='dar')
|
|
853
|
+
start_msgs.append(('dar path:', dar_properties['path']))
|
|
854
|
+
start_msgs.append(('dar version:', dar_properties['version']))
|
|
858
855
|
|
|
859
856
|
file_dir = os.path.normpath(os.path.dirname(__file__))
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
args.verbose and args.
|
|
865
|
-
args.verbose and args.
|
|
857
|
+
start_msgs.append(('Script directory:', os.path.abspath(file_dir)))
|
|
858
|
+
start_msgs.append(('Config file:', os.path.abspath(args.config_file)))
|
|
859
|
+
start_msgs.append((".darrc location:", args.darrc))
|
|
860
|
+
|
|
861
|
+
args.verbose and args.full_backup and start_msgs.append(("Type of backup:", "FULL"))
|
|
862
|
+
args.verbose and args.differential_backup and start_msgs.append(("Type of backup:", "DIFF"))
|
|
863
|
+
args.verbose and args.incremental_backup and start_msgs.append(("Type of backup:", "INCR"))
|
|
864
|
+
args.verbose and args.backup_definition and start_msgs.append(("Backup definition:", args.backup_definition))
|
|
866
865
|
if args.alternate_reference_archive:
|
|
867
|
-
args.verbose and (
|
|
868
|
-
args.verbose and (
|
|
869
|
-
args.verbose and (
|
|
866
|
+
args.verbose and start_msgs.append(("Alternate ref archive:", args.alternate_reference_archive))
|
|
867
|
+
args.verbose and start_msgs.append(("Backup.d dir:", config_settings.backup_d_dir))
|
|
868
|
+
args.verbose and start_msgs.append(("Backup dir:", config_settings.backup_dir))
|
|
870
869
|
|
|
871
870
|
restore_dir = args.restore_dir if args.restore_dir else config_settings.test_restore_dir
|
|
872
|
-
args.verbose and (
|
|
871
|
+
args.verbose and start_msgs.append(("Restore dir:", restore_dir))
|
|
873
872
|
|
|
874
|
-
args.verbose and (
|
|
875
|
-
args.verbose and (
|
|
876
|
-
args.verbose and (
|
|
877
|
-
|
|
873
|
+
args.verbose and start_msgs.append(("Logfile location:", config_settings.logfile_location))
|
|
874
|
+
args.verbose and start_msgs.append(("PAR2 enabled:", config_settings.par2_enabled))
|
|
875
|
+
args.verbose and start_msgs.append(("--do-not-compare:", args.do_not_compare))
|
|
876
|
+
|
|
877
|
+
dangerous_keywords = ["--do-not", "alternate"] # TODO: add more dangerous keywords
|
|
878
|
+
print_aligned_settings(start_msgs)
|
|
878
879
|
|
|
879
880
|
# sanity check
|
|
880
881
|
if args.backup_definition and not os.path.exists(os.path.join(config_settings.backup_d_dir, args.backup_definition)):
|
|
@@ -936,12 +937,17 @@ def main():
|
|
|
936
937
|
else:
|
|
937
938
|
logger.error(f"not correct result type: {result}, which must be a tuple (<msg>, <exit_code>)")
|
|
938
939
|
i=i+1
|
|
940
|
+
|
|
941
|
+
console = Console()
|
|
939
942
|
if error:
|
|
940
|
-
args.verbose
|
|
943
|
+
if args.verbose:
|
|
944
|
+
console.print(Text("Errors encountered", style="bold red"))
|
|
941
945
|
exit(1)
|
|
942
946
|
else:
|
|
943
|
-
args.verbose
|
|
947
|
+
if args.verbose:
|
|
948
|
+
console.print(Text("Success: all backups completed", style="bold green"))
|
|
944
949
|
exit(0)
|
|
950
|
+
|
|
945
951
|
|
|
946
952
|
if __name__ == "__main__":
|
|
947
953
|
main()
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import os
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from dar_backup.config_settings import ConfigSettings
|
|
5
|
+
from dar_backup.util import setup_logging, get_logger
|
|
6
|
+
from dar_backup.command_runner import CommandRunner
|
|
7
|
+
from dar_backup.manager import create_db
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def run_installer(config_file: str, create_db_flag: bool):
|
|
11
|
+
config_file = os.path.expanduser(os.path.expandvars(config_file))
|
|
12
|
+
config_settings = ConfigSettings(config_file)
|
|
13
|
+
|
|
14
|
+
# Set up logging based on the config's specified log file
|
|
15
|
+
command_log = config_settings.logfile_location.replace("dar-backup.log", "dar-backup-commands.log")
|
|
16
|
+
logger = setup_logging(
|
|
17
|
+
config_settings.logfile_location,
|
|
18
|
+
command_log,
|
|
19
|
+
log_level="info",
|
|
20
|
+
log_stdout=True,
|
|
21
|
+
)
|
|
22
|
+
command_logger = get_logger(command_output_logger=True)
|
|
23
|
+
runner = CommandRunner(logger=logger, command_logger=command_logger)
|
|
24
|
+
|
|
25
|
+
# Create directories listed in config
|
|
26
|
+
for attr in ["backup_dir", "test_restore_dir", "backup_d_dir", "manager_db_dir"]:
|
|
27
|
+
path = getattr(config_settings, attr, None)
|
|
28
|
+
if path:
|
|
29
|
+
dir_path = Path(path).expanduser()
|
|
30
|
+
if not dir_path.exists():
|
|
31
|
+
dir_path.mkdir(parents=True, exist_ok=True)
|
|
32
|
+
print(f"Created directory: {dir_path}")
|
|
33
|
+
else:
|
|
34
|
+
print(f"Directory already exists: {dir_path}")
|
|
35
|
+
|
|
36
|
+
# Optionally create databases
|
|
37
|
+
if create_db_flag:
|
|
38
|
+
for file in os.listdir(config_settings.backup_d_dir):
|
|
39
|
+
backup_def = os.path.basename(file)
|
|
40
|
+
print(f"Creating catalog for: {backup_def}")
|
|
41
|
+
result = create_db(backup_def, config_settings, logger)
|
|
42
|
+
if result == 0:
|
|
43
|
+
print(f"✔️ Catalog created (or already existed): {backup_def}")
|
|
44
|
+
else:
|
|
45
|
+
print(f"❌ Failed to create catalog: {backup_def}")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def installer_main():
|
|
49
|
+
parser = argparse.ArgumentParser(description="dar-backup installer")
|
|
50
|
+
parser.add_argument("--config", required=True, help="Path to config file")
|
|
51
|
+
parser.add_argument("--create-db", action="store_true", help="Create catalog databases")
|
|
52
|
+
args = parser.parse_args()
|
|
53
|
+
|
|
54
|
+
run_installer(args.config, args.create_db)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
if __name__ == "__main__":
|
|
58
|
+
installer_main()
|