declarative-data-backup 0.2.0__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 (27) hide show
  1. {declarative_data_backup-0.2.0 → declarative_data_backup-0.3.0}/PKG-INFO +27 -24
  2. {declarative_data_backup-0.2.0 → declarative_data_backup-0.3.0}/README.md +26 -18
  3. {declarative_data_backup-0.2.0 → declarative_data_backup-0.3.0}/pyproject.toml +2 -2
  4. {declarative_data_backup-0.2.0 → declarative_data_backup-0.3.0}/src/declarative_data_backup/backup.py +7 -0
  5. {declarative_data_backup-0.2.0 → declarative_data_backup-0.3.0}/src/declarative_data_backup/borg/borg.py +13 -7
  6. {declarative_data_backup-0.2.0 → declarative_data_backup-0.3.0}/src/declarative_data_backup/environment.py +10 -0
  7. {declarative_data_backup-0.2.0 → declarative_data_backup-0.3.0}/src/declarative_data_backup.egg-info/PKG-INFO +27 -24
  8. declarative_data_backup-0.3.0/src/declarative_data_backup.egg-info/requires.txt +1 -0
  9. {declarative_data_backup-0.2.0 → declarative_data_backup-0.3.0}/tests/test_backup.py +4 -0
  10. declarative_data_backup-0.2.0/src/declarative_data_backup.egg-info/requires.txt +0 -7
  11. {declarative_data_backup-0.2.0 → declarative_data_backup-0.3.0}/setup.cfg +0 -0
  12. {declarative_data_backup-0.2.0 → declarative_data_backup-0.3.0}/src/declarative_data_backup/__init__.py +0 -0
  13. {declarative_data_backup-0.2.0 → declarative_data_backup-0.3.0}/src/declarative_data_backup/__main__.py +0 -0
  14. {declarative_data_backup-0.2.0 → declarative_data_backup-0.3.0}/src/declarative_data_backup/borg/__init__.py +0 -0
  15. {declarative_data_backup-0.2.0 → declarative_data_backup-0.3.0}/src/declarative_data_backup/borg/archive_naming.py +0 -0
  16. {declarative_data_backup-0.2.0 → declarative_data_backup-0.3.0}/src/declarative_data_backup/borg/borg_cli.py +0 -0
  17. {declarative_data_backup-0.2.0 → declarative_data_backup-0.3.0}/src/declarative_data_backup/config/__init__.py +0 -0
  18. {declarative_data_backup-0.2.0 → declarative_data_backup-0.3.0}/src/declarative_data_backup/config/backup_config/__init__.py +0 -0
  19. {declarative_data_backup-0.2.0 → declarative_data_backup-0.3.0}/src/declarative_data_backup/config/backup_config/models.py +0 -0
  20. {declarative_data_backup-0.2.0 → declarative_data_backup-0.3.0}/src/declarative_data_backup/config/backup_config/utils.py +0 -0
  21. {declarative_data_backup-0.2.0 → declarative_data_backup-0.3.0}/src/declarative_data_backup/config/backup_config/yaml_parser.py +0 -0
  22. {declarative_data_backup-0.2.0 → declarative_data_backup-0.3.0}/src/declarative_data_backup/config/backup_config/yaml_raw_loader.py +0 -0
  23. {declarative_data_backup-0.2.0 → declarative_data_backup-0.3.0}/src/declarative_data_backup/config/logging.py +0 -0
  24. {declarative_data_backup-0.2.0 → declarative_data_backup-0.3.0}/src/declarative_data_backup/errors.py +0 -0
  25. {declarative_data_backup-0.2.0 → declarative_data_backup-0.3.0}/src/declarative_data_backup.egg-info/SOURCES.txt +0 -0
  26. {declarative_data_backup-0.2.0 → declarative_data_backup-0.3.0}/src/declarative_data_backup.egg-info/dependency_links.txt +0 -0
  27. {declarative_data_backup-0.2.0 → declarative_data_backup-0.3.0}/src/declarative_data_backup.egg-info/top_level.txt +0 -0
@@ -1,15 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: declarative-data-backup
3
- Version: 0.2.0
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
7
7
  Requires-Dist: PyYAML<7,>=6
