dar-backup 0.6.19__tar.gz → 0.6.20__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.
Files changed (104) hide show
  1. {dar_backup-0.6.19/src/dar_backup → dar_backup-0.6.20}/Changelog.md +16 -0
  2. {dar_backup-0.6.19 → dar_backup-0.6.20}/PKG-INFO +1 -1
  3. {dar_backup-0.6.19 → dar_backup-0.6.20}/pyproject.toml +1 -1
  4. {dar_backup-0.6.19 → dar_backup-0.6.20/src/dar_backup}/Changelog.md +16 -0
  5. dar_backup-0.6.20/src/dar_backup/__about__.py +1 -0
  6. {dar_backup-0.6.19 → dar_backup-0.6.20}/src/dar_backup/cleanup.py +22 -22
  7. dar_backup-0.6.20/src/dar_backup/config_settings.py +129 -0
  8. {dar_backup-0.6.19 → dar_backup-0.6.20}/src/dar_backup/dar-backup.conf +4 -0
  9. {dar_backup-0.6.19 → dar_backup-0.6.20}/src/dar_backup/dar_backup.py +38 -32
  10. dar_backup-0.6.20/src/dar_backup/exceptions.py +3 -0
  11. dar_backup-0.6.20/src/dar_backup/installer.py +58 -0
  12. {dar_backup-0.6.19 → dar_backup-0.6.20}/src/dar_backup/manager.py +55 -28
  13. {dar_backup-0.6.19 → dar_backup-0.6.20}/src/dar_backup/util.py +100 -3
  14. dar_backup-0.6.19/src/dar_backup/__about__.py +0 -1
  15. dar_backup-0.6.19/src/dar_backup/config_settings.py +0 -99
  16. {dar_backup-0.6.19 → dar_backup-0.6.20}/.gitignore +0 -0
  17. {dar_backup-0.6.19 → dar_backup-0.6.20}/LICENSE +0 -0
  18. {dar_backup-0.6.19 → dar_backup-0.6.20}/README.md +0 -0
  19. {dar_backup-0.6.19 → dar_backup-0.6.20}/doc/dev.md +0 -0
  20. {dar_backup-0.6.19 → dar_backup-0.6.20}/doc/doc.md +0 -0
  21. {dar_backup-0.6.19 → dar_backup-0.6.20}/src/dar_backup/.darrc +0 -0
  22. {dar_backup-0.6.19 → dar_backup-0.6.20}/src/dar_backup/README.md +0 -0
  23. {dar_backup-0.6.19 → dar_backup-0.6.20}/src/dar_backup/__init__.py +0 -0
  24. {dar_backup-0.6.19 → dar_backup-0.6.20}/src/dar_backup/clean_log.py +0 -0
  25. {dar_backup-0.6.19 → dar_backup-0.6.20}/src/dar_backup/command_runner.py +0 -0
  26. {dar_backup-0.6.19 → dar_backup-0.6.20}/src/dar_backup/dar_backup_systemd.py +0 -0
  27. /dar_backup-0.6.19/src/dar_backup/installer.py → /dar_backup-0.6.20/src/dar_backup/demo.py +0 -0
  28. {dar_backup-0.6.19 → dar_backup-0.6.20}/src/dar_backup/rich_progress.py +0 -0
  29. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/DataProperty-1.1.0.dist-info/LICENSE +0 -0
  30. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/SecretStorage-3.3.3.dist-info/LICENSE +0 -0
  31. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/anyio-4.4.0.dist-info/LICENSE +0 -0
  32. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/build-1.2.1.dist-info/LICENSE +0 -0
  33. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/certifi-2024.7.4.dist-info/LICENSE +0 -0
  34. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/cffi-1.16.0.dist-info/LICENSE +0 -0
  35. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/chardet-5.2.0.dist-info/LICENSE +0 -0
  36. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/charset_normalizer-3.3.2.dist-info/LICENSE +0 -0
  37. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/cryptography-43.0.0.dist-info/license_files/LICENSE +0 -0
  38. {dar_backup-0.6.19/venv/lib/python3.12/site-packages/dar_backup-0.6.19.dist-info → dar_backup-0.6.20/venv/lib/python3.12/site-packages/dar_backup-0.6.20.dist-info}/licenses/LICENSE +0 -0
  39. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/filelock-3.15.4.dist-info/licenses/LICENSE +0 -0
  40. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/hyperlink-21.0.0.dist-info/LICENSE +0 -0
  41. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/importlib_metadata-8.2.0.dist-info/LICENSE +0 -0
  42. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/iniconfig-2.0.0.dist-info/licenses/LICENSE +0 -0
  43. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/jaraco.classes-3.4.0.dist-info/LICENSE +0 -0
  44. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/jaraco.context-5.3.0.dist-info/LICENSE +0 -0
  45. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/jaraco.functools-4.0.2.dist-info/LICENSE +0 -0
  46. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/jeepney-0.8.0.dist-info/LICENSE +0 -0
  47. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/keyring-25.3.0.dist-info/LICENSE +0 -0
  48. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/markdown_it_py-3.0.0.dist-info/LICENSE +0 -0
  49. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/mbstrdecoder-1.1.4.dist-info/LICENSE +0 -0
  50. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/mdurl-0.1.2.dist-info/LICENSE +0 -0
  51. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/more_itertools-10.3.0.dist-info/LICENSE +0 -0
  52. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/packaging-24.1.dist-info/LICENSE +0 -0
  53. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/pathspec-0.12.1.dist-info/LICENSE +0 -0
  54. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/pathvalidate-3.2.3.dist-info/LICENSE +0 -0
  55. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/pexpect-4.9.0.dist-info/LICENSE +0 -0
  56. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/platformdirs-4.2.2.dist-info/licenses/LICENSE +0 -0
  57. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/pluggy-1.5.0.dist-info/LICENSE +0 -0
  58. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/prettytable-3.16.0.dist-info/licenses/LICENSE +0 -0
  59. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/psutil-7.0.0.dist-info/LICENSE +0 -0
  60. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/ptyprocess-0.7.0.dist-info/LICENSE +0 -0
  61. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/pycparser-2.22.dist-info/LICENSE +0 -0
  62. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/pygments-2.19.1.dist-info/licenses/LICENSE +0 -0
  63. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/pytablewriter-1.2.1.dist-info/LICENSE +0 -0
  64. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/pytest-8.3.2.dist-info/LICENSE +0 -0
  65. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/pytest_cov-6.0.0.dist-info/LICENSE +0 -0
  66. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/pytest_timeout-2.3.1.dist-info/LICENSE +0 -0
  67. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/python_dateutil-2.9.0.post0.dist-info/LICENSE +0 -0
  68. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/python_slugify-8.0.4.dist-info/LICENSE +0 -0
  69. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/readme_renderer-44.0.dist-info/LICENSE +0 -0
  70. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/requests-2.32.3.dist-info/LICENSE +0 -0
  71. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/requests_toolbelt-1.0.0.dist-info/LICENSE +0 -0
  72. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/rfc3986-2.0.0.dist-info/LICENSE +0 -0
  73. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/rich-14.0.0.dist-info/LICENSE +0 -0
  74. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand-2.2.2.dist-info/LICENSE +0 -0
  75. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/setuptools/_vendor/backports.tarfile-1.2.0.dist-info/LICENSE +0 -0
  76. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata-8.0.0.dist-info/LICENSE +0 -0
  77. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/setuptools/_vendor/inflect-7.3.1.dist-info/LICENSE +0 -0
  78. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco.collections-5.1.0.dist-info/LICENSE +0 -0
  79. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco.context-5.3.0.dist-info/LICENSE +0 -0
  80. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco.functools-4.0.1.dist-info/LICENSE +0 -0
  81. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco.text-3.12.1.dist-info/LICENSE +0 -0
  82. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/setuptools/_vendor/more_itertools-10.3.0.dist-info/LICENSE +0 -0
  83. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/setuptools/_vendor/packaging-24.2.dist-info/LICENSE +0 -0
  84. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs-4.2.2.dist-info/licenses/LICENSE +0 -0
  85. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/setuptools/_vendor/tomli-2.0.1.dist-info/LICENSE +0 -0
  86. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/setuptools/_vendor/typeguard-4.3.0.dist-info/LICENSE +0 -0
  87. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/setuptools/_vendor/typing_extensions-4.12.2.dist-info/LICENSE +0 -0
  88. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/vendored/packaging/LICENSE +0 -0
  89. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/setuptools/_vendor/zipp-3.19.2.dist-info/LICENSE +0 -0
  90. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/setuptools-78.1.0.dist-info/licenses/LICENSE +0 -0
  91. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/shellingham-1.5.4.dist-info/LICENSE +0 -0
  92. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/six-1.17.0.dist-info/LICENSE +0 -0
  93. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/sniffio-1.3.1.dist-info/LICENSE +0 -0
  94. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/tabledata-1.3.4.dist-info/LICENSE +0 -0
  95. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/tcolorpy-0.1.7.dist-info/LICENSE +0 -0
  96. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/tomli_w-1.0.0.dist-info/LICENSE +0 -0
  97. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/tomlkit-0.13.2.dist-info/LICENSE +0 -0
  98. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/trove_classifiers-2024.7.2.dist-info/LICENSE +0 -0
  99. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/twine-6.0.1.dist-info/LICENSE +0 -0
  100. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/typepy-1.3.4.dist-info/LICENSE +0 -0
  101. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/virtualenv-20.26.3.dist-info/licenses/LICENSE +0 -0
  102. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/wcwidth-0.2.13.dist-info/LICENSE +0 -0
  103. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/zipp-3.19.2.dist-info/LICENSE +0 -0
  104. {dar_backup-0.6.19 → dar_backup-0.6.20}/venv/lib/python3.12/site-packages/zstandard-0.23.0.dist-info/LICENSE +0 -0
