dbara 20260714__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.
- dbara-20260714/LICENSE +21 -0
- dbara-20260714/PKG-INFO +420 -0
- dbara-20260714/README.md +393 -0
- dbara-20260714/dbara/__init__.py +1 -0
- dbara-20260714/dbara/__main__.py +5 -0
- dbara-20260714/dbara/archive.py +101 -0
- dbara-20260714/dbara/checksum.py +111 -0
- dbara-20260714/dbara/cli.py +237 -0
- dbara-20260714/dbara/config.py +74 -0
- dbara-20260714/dbara/docker_ops.py +55 -0
- dbara-20260714/dbara/engine.py +431 -0
- dbara-20260714/dbara/hooks.py +23 -0
- dbara-20260714/dbara/locks.py +30 -0
- dbara-20260714/dbara/log.py +71 -0
- dbara-20260714/dbara/runner.py +74 -0
- dbara-20260714/dbara/sqlite_backup.py +59 -0
- dbara-20260714/dbara.egg-info/PKG-INFO +420 -0
- dbara-20260714/dbara.egg-info/SOURCES.txt +32 -0
- dbara-20260714/dbara.egg-info/dependency_links.txt +1 -0
- dbara-20260714/dbara.egg-info/entry_points.txt +2 -0
- dbara-20260714/dbara.egg-info/top_level.txt +1 -0
- dbara-20260714/pyproject.toml +60 -0
- dbara-20260714/setup.cfg +4 -0
- dbara-20260714/tests/test_archive.py +166 -0
- dbara-20260714/tests/test_checksum.py +138 -0
- dbara-20260714/tests/test_cli.py +207 -0
- dbara-20260714/tests/test_config.py +100 -0
- dbara-20260714/tests/test_docker_ops.py +106 -0
- dbara-20260714/tests/test_engine_backup.py +255 -0
- dbara-20260714/tests/test_engine_restore.py +321 -0
- dbara-20260714/tests/test_hooks.py +42 -0
- dbara-20260714/tests/test_locks.py +39 -0
- dbara-20260714/tests/test_runner.py +115 -0
- dbara-20260714/tests/test_sqlite_backup.py +97 -0
dbara-20260714/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 DBaRA Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
dbara-20260714/PKG-INFO
ADDED
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dbara
|
|
3
|
+
Version: 20260714
|
|
4
|
+
Summary: Docker Backup and Restore Application — stop, archive, verify, restart
|
|
5
|
+
Author: NASible
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/NASible/DBaRA
|
|
8
|
+
Project-URL: Repository, https://github.com/NASible/DBaRA
|
|
9
|
+
Project-URL: Bug Tracker, https://github.com/NASible/DBaRA/issues
|
|
10
|
+
Keywords: docker,backup,restore,archive,homelab,containers
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: System Administrators
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
21
|
+
Classifier: Topic :: System :: Archiving :: Backup
|
|
22
|
+
Classifier: Topic :: System :: Systems Administration
|
|
23
|
+
Requires-Python: >=3.11
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Dynamic: license-file
|
|
27
|
+
|
|
28
|
+
# DBaRA — Docker Backup and Restore Application
|
|
29
|
+
|
|
30
|
+
[](https://github.com/NASible/DBaRA/actions/workflows/ci.yml)
|
|
31
|
+
[](https://pypi.org/project/dbara/)
|
|
32
|
+
[](https://pypi.org/project/dbara/)
|
|
33
|
+
|
|
34
|
+
A production-ready Python CLI for backing up and restoring Docker application data directories.
|
|
35
|
+
Stops the container, archives the data, verifies integrity, and restarts.
|
|
36
|
+
|
|
37
|
+
## Features
|
|
38
|
+
|
|
39
|
+
- **change detection**
|
|
40
|
+
- **checksums**
|
|
41
|
+
- **retry logic**
|
|
42
|
+
- **hooks**
|
|
43
|
+
- **per-app locking**
|
|
44
|
+
|
|
45
|
+
## Requirements
|
|
46
|
+
|
|
47
|
+
- **Linux Desktop/Server OS**
|
|
48
|
+
- **Python 3.11+**
|
|
49
|
+
- **Docker** in `PATH`
|
|
50
|
+
- **zstd** or **gzip** (zstd recommended)
|
|
51
|
+
- **xxh128sum** (optional — falls back to sha256sum when absent)
|
|
52
|
+
|
|
53
|
+
## Installation
|
|
54
|
+
|
|
55
|
+
### From PyPI (recommended)
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
pipx install dbara
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Or into an isolated virtualenv:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
python3 -m venv /opt/dbara
|
|
65
|
+
/opt/dbara/bin/pip install dbara
|
|
66
|
+
ln -s /opt/dbara/bin/dbara /usr/local/bin/dbara
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### From source
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
git clone https://github.com/NASible/DBaRA.git
|
|
73
|
+
cd DBaRA
|
|
74
|
+
pipx install .
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Verify the install:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
dbara --version
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Examples
|
|
86
|
+
|
|
87
|
+
### Backup — single app
|
|
88
|
+
|
|
89
|
+
Stop the container, archive the data directory, verify the checksum, then restart:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
dbara -m backup \
|
|
93
|
+
-a jellyfin \
|
|
94
|
+
-d /srv/apps \
|
|
95
|
+
-b /srv/backups \
|
|
96
|
+
--keep-last 7 \
|
|
97
|
+
-v
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
`--keep-last 7` automatically prunes archives older than the 7 most recent.
|
|
101
|
+
`-v` enables debug output. Use `-vv` to also write a logfile.
|
|
102
|
+
|
|
103
|
+
### Backup — all apps
|
|
104
|
+
|
|
105
|
+
Iterate every subdirectory under `--app-folder-dir`, skipping apps whose contents have not
|
|
106
|
+
changed since the last backup:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
dbara -m backup \
|
|
110
|
+
-A \
|
|
111
|
+
-d /srv/apps \
|
|
112
|
+
-b /srv/backups \
|
|
113
|
+
-p prod \
|
|
114
|
+
--keep-last 7
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
`-p prod` inserts `prod` into every archive filename:
|
|
118
|
+
`myhost_prod_jellyfin_20240615_020000.bkup.tar.zst`
|
|
119
|
+
|
|
120
|
+
### Backup — force even when unchanged
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
dbara -m backup -a sonarr -d /srv/apps -b /srv/backups -f
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
`-f / --force` bypasses change detection and always creates a new archive.
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
### Restore — single app (latest archive)
|
|
131
|
+
|
|
132
|
+
Auto-selects the most recent archive for the app by modification time:
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
dbara -m restore \
|
|
136
|
+
-a jellyfin \
|
|
137
|
+
-d /srv/apps \
|
|
138
|
+
-b /srv/backups \
|
|
139
|
+
-r /srv/apps \
|
|
140
|
+
-o 1000:1000
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
`-o 1000:1000` runs `chown -R 1000:1000` on the restored directory.
|
|
144
|
+
|
|
145
|
+
### Restore — single app (specific archive)
|
|
146
|
+
|
|
147
|
+
Pass the archive path directly to `--backup-dest-dir` to restore a particular snapshot:
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
dbara -m restore \
|
|
151
|
+
-a jellyfin \
|
|
152
|
+
-d /srv/apps \
|
|
153
|
+
-b /srv/backups/myhost_jellyfin_20240615_020000.bkup.tar.zst \
|
|
154
|
+
-r /srv/apps
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Restore — several specific apps
|
|
158
|
+
|
|
159
|
+
`--apps` takes a space-separated list and restores each one sequentially:
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
dbara -m restore \
|
|
163
|
+
--apps authelia authentik vaultwarden \
|
|
164
|
+
-d /srv/apps \
|
|
165
|
+
-b /srv/backups \
|
|
166
|
+
-r /srv/apps \
|
|
167
|
+
--ownership-map /etc/dbara/owners.txt
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Using `--ownership-map` instead of `-o` applies the right ownership per app automatically.
|
|
171
|
+
|
|
172
|
+
### Restore — all apps
|
|
173
|
+
|
|
174
|
+
Auto-discovers every app present in the backup directory and restores each one.
|
|
175
|
+
Useful after a full server rebuild:
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
dbara -m restore \
|
|
179
|
+
-A \
|
|
180
|
+
-d /srv/apps \
|
|
181
|
+
-b /srv/backups \
|
|
182
|
+
-r /srv/apps \
|
|
183
|
+
--ownership-map /etc/dbara/owners.txt \
|
|
184
|
+
-v
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
> **Note:** `-A` for restore infers app names from archive filenames. App names that contain
|
|
188
|
+
> underscores (e.g. `my_app`) will not be auto-discovered — use `--apps` explicitly for those.
|
|
189
|
+
|
|
190
|
+
### Restore — without restarting the container
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
dbara -m restore \
|
|
194
|
+
-a sonarr \
|
|
195
|
+
-d /srv/apps \
|
|
196
|
+
-b /srv/backups \
|
|
197
|
+
-r /srv/apps \
|
|
198
|
+
-s
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
`-s / --no-start-container` extracts the data but leaves the container stopped.
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## Archive Filename Format
|
|
206
|
+
|
|
207
|
+
```text
|
|
208
|
+
{hostname}_{prefix_}{app}_{YYYYMMDD_HHMMSS}.bkup.tar.{ext}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Examples:
|
|
212
|
+
|
|
213
|
+
| Scenario | Filename |
|
|
214
|
+
| -------- | -------- |
|
|
215
|
+
| No prefix, zstd | `myhost_jellyfin_20240615_020000.bkup.tar.zst` |
|
|
216
|
+
| With prefix "prod" | `myhost_prod_jellyfin_20240615_020000.bkup.tar.zst` |
|
|
217
|
+
| gzip compressor | `myhost_jellyfin_20240615_020000.bkup.tar.tgz` |
|
|
218
|
+
|
|
219
|
+
Checksum sidecar files sit next to the archive:
|
|
220
|
+
|
|
221
|
+
- `...bkup.tar.zst.xxh128` (when xxh128sum is available)
|
|
222
|
+
- `...bkup.tar.zst.sha256sum` (fallback)
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
## CLI Reference
|
|
227
|
+
|
|
228
|
+
### Required flags
|
|
229
|
+
|
|
230
|
+
| Flag | Description |
|
|
231
|
+
| ---- | ----------- |
|
|
232
|
+
| `-m, --mode` | Operation: `backup` or `restore` |
|
|
233
|
+
| `-d, --app-folder-dir` | Parent directory containing per-app folders |
|
|
234
|
+
| `-b, --backup-dest-dir` | Backup destination directory (or specific archive file for restore) |
|
|
235
|
+
|
|
236
|
+
### Common options
|
|
237
|
+
|
|
238
|
+
| Flag | Default | Description |
|
|
239
|
+
| ---- | ------- | ----------- |
|
|
240
|
+
| `-a, --app-name` | — | Single app to back up or restore |
|
|
241
|
+
| `--apps APP [APP ...]` | — | Restore a specific list of apps (restore mode only) |
|
|
242
|
+
| `-A, --all-apps` | — | Backup: every subdirectory under `--app-folder-dir`; Restore: every app found in `--backup-dest-dir` |
|
|
243
|
+
| `-r, --restore-dest-dir` | — | Where to extract restored files |
|
|
244
|
+
| `-o, --owner-group` | — | Set `user:group` ownership after restore (e.g. `1000:1000`) |
|
|
245
|
+
| `-p, --optional-prefix` | — | Prefix inserted in the archive filename |
|
|
246
|
+
| `-x, --remove-after-backup` | off | Delete app directory after backup (**archive integrity verified first**) |
|
|
247
|
+
| `-s, --no-start-container` | off | Do not restart the container after the operation |
|
|
248
|
+
| `-f, --force` | off | Force backup even when no changes are detected |
|
|
249
|
+
| `--strict` | off | Treat any warning as a fatal error |
|
|
250
|
+
| `--skip-checksum` | off | Skip checksum generation/verification |
|
|
251
|
+
| `-v, --verbose` | — | `-v` enables debug output; `-vv` also writes a logfile |
|
|
252
|
+
|
|
253
|
+
### Performance / behaviour
|
|
254
|
+
|
|
255
|
+
| Flag | Default | Description |
|
|
256
|
+
| ---- | ------- | ----------- |
|
|
257
|
+
| `--compress` | `zstd` | Compressor: `zstd` or `gzip` |
|
|
258
|
+
| `--zstd-opts` | `-T0 -3` | Options forwarded to `zstd` |
|
|
259
|
+
| `--fast-hash / --no-fast-hash` | enabled | Prefer `xxh128sum` over `sha256sum` when available |
|
|
260
|
+
| `--io-nice / --no-io-nice` | enabled | Wrap heavy I/O with `ionice -c2 -n7 nice -n 19` |
|
|
261
|
+
| `--keep-last N` | `0` (disabled) | Prune all but the last N archives per app |
|
|
262
|
+
| `--state-dir` | `/var/lib/app-backup/state` | Directory for change-detection `.sig` state files |
|
|
263
|
+
| `--hooks-dir` | `/etc/app-backup/hooks` | Root directory for per-app hook scripts |
|
|
264
|
+
| `--lock-dir` | `/var/lock` | Directory for per-app lock files |
|
|
265
|
+
| `--ownership-map` | — | File mapping app names to `user:group` ownership |
|
|
266
|
+
| `--stop-timeout` | `30` | Seconds passed to `docker stop -t` |
|
|
267
|
+
| `--retry-max` | `5` | Maximum retry attempts for shell commands |
|
|
268
|
+
| `--retry-base-sleep` | `2` | Base sleep for exponential backoff: `base ** attempt` seconds |
|
|
269
|
+
| `--logfile` | `/var/log/app-backup-restore.log` | Log file path (written when `-vv` is active) |
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## Hooks
|
|
274
|
+
|
|
275
|
+
Place executable scripts at `HOOKS_DIR/<app>/<hook-name>`:
|
|
276
|
+
|
|
277
|
+
```text
|
|
278
|
+
/etc/app-backup/hooks/
|
|
279
|
+
├── jellyfin/
|
|
280
|
+
│ ├── pre-backup
|
|
281
|
+
│ ├── post-backup
|
|
282
|
+
│ ├── pre-restore
|
|
283
|
+
│ └── post-restore
|
|
284
|
+
└── sonarr/
|
|
285
|
+
└── pre-backup
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
Hook scripts receive no arguments. The backup or restore is **aborted** if a hook exits non-zero
|
|
289
|
+
(subject to `--retry-max` retries with exponential backoff).
|
|
290
|
+
|
|
291
|
+
Example pre-backup hook that flushes a database:
|
|
292
|
+
|
|
293
|
+
```bash
|
|
294
|
+
#!/bin/bash
|
|
295
|
+
sqlite3 /srv/apps/myapp/data.db "PRAGMA wal_checkpoint(FULL);"
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
---
|
|
299
|
+
|
|
300
|
+
## Ownership Map
|
|
301
|
+
|
|
302
|
+
Use `--ownership-map` to map app names to `user:group` ownership without specifying `-o` on every
|
|
303
|
+
restore command:
|
|
304
|
+
|
|
305
|
+
```ini
|
|
306
|
+
# /etc/app-backup/owners.txt
|
|
307
|
+
jellyfin=1000:1000
|
|
308
|
+
sonarr=1001:1001
|
|
309
|
+
radarr=1001:1001
|
|
310
|
+
# Lines starting with # are ignored
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
```bash
|
|
314
|
+
dbara -m restore -a jellyfin -d /srv/apps -b /srv/backups -r /srv/apps \
|
|
315
|
+
--ownership-map /etc/app-backup/owners.txt
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
If `--owner-group` is also specified it takes precedence over the map file.
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
## Change Detection
|
|
323
|
+
|
|
324
|
+
DBaRA computes a SHA-256 digest over the sorted list of `(relative_path, file_size, mtime_ns)`
|
|
325
|
+
tuples for every file under the app directory. The digest is stored as a `.sig` file in
|
|
326
|
+
`--state-dir`. If the digest is unchanged since the last run, the backup is skipped.
|
|
327
|
+
|
|
328
|
+
- Use `-f / --force` to override and backup anyway.
|
|
329
|
+
- State files are per-app: `{state-dir}/{app}.sig`.
|
|
330
|
+
|
|
331
|
+
---
|
|
332
|
+
|
|
333
|
+
## SQLite Safe Backup
|
|
334
|
+
|
|
335
|
+
Before archiving, DBaRA automatically creates consistent hot-backup copies of every SQLite
|
|
336
|
+
database it finds (`.sqlite`, `.sqlite3`, `.db`) using Python's built-in `sqlite3.backup()`.
|
|
337
|
+
The copies are stored as `<file>.bak` alongside the originals, included in the archive, and then
|
|
338
|
+
deleted after the tar operation completes.
|
|
339
|
+
|
|
340
|
+
This ensures the archive contains a consistent point-in-time snapshot even if the application
|
|
341
|
+
was writing to the database at the time of backup.
|
|
342
|
+
|
|
343
|
+
---
|
|
344
|
+
|
|
345
|
+
## Running as a Scheduled Job
|
|
346
|
+
|
|
347
|
+
### systemd timer (recommended)
|
|
348
|
+
|
|
349
|
+
```ini
|
|
350
|
+
# /etc/systemd/system/dbara-backup.timer
|
|
351
|
+
[Unit]
|
|
352
|
+
Description=Nightly DBaRA backup
|
|
353
|
+
|
|
354
|
+
[Timer]
|
|
355
|
+
OnCalendar=*-*-* 02:00:00
|
|
356
|
+
RandomizedDelaySec=300
|
|
357
|
+
Persistent=true
|
|
358
|
+
|
|
359
|
+
[Install]
|
|
360
|
+
WantedBy=timers.target
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
```ini
|
|
364
|
+
# /etc/systemd/system/dbara-backup.service
|
|
365
|
+
[Unit]
|
|
366
|
+
Description=DBaRA backup
|
|
367
|
+
|
|
368
|
+
[Service]
|
|
369
|
+
Type=oneshot
|
|
370
|
+
ExecStart=/usr/local/bin/dbara -m backup -A \
|
|
371
|
+
-d /srv/apps -b /srv/backups \
|
|
372
|
+
--keep-last 7 -vv
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
```bash
|
|
376
|
+
systemctl enable --now dbara-backup.timer
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
### cron
|
|
380
|
+
|
|
381
|
+
```cron
|
|
382
|
+
0 2 * * * root /usr/local/bin/dbara -m backup -A -d /srv/apps -b /srv/backups --keep-last 7
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
---
|
|
386
|
+
|
|
387
|
+
## Deployment with Ansible
|
|
388
|
+
|
|
389
|
+
An Ansible role is available in a separate repository:
|
|
390
|
+
[**NASible/ansible-role-dbara**](https://github.com/NASible/ansible-role-dbara)
|
|
391
|
+
|
|
392
|
+
```bash
|
|
393
|
+
ansible-galaxy install nasible.dbara
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
The role installs DBaRA into a virtualenv, creates required directories,
|
|
397
|
+
and configures a systemd timer for scheduled backups.
|
|
398
|
+
|
|
399
|
+
---
|
|
400
|
+
|
|
401
|
+
## Development
|
|
402
|
+
|
|
403
|
+
```bash
|
|
404
|
+
# Install dev dependencies
|
|
405
|
+
python3 -m venv .venv
|
|
406
|
+
.venv/bin/pip install -r requirements-dev.txt -e .
|
|
407
|
+
|
|
408
|
+
# Run tests
|
|
409
|
+
.venv/bin/pytest
|
|
410
|
+
|
|
411
|
+
# Type-check
|
|
412
|
+
.venv/bin/mypy dbara
|
|
413
|
+
|
|
414
|
+
# Lint
|
|
415
|
+
.venv/bin/ruff check dbara tests
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
All tests are pure unit tests — no Docker, no real filesystem writes (except where `tmp_path`
|
|
419
|
+
is used), no subprocess calls. The `FakeRunner` and `FakeDockerClient` test doubles in
|
|
420
|
+
`tests/conftest.py` cover every code path without touching the system.
|