8
- Provides-Extra: dev
9
- Requires-Dist: pre-commit<5,>=4; extra == "dev"
10
- Requires-Dist: pyright<2,>=1.1.400; extra == "dev"
11
- Requires-Dist: pytest<10,>=8; extra == "dev"
12
- Requires-Dist: ruff<1,>=0.11; extra == "dev"
13
8
 
14
9
  # Declarative Data Backup
15
10
 
@@ -31,18 +26,20 @@ The application never initializes a repository. Borg 2 is not supported.
31
26
 
32
27
  ## Installation
33
28
 
34
- Install the package from PyPI:
29
+ Install the package from PyPI into a `uv`-managed environment:
35
30
 
36
31
  ```shell
37
- python -m pip install declarative-data-backup
32
+ uv venv
33
+ uv pip install declarative-data-backup
38
34
  ```
39
35
 
40
- Or install a checkout with the development dependencies:
36
+ For a checkout, install the development dependencies with:
41
37
 
42
38
  ```shell
43
- python -m pip install -e ".[dev]"
39
+ make install
44
40
  ```
45
41
 
42
+ To also install the repository's pre-commit hooks, run `make setup`.
46
43
  The PyPI distribution is named `declarative-data-backup`; its importable
47
44
  package is `declarative_data_backup`.
48
45
 
@@ -56,8 +53,8 @@ release pipeline.
56
53
 
57
54
  For each version tag, the pipeline:
58
55
 
59
- 1. Sets the package version in the CI checkout's `pyproject.toml`, builds a
60
- wheel and source distribution, and checks them with Twine.
56
+ 1. Sets the package version in the CI checkout's `pyproject.toml` and uses
57
+ `uv build` to create a wheel and source distribution.
61
58
  2. Uploads `declarative-data-backup` to PyPI.
62
59
  3. Builds the existing Python 3.13 Dockerfile with the same package version,
63
60
  verifies the installed version, and pushes
@@ -152,6 +149,8 @@ Optional variables:
152
149
  | --- | --- |
153
150
  | `BORG_REMOTE_PATH` | Remote Borg executable path, passed as Borg's `--remote-path`. Leave unset to use Borg's default. |
154
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. |
155
154
 
156
155
  Provide credentials through a secret manager or protected files; do not put
157
156
  passphrases, private keys, or credentials in the YAML file, source control, or
@@ -164,7 +163,10 @@ python -m declarative_data_backup
164
163
 
165
164
  The command exits with status `0` on success, nonzero on configuration or Borg
166
165
  failure, and `130` when interrupted. Borg's exit code `1` indicates warnings:
167
- 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.
168
170
 
169
171
  ## Configuration reference
170
172
 
@@ -292,24 +294,25 @@ container integration test.
292
294
 
293
295
  ## Testing
294
296
 
295
- Run the unit tests:
297
+ Run the unit tests, integration tests, or both:
296
298
 
297
299
  ```shell
298
- python -m pytest -m "not integration"
300
+ make test-unit
301
+ make test-integration
302
+ make test
299
303
  ```
300
304
 
301
- 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.
302
309
 
303
- ```shell
304
- RUN_BORG_LOCAL_INTEGRATION=1 python -m pytest -m integration \
305
- tests/integration/test_local_borg_integration.py
306
- ```
307
-
308
- The opt-in Docker SSH integration test uses disposable containers and a
309
- temporary repository; it does not use production credentials or BorgBase:
310
+ To run either integration suite individually:
310
311
 
311
312
  ```shell
312
- RUN_BORG_DOCKER_INTEGRATION=1 python -m pytest -m integration \
313
+ RUN_BORG_LOCAL_INTEGRATION=1 uv run pytest -m integration \
314
+ tests/integration/test_local_borg_integration.py
315
+ RUN_BORG_DOCKER_INTEGRATION=1 uv run pytest -m integration \
313
316
  tests/integration/test_docker_integration.py
314
317
  ```
315
318
 
@@ -18,18 +18,20 @@ The application never initializes a repository. Borg 2 is not supported.
18
18
 
