declarative-data-backup 0.2.1__tar.gz → 0.3.0__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 (26) hide show
  1. {declarative_data_backup-0.2.1 → declarative_data_backup-0.3.0}/PKG-INFO +17 -11
  2. {declarative_data_backup-0.2.1 → declarative_data_backup-0.3.0}/README.md +16 -10
  3. {declarative_data_backup-0.2.1 → declarative_data_backup-0.3.0}/pyproject.toml +1 -1
  4. {declarative_data_backup-0.2.1 → declarative_data_backup-0.3.0}/src/declarative_data_backup/backup.py +7 -0
  5. {declarative_data_backup-0.2.1 → declarative_data_backup-0.3.0}/src/declarative_data_backup/borg/borg.py +13 -7
  6. {declarative_data_backup-0.2.1 → declarative_data_backup-0.3.0}/src/declarative_data_backup/environment.py +10 -0
  7. {declarative_data_backup-0.2.1 → declarative_data_backup-0.3.0}/src/declarative_data_backup.egg-info/PKG-INFO +17 -11
  8. {declarative_data_backup-0.2.1 → declarative_data_backup-0.3.0}/tests/test_backup.py +4 -0
  9. {declarative_data_backup-0.2.1 → declarative_data_backup-0.3.0}/setup.cfg +0 -0
  10. {declarative_data_backup-0.2.1 → declarative_data_backup-0.3.0}/src/declarative_data_backup/__init__.py +0 -0
  11. {declarative_data_backup-0.2.1 → declarative_data_backup-0.3.0}/src/declarative_data_backup/__main__.py +0 -0
  12. {declarative_data_backup-0.2.1 → declarative_data_backup-0.3.0}/src/declarative_data_backup/borg/__init__.py +0 -0
  13. {declarative_data_backup-0.2.1 → declarative_data_backup-0.3.0}/src/declarative_data_backup/borg/archive_naming.py +0 -0
  14. {declarative_data_backup-0.2.1 → declarative_data_backup-0.3.0}/src/declarative_data_backup/borg/borg_cli.py +0 -0
  15. {declarative_data_backup-0.2.1 → declarative_data_backup-0.3.0}/src/declarative_data_backup/config/__init__.py +0 -0
  16. {declarative_data_backup-0.2.1 → declarative_data_backup-0.3.0}/src/declarative_data_backup/config/backup_config/__init__.py +0 -0
  17. {declarative_data_backup-0.2.1 → declarative_data_backup-0.3.0}/src/declarative_data_backup/config/backup_config/models.py +0 -0
  18. {declarative_data_backup-0.2.1 → declarative_data_backup-0.3.0}/src/declarative_data_backup/config/backup_config/utils.py +0 -0
  19. {declarative_data_backup-0.2.1 → declarative_data_backup-0.3.0}/src/declarative_data_backup/config/backup_config/yaml_parser.py +0 -0
  20. {declarative_data_backup-0.2.1 → declarative_data_backup-0.3.0}/src/declarative_data_backup/config/backup_config/yaml_raw_loader.py +0 -0
  21. {declarative_data_backup-0.2.1 → declarative_data_backup-0.3.0}/src/declarative_data_backup/config/logging.py +0 -0
  22. {declarative_data_backup-0.2.1 → declarative_data_backup-0.3.0}/src/declarative_data_backup/errors.py +0 -0
  23. {declarative_data_backup-0.2.1 → declarative_data_backup-0.3.0}/src/declarative_data_backup.egg-info/SOURCES.txt +0 -0
  24. {declarative_data_backup-0.2.1 → declarative_data_backup-0.3.0}/src/declarative_data_backup.egg-info/dependency_links.txt +0 -0
  25. {declarative_data_backup-0.2.1 → declarative_data_backup-0.3.0}/src/declarative_data_backup.egg-info/requires.txt +0 -0
  26. {declarative_data_backup-0.2.1 → declarative_data_backup-0.3.0}/src/declarative_data_backup.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: declarative-data-backup
3
- Version: 0.2.1
3
+ Version: 0.3.0
4
4
  Summary: Declarative Borg-based data backups
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -149,6 +149,8 @@ Optional variables:
149
149
  | --- | --- |
150
150
  | `BORG_REMOTE_PATH` | Remote Borg executable path, passed as Borg's `--remote-path`. Leave unset to use Borg's default. |
151
151
  | `BORG_EXIT_CODES` | Borg exit-code mode: `legacy` or `modern`. |