@@ -1,6 +1,22 @@
1
1
  <!-- markdownlint-disable MD024 -->
2
2
  # dar-backup Changelog
3
3
 
4
+ ## v2-beta-0.6.20 - 2025-05-03
5
+
6
+ Github link: [v2-beta-0.6.20](https://github.com/per2jensen/dar-backup/tree/v2-beta-0.6.20/v2)
7
+
8
+ ### Added
9
+
10
+ - show_version() moved to util and tests for dar-backup, manager and cleanup
11
+ - startup informational messages now works the same across the scripts
12
+ - Improved ConfigSettings class to handle optional configuration keys
13
+
14
+ -- test cases added
15
+
16
+ - Optional config parameter: MANAGER_DB_DIR, ideally to point to another disk for safe keeping backup catalogs
17
+
18
+ -- test cases added
19
+
4
20
  ## v2-beta-0.6.19 - 2025-04-21
5
21
 
6
22
  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.19
3
+ Version: 0.6.20
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
- installer = "dar_backup.installer:main"
50
+ demo = "dar_backup.demo:main"
51
51
  dar-backup-systemd = "dar_backup.dar_backup_systemd:main"
52
52
 
53
53
 
@@ -1,6 +1,22 @@
1
1
  <!-- markdownlint-disable MD024 -->
2
2
  # dar-backup Changelog
3
3
 
4
+ ## v2-beta-0.6.20 - 2025-05-03
5
+
6
+ Github link: [v2-beta-0.6.20](https://github.com/per2jensen/dar-backup/tree/v2-beta-0.6.20/v2)
7
+
8
+ ### Added
9
+
10
+ - show_version() moved to util and tests for dar-backup, manager and cleanup
11
+ - startup informational messages now works the same across the scripts
12
+ - Improved ConfigSettings class to handle optional configuration keys
13
+
14
+ -- test cases added
15
+
16
+ - Optional config parameter: MANAGER_DB_DIR, ideally to point to another disk for safe keeping backup catalogs
17
+
18
+ -- test cases added
19
+
4
20
  ## v2-beta-0.6.19 - 2025-04-21
5
21
 
6
22
  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"
@@ -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
- confirmation = os.environ.get("CLEANUP_TEST_DELETE_FULL")
167
- if confirmation is None:
168
- raise RuntimeError("envvar 'CLEANUP_TEST_DELETE_FULL' not set")
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
- logger.info(f"=====================================")
224
- logger.info(f"cleanup.py started, version: {about.__version__}")
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 (print(f"Script directory: {file_dir}"))
232
- args.verbose and (print(f"Config file: {args.config_file}"))
233
- args.verbose and (print(f"Backup dir: {config_settings.backup_dir}"))
234
- args.verbose and (print(f"Logfile location: {config_settings.logfile_location}"))
235
- args.verbose and (print(f"--alternate-archive-dir: {args.alternate_archive_dir}"))
236
- args.verbose and (print(f"--cleanup-specific-archives:{args.cleanup_specific_archives}"))
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
- logger.info(f"=====================================")
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"`args`:\n{args}")
854
- logger.debug(f"`config_settings`:\n{config_settings}")
855
- dar_manager_properties = get_binary_info(command='dar')
856
- logger.debug(f"dar path: {dar_manager_properties['path']}")
857
- logger.debug(f"dar version: {dar_manager_properties['version']}")
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
- args.verbose and (print(f"Script directory: {file_dir}"))
861
- args.verbose and (print(f"Config file: {args.config_file}"))
862
- args.verbose and args.full_backup and (print(f"Type of backup: FULL"))
863
- args.verbose and args.differential_backup and (print(f"Type of backup: DIFF"))
864
- args.verbose and args.incremental_backup and (print(f"Type of backup: INCR"))
865
- args.verbose and args.backup_definition and (print(f"Backup definition: '{args.backup_definition}'"))
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 (print(f"Alternate ref archive: {args.alternate_reference_archive}"))
868
- args.verbose and (print(f"Backup.d dir: {config_settings.backup_d_dir}"))
869
- args.verbose and (print(f"Backup dir: {config_settings.backup_dir}"))
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 (print(f"Restore dir: {restore_dir}"))
871
+ args.verbose and start_msgs.append(("Restore dir:", restore_dir))
873
872
 
874
- args.verbose and (print(f"Logfile location: {config_settings.logfile_location}"))
875
- args.verbose and (print(f".darrc location: {args.darrc}"))
876
- args.verbose and (print(f"PAR2 enabled: {config_settings.par2_enabled}"))
877
- args.verbose and (print(f"--do-not-compare: {args.do_not_compare}"))
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 and print("\033[1m\033[31mErrors\033[0m encountered")
943
+ if args.verbose:
944
+ console.print(Text("Errors encountered", style="bold red"))
941
945
  exit(1)
942
946
  else:
943
- args.verbose and print("\033[1m\033[32mSuccess\033[0m all backups completed")
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,3 @@
1
+ class ConfigSettingsError(Exception):
2
+ """Raised when ConfigSettings encounters a critical error."""
3
+ pass
@@ -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()