19
19
  ## Installation
20
20
 
21
- Install the package from PyPI:
21
+ Install the package from PyPI into a `uv`-managed environment:
22
22
 
23
23
  ```shell
24
- python -m pip install declarative-data-backup
24
+ uv venv
25
+ uv pip install declarative-data-backup
25
26
  ```
26
27
 
27
- Or install a checkout with the development dependencies:
28
+ For a checkout, install the development dependencies with:
28
29
 
29
30
  ```shell
30
- python -m pip install -e ".[dev]"
31
+ make install
31
32
  ```
32
33
 
34
+ To also install the repository's pre-commit hooks, run `make setup`.
33
35
  The PyPI distribution is named `declarative-data-backup`; its importable
34
36
  package is `declarative_data_backup`.
35
37
 
@@ -43,8 +45,8 @@ release pipeline.
43
45
 
44
46
  For each version tag, the pipeline:
45
47
 
46
- 1. Sets the package version in the CI checkout's `pyproject.toml`, builds a
47
- wheel and source distribution, and checks them with Twine.
48
+ 1. Sets the package version in the CI checkout's `pyproject.toml` and uses
49
+ `uv build` to create a wheel and source distribution.
48
50
  2. Uploads `declarative-data-backup` to PyPI.
49
51
  3. Builds the existing Python 3.13 Dockerfile with the same package version,
50
52
  verifies the installed version, and pushes
@@ -139,6 +141,8 @@ Optional variables:
139
141
  | --- | --- |
140
142
  | `BORG_REMOTE_PATH` | Remote Borg executable path, passed as Borg's `--remote-path`. Leave unset to use Borg's default. |
141
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. |
142
146
 
143
147
  Provide credentials through a secret manager or protected files; do not put
144
148
  passphrases, private keys, or credentials in the YAML file, source control, or
@@ -151,7 +155,10 @@ python -m declarative_data_backup
151
155
 
152
156
  The command exits with status `0` on success, nonzero on configuration or Borg
153
157
  failure, and `130` when interrupted. Borg's exit code `1` indicates warnings:
154
- 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.
155
162
 
156
163
  ## Configuration reference
157
164
 
@@ -279,24 +286,25 @@ container integration test.
279
286
 
280
287
  ## Testing
281
288
 
282
- Run the unit tests:
289
+ Run the unit tests, integration tests, or both:
283
290
 
284
291
  ```shell
285
- python -m pytest -m "not integration"
292
+ make test-unit
293
+ make test-integration
294
+ make test
286
295
  ```
287
296
 
288
- 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.
289
301
 
290
- ```shell
291
- RUN_BORG_LOCAL_INTEGRATION=1 python -m pytest -m integration \
292
- tests/integration/test_local_borg_integration.py
293
- ```
294
-
295
- The opt-in Docker SSH integration test uses disposable containers and a
296
- temporary repository; it does not use production credentials or BorgBase:
302
+ To run either integration suite individually:
297
303
 
298
304
  ```shell
299
- RUN_BORG_DOCKER_INTEGRATION=1 python -m pytest -m integration \
305
+ RUN_BORG_LOCAL_INTEGRATION=1 uv run pytest -m integration \
306
+ tests/integration/test_local_borg_integration.py
307
+ RUN_BORG_DOCKER_INTEGRATION=1 uv run pytest -m integration \
300
308
  tests/integration/test_docker_integration.py
301
309
  ```
302
310
 
@@ -4,13 +4,13 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "declarative-data-backup"
7
- version = "0.2.0"
7
+ version = "0.3.0"
8
8
  description = "Declarative Borg-based data backups"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
11
11
  dependencies = ["PyYAML>=6,<7"]
12
12
 