152
+ | `BACKUP_PROGRESS` | Set to `true` to enable Borg's live progress output during archive creation. Defaults to `false` to limit log volume. |
153
+ | `BACKUP_VERBOSE` | Set to `true` to show Borg's per-file create and per-archive prune output. Defaults to `false` for concise logs. |
152
154
 
153
155
  Provide credentials through a secret manager or protected files; do not put
154
156
  passphrases, private keys, or credentials in the YAML file, source control, or
@@ -161,7 +163,10 @@ python -m declarative_data_backup
161
163
 
162
164
  The command exits with status `0` on success, nonzero on configuration or Borg
163
165
  failure, and `130` when interrupted. Borg's exit code `1` indicates warnings:
164
- the run fails and pruning is skipped.
166
+ the run fails and pruning is skipped. Logs include group and source start/end
167
+ messages, while Borg's per-file and per-archive output is disabled by default.
168
+ Set `BACKUP_VERBOSE=true` for detailed Borg output or `BACKUP_PROGRESS=true`
169
+ for live progress during archive creation; either can increase log volume.
165
170
 
166
171
  ## Configuration reference
167
172
 
@@ -289,23 +294,24 @@ container integration test.
289
294
 
290
295
  ## Testing
291
296
 
292
- Run the unit tests:
297
+ Run the unit tests, integration tests, or both:
293
298
 
294
299
  ```shell
295
- uv run pytest -m "not integration"
300
+ make test-unit
301
+ make test-integration
302
+ make test
296
303
  ```
297
304
 
298
- The opt-in local Borg integration test uses a temporary encrypted repository:
305
+ The integration target opts in to both local Borg tests (which use temporary
306
+ encrypted repositories) and the Docker SSH test (which uses disposable
307
+ containers and a temporary repository). It does not use production credentials
308
+ or BorgBase. Docker must be installed and its daemon available.
309
+
310
+ To run either integration suite individually:
299
311
 
300
312
  ```shell
301
313
  RUN_BORG_LOCAL_INTEGRATION=1 uv run pytest -m integration \
302
314
  tests/integration/test_local_borg_integration.py
303
- ```
304
-
305
- The opt-in Docker SSH integration test uses disposable containers and a
306
- temporary repository; it does not use production credentials or BorgBase:
307
-
308
- ```shell
309
315
  RUN_BORG_DOCKER_INTEGRATION=1 uv run pytest -m integration \
310
316
  tests/integration/test_docker_integration.py
311
317
  ```
@@ -141,6 +141,8 @@ Optional variables:
141
141
  | --- | --- |
142
142
  | `BORG_REMOTE_PATH` | Remote Borg executable path, passed as Borg's `--remote-path`. Leave unset to use Borg's default. |
143
143
  | `BORG_EXIT_CODES` | Borg exit-code mode: `legacy` or `modern`. |
144
+ | `BACKUP_PROGRESS` | Set to `true` to enable Borg's live progress output during archive creation. Defaults to `false` to limit log volume. |
145
+ | `BACKUP_VERBOSE` | Set to `true` to show Borg's per-file create and per-archive prune output. Defaults to `false` for concise logs. |
144
146
 
145
147
  Provide credentials through a secret manager or protected files; do not put
146
148
  passphrases, private keys, or credentials in the YAML file, source control, or
@@ -153,7 +155,10 @@ python -m declarative_data_backup
153
155
 
154
156
  The command exits with status `0` on success, nonzero on configuration or Borg
155
157
  failure, and `130` when interrupted. Borg's exit code `1` indicates warnings:
156
- the run fails and pruning is skipped.
158
+ the run fails and pruning is skipped. Logs include group and source start/end
159
+ messages, while Borg's per-file and per-archive output is disabled by default.
160
+ Set `BACKUP_VERBOSE=true` for detailed Borg output or `BACKUP_PROGRESS=true`
161
+ for live progress during archive creation; either can increase log volume.
157
162
 
158
163
  ## Configuration reference
159
164
 
@@ -281,23 +286,24 @@ container integration test.
281
286
 
282
287
  ## Testing
283
288
 
284
- Run the unit tests:
289
+ Run the unit tests, integration tests, or both:
285
290
 
286
291
  ```shell
287
- uv run pytest -m "not integration"
292
+ make test-unit
293
+ make test-integration
294
+ make test
288
295
  ```
289
296
 