13
- [project.optional-dependencies]
13
+ [dependency-groups]
14
14
  dev = [
15
15
  "pre-commit>=4,<5",
16
16
  "pyright>=1.1.400,<2",
@@ -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,15 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: declarative-data-backup
3
- Version: 0.2.0
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
7
7
  Requires-Dist: PyYAML<7,>=6
8
- Provides-Extra: dev
9
- Requires-Dist: pre-commit<5,>=4; extra == "dev"
10
- Requires-Dist: pyright<2,>=1.1.400; extra == "dev"
11
- Requires-Dist: pytest<10,>=8; extra == "dev"
12
- Requires-Dist: ruff<1,>=0.11; extra == "dev"
13
8
 
14
9
  # Declarative Data Backup
15
10
 
@@ -31,18 +26,20 @@ The application never initializes a repository. Borg 2 is not supported.
31
26
 
32
27
  ## Installation
33
28
 
34
- Install the package from PyPI:
29
+ Install the package from PyPI into a `uv`-managed environment:
35
30
 
36
31
  ```shell
37
- python -m pip install declarative-data-backup
32
+ uv venv
33
+ uv pip install declarative-data-backup
38
34
  ```
39
35
 
40
- Or install a checkout with the development dependencies:
36
+ For a checkout, install the development dependencies with:
41
37
 
42
38
  ```shell
43
- python -m pip install -e ".[dev]"
39
+ make install
44
40
  ```
45
41
 
42
+ To also install the repository's pre-commit hooks, run `make setup`.
46
43
  The PyPI distribution is named `declarative-data-backup`; its importable
47
44
  package is `declarative_data_backup`.
48
45
 
@@ -56,8 +53,8 @@ release pipeline.
56
53
 
57
54
  For each version tag, the pipeline:
58
55
 
59
- 1. Sets the package version in the CI checkout's `pyproject.toml`, builds a
60
- wheel and source distribution, and checks them with Twine.
56
+ 1. Sets the package version in the CI checkout's `pyproject.toml` and uses
57
+ `uv build` to create a wheel and source distribution.
61
58
  2. Uploads `declarative-data-backup` to PyPI.
62
59
  3. Builds the existing Python 3.13 Dockerfile with the same package version,
63
60
  verifies the installed version, and pushes
@@ -152,6 +149,8 @@ Optional variables:
152
149
  | --- | --- |
153
150
  | `BORG_REMOTE_PATH` | Remote Borg executable path, passed as Borg's `--remote-path`. Leave unset to use Borg's default. |
154
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. |
155
154
 
156
155
  Provide credentials through a secret manager or protected files; do not put
157
156
  passphrases, private keys, or credentials in the YAML file, source control, or
@@ -164,7 +163,10 @@ python -m declarative_data_backup
164
163
 
165
164
  The command exits with status `0` on success, nonzero on configuration or Borg
166
165
  failure, and `130` when interrupted. Borg's exit code `1` indicates warnings:
167
- 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.
168
170
 
169
171
  ## Configuration reference
170
172
 
@@ -292,24 +294,25 @@ container integration test.
292
294
 
293
295
  ## Testing
294
296
 
295
- Run the unit tests:
297
+ Run the unit tests, integration tests, or both:
296
298
 
297
299
  ```shell
298
- python -m pytest -m "not integration"
300
+ make test-unit
301
+ make test-integration
302
+ make test
299
303
  ```
300
304
 
301
- 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.
302
309
 
303
- ```shell
304
- RUN_BORG_LOCAL_INTEGRATION=1 python -m pytest -m integration \
305
- tests/integration/test_local_borg_integration.py
306
- ```
307
-
308
- The opt-in Docker SSH integration test uses disposable containers and a
309
- temporary repository; it does not use production credentials or BorgBase:
310
+ To run either integration suite individually:
310
311
 
311
312
  ```shell
312
- RUN_BORG_DOCKER_INTEGRATION=1 python -m pytest -m integration \
313
+ RUN_BORG_LOCAL_INTEGRATION=1 uv run pytest -m integration \
314
+ tests/integration/test_local_borg_integration.py
315
+ RUN_BORG_DOCKER_INTEGRATION=1 uv run pytest -m integration \
313
316
  tests/integration/test_docker_integration.py
314
317
  ```
315
318
 
@@ -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(
@@ -1,7 +0,0 @@
1
- PyYAML<7,>=6
2
-
3
- [dev]
4
- pre-commit<5,>=4
5
- pyright<2,>=1.1.400
6
- pytest<10,>=8
7
- ruff<1,>=0.11