290
- The opt-in local Borg integration test uses a temporary encrypted repository:
297
+ The integration target opts in to both local Borg tests (which use temporary
298
+ encrypted repositories) and the Docker SSH test (which uses disposable
299
+ containers and a temporary repository). It does not use production credentials
300
+ or BorgBase. Docker must be installed and its daemon available.
301
+
302
+ To run either integration suite individually:
291
303
 
292
304
  ```shell
293
305
  RUN_BORG_LOCAL_INTEGRATION=1 uv run pytest -m integration \
294
306
  tests/integration/test_local_borg_integration.py
295
- ```
296
-
297
- The opt-in Docker SSH integration test uses disposable containers and a
298
- temporary repository; it does not use production credentials or BorgBase:
299
-
300
- ```shell
301
307
  RUN_BORG_DOCKER_INTEGRATION=1 uv run pytest -m integration \
302
308
  tests/integration/test_docker_integration.py
303
309
  ```
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "declarative-data-backup"
7
- version = "0.2.1"
7
+ version = "0.3.0"
8
8
  description = "Declarative Borg-based data backups"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -70,8 +70,14 @@ def run_backup(
70
70
  run_id,
71
71
  definition.excludes,
72
72
  )
73
+ logger.info("Backup group '%s' completed.", definition.name)
73
74
 
74
75
  for definition in definitions:
76
+ logger.info(
77
+ "Starting retention pruning for group '%s' (%d sources).",
78
+ definition.name,
79
+ len(definition.sources),
80
+ )
75
81
  for folder in definition.sources:
76
82
  prune(
77
83
  folder,
@@ -79,6 +85,7 @@ def run_backup(
79
85
  definition.name,
80
86
  settings,
81
87
  )
88
+ logger.info("Retention pruning for group '%s' completed.", definition.name)
82
89
 
83
90
 
84
91
  def main(environ: Mapping[str, str] | None = None) -> int:
@@ -41,11 +41,14 @@ def backup_folder(
41
41
  logger.info("Starting backup for %s as archive %s.", context, name)
42
42
  args = [
43
43
  "create",
44
- "--verbose",
45
44
  "--stats",
46
45
  "--compression",
47
46
  compression,
48
47
  ]
48
+ if settings.backup_verbose:
49
+ args.append("--verbose")
50
+ if settings.backup_progress:
51
+ args.append("--progress")
49
52
  for pattern in excludes:
50
53
  args.extend(("--exclude", pattern))
51
54
  args.extend((f"{settings.borg_repo}::{name}", str(folder)))
@@ -85,17 +88,20 @@ def prune(
85
88
  retention_days,
86
89
  )
87
90
  try:
88
- run_borg(
89
- [
90
- "prune",
91
- "--verbose",
92
- "--list",
91
+ args = ["prune"]
92
+ if settings.backup_verbose:
93
+ args.extend(("--verbose", "--list"))
94
+ args.extend(
95
+ (
93
96
  "--glob-archives",
94
97
  selector,
95
98
  "--keep-daily",
96
99
  str(retention_days),
97
100
  settings.borg_repo,
98
- ],
101
+ )
102
+ )
103
+ run_borg(
104
+ args,
99
105
  settings,
100
106
  "prune",
101
107
  context,
@@ -17,6 +17,8 @@ class Environment:
17
17
  borg_passcommand: str
18
18
  borg_remote_path: str | None
19
19
  borg_exit_codes: str | None
20
+ backup_progress: bool
21
+ backup_verbose: bool
20
22
 
21
23
  @classmethod
22
24
  def from_env(
@@ -46,6 +48,12 @@ class Environment:
46
48
  borg_exit_codes = source.get("BORG_EXIT_CODES")
47
49
  if borg_exit_codes not in (None, "legacy", "modern"):
48
50
  fail("BORG_EXIT_CODES must be either 'legacy' or 'modern'.")
51
+ backup_progress_value = source.get("BACKUP_PROGRESS", "false").lower()
52
+ if backup_progress_value not in ("true", "false"):
53
+ fail("BACKUP_PROGRESS must be either 'true' or 'false'.")
54
+ backup_verbose_value = source.get("BACKUP_VERBOSE", "false").lower()
55
+ if backup_verbose_value not in ("true", "false"):
56
+ fail("BACKUP_VERBOSE must be either 'true' or 'false'.")
49
57
 
50
58
  try:
51
59
  server_root = required_path("SERVER_ROOT").resolve(strict=True)
@@ -62,6 +70,8 @@ class Environment:
62
70
  borg_passcommand=required("BORG_PASSCOMMAND"),
63
71
  borg_remote_path=source.get("BORG_REMOTE_PATH"),
64
72
  borg_exit_codes=borg_exit_codes,
73
+ backup_progress=backup_progress_value == "true",
74
+ backup_verbose=backup_verbose_value == "true",
65
75
  )
66
76
 
67
77
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: declarative-data-backup
3
- Version: 0.2.1
3
+ Version: 0.3.0
4
4
  Summary: Declarative Borg-based data backups
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -149,6 +149,8 @@ Optional variables:
149
149
  | --- | --- |
150
150
  | `BORG_REMOTE_PATH` | Remote Borg executable path, passed as Borg's `--remote-path`. Leave unset to use Borg's default. |
151
151
  | `BORG_EXIT_CODES` | Borg exit-code mode: `legacy` or `modern`. |
152
+ | `BACKUP_PROGRESS` | Set to `true` to enable Borg's live progress output during archive creation. Defaults to `false` to limit log volume. |
153
+ | `BACKUP_VERBOSE` | Set to `true` to show Borg's per-file create and per-archive prune output. Defaults to `false` for concise logs. |
152
154
 
153
155
  Provide credentials through a secret manager or protected files; do not put
154
156
  passphrases, private keys, or credentials in the YAML file, source control, or
@@ -161,7 +163,10 @@ python -m declarative_data_backup
161
163
 
162
164
  The command exits with status `0` on success, nonzero on configuration or Borg
163
165
  failure, and `130` when interrupted. Borg's exit code `1` indicates warnings:
164
- the run fails and pruning is skipped.
166
+ the run fails and pruning is skipped. Logs include group and source start/end
167
+ messages, while Borg's per-file and per-archive output is disabled by default.
168
+ Set `BACKUP_VERBOSE=true` for detailed Borg output or `BACKUP_PROGRESS=true`
169
+ for live progress during archive creation; either can increase log volume.
165
170
 
166
171
  ## Configuration reference
167
172
 
@@ -289,23 +294,24 @@ container integration test.
289
294
 
290
295
  ## Testing
291
296
 
292
- Run the unit tests:
297
+ Run the unit tests, integration tests, or both:
293
298
 
294
299
  ```shell
295
- uv run pytest -m "not integration"
300
+ make test-unit
301
+ make test-integration
302
+ make test
296
303
  ```
297
304
 
298
- The opt-in local Borg integration test uses a temporary encrypted repository:
305
+ The integration target opts in to both local Borg tests (which use temporary
306
+ encrypted repositories) and the Docker SSH test (which uses disposable
307
+ containers and a temporary repository). It does not use production credentials
308
+ or BorgBase. Docker must be installed and its daemon available.
309
+
310
+ To run either integration suite individually:
299
311
 
300
312
  ```shell
301
313
  RUN_BORG_LOCAL_INTEGRATION=1 uv run pytest -m integration \
302
314
  tests/integration/test_local_borg_integration.py
303
- ```
304
-
305
- The opt-in Docker SSH integration test uses disposable containers and a
306
- temporary repository; it does not use production credentials or BorgBase:
307
-
308
- ```shell
309
315
  RUN_BORG_DOCKER_INTEGRATION=1 uv run pytest -m integration \
310
316
  tests/integration/test_docker_integration.py
311
317
  ```
@@ -29,6 +29,7 @@ def _configure_preflight(monkeypatch):
29
29
  def test_main_runs_all_creates_before_any_prune(
30
30
  settings_factory,
31
31
  monkeypatch,
32
+ caplog,
32
33
  ):
33
34
  settings = settings_factory()
34
35
  media = settings.server_root / "media"
@@ -62,6 +63,7 @@ def test_main_runs_all_creates_before_any_prune(
62
63
  monkeypatch.setattr(
63
64
  backup.uuid, "uuid4", lambda: type("UUID", (), {"hex": "a" * 32})()
64
65
  )
66
+ caplog.set_level(logging.INFO)
65
67
 
66
68
  assert backup.main(_settings_environment(settings)) == 0
67
69
  assert [event[0] for event in events] == [
@@ -73,6 +75,8 @@ def test_main_runs_all_creates_before_any_prune(
73
75
  assert events[0][3] == "zstd,5"
74
76
  assert events[1][3] == "auto,zstd,22"
75
77
  assert events[2][3] == 30
78
+ assert "Backup group 'media_backup' completed." in caplog.text
79
+ assert "Retention pruning for group 'media_backup' completed." in caplog.text
76
80
 
77
81
 
78
82
  def test_main_warning_aborts_remaining_creates_and_all_pruning(