zpbs-backup 0.11.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.
- zpbs_backup-0.11.0/.github/workflows/ci.yml +42 -0
- zpbs_backup-0.11.0/.github/workflows/release.yml +117 -0
- zpbs_backup-0.11.0/.gitignore +33 -0
- zpbs_backup-0.11.0/CHANGELOG.md +205 -0
- zpbs_backup-0.11.0/LICENSE +21 -0
- zpbs_backup-0.11.0/Makefile +31 -0
- zpbs_backup-0.11.0/PKG-INFO +638 -0
- zpbs_backup-0.11.0/README.md +605 -0
- zpbs_backup-0.11.0/packaging/build-packages.sh +88 -0
- zpbs_backup-0.11.0/packaging/nfpm.yaml +67 -0
- zpbs_backup-0.11.0/packaging/scripts/postinstall.sh +67 -0
- zpbs_backup-0.11.0/packaging/scripts/postremove.sh +12 -0
- zpbs_backup-0.11.0/packaging/scripts/preremove.sh +15 -0
- zpbs_backup-0.11.0/pyproject.toml +100 -0
- zpbs_backup-0.11.0/src/zpbs_backup/__init__.py +3 -0
- zpbs_backup-0.11.0/src/zpbs_backup/backup.py +600 -0
- zpbs_backup-0.11.0/src/zpbs_backup/cli.py +761 -0
- zpbs_backup-0.11.0/src/zpbs_backup/config.py +360 -0
- zpbs_backup-0.11.0/src/zpbs_backup/lock.py +60 -0
- zpbs_backup-0.11.0/src/zpbs_backup/metrics.py +220 -0
- zpbs_backup-0.11.0/src/zpbs_backup/notify.py +248 -0
- zpbs_backup-0.11.0/src/zpbs_backup/pbs.py +537 -0
- zpbs_backup-0.11.0/src/zpbs_backup/retention.py +143 -0
- zpbs_backup-0.11.0/src/zpbs_backup/scheduler.py +175 -0
- zpbs_backup-0.11.0/src/zpbs_backup/zfs.py +544 -0
- zpbs_backup-0.11.0/systemd/zpbs-backup-prune.service +34 -0
- zpbs_backup-0.11.0/systemd/zpbs-backup-prune.timer +15 -0
- zpbs_backup-0.11.0/systemd/zpbs-backup.service +40 -0
- zpbs_backup-0.11.0/systemd/zpbs-backup.timer +14 -0
- zpbs_backup-0.11.0/tests/__init__.py +1 -0
- zpbs_backup-0.11.0/tests/test_backup.py +694 -0
- zpbs_backup-0.11.0/tests/test_cli_prune_locking.py +63 -0
- zpbs_backup-0.11.0/tests/test_cli_run_reporting.py +194 -0
- zpbs_backup-0.11.0/tests/test_cli_status.py +106 -0
- zpbs_backup-0.11.0/tests/test_config.py +381 -0
- zpbs_backup-0.11.0/tests/test_lock.py +117 -0
- zpbs_backup-0.11.0/tests/test_metrics.py +264 -0
- zpbs_backup-0.11.0/tests/test_notify.py +118 -0
- zpbs_backup-0.11.0/tests/test_pbs.py +339 -0
- zpbs_backup-0.11.0/tests/test_retention.py +121 -0
- zpbs_backup-0.11.0/tests/test_scheduler.py +198 -0
- zpbs_backup-0.11.0/tests/test_systemd_unit.py +153 -0
- zpbs_backup-0.11.0/tests/test_zfs.py +561 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
push:
|
|
6
|
+
# Branches only. A tag push is a release, and release.yml runs the tests there.
|
|
7
|
+
branches:
|
|
8
|
+
- '**'
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
test:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
name: Test
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v6
|
|
16
|
+
- uses: actions/setup-python@v6
|
|
17
|
+
with:
|
|
18
|
+
python-version: '3.11'
|
|
19
|
+
- run: pip install .[dev]
|
|
20
|
+
- run: pytest
|
|
21
|
+
|
|
22
|
+
lint:
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
name: Lint
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@v6
|
|
27
|
+
- uses: actions/setup-python@v6
|
|
28
|
+
with:
|
|
29
|
+
python-version: '3.11'
|
|
30
|
+
- run: pip install .[dev]
|
|
31
|
+
- run: ruff check .
|
|
32
|
+
|
|
33
|
+
typecheck:
|
|
34
|
+
runs-on: ubuntu-latest
|
|
35
|
+
name: Type check
|
|
36
|
+
steps:
|
|
37
|
+
- uses: actions/checkout@v6
|
|
38
|
+
- uses: actions/setup-python@v6
|
|
39
|
+
with:
|
|
40
|
+
python-version: '3.11'
|
|
41
|
+
- run: pip install .[dev]
|
|
42
|
+
- run: mypy
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- 'v*'
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: write
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
test:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
name: Test
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v6
|
|
17
|
+
- uses: actions/setup-python@v6
|
|
18
|
+
with:
|
|
19
|
+
python-version: '3.11'
|
|
20
|
+
- run: pip install .[dev]
|
|
21
|
+
- run: pytest
|
|
22
|
+
|
|
23
|
+
build-wheel:
|
|
24
|
+
runs-on: ubuntu-latest
|
|
25
|
+
needs: test
|
|
26
|
+
name: Build wheel
|
|
27
|
+
steps:
|
|
28
|
+
- uses: actions/checkout@v6
|
|
29
|
+
- uses: actions/setup-python@v6
|
|
30
|
+
with:
|
|
31
|
+
python-version: '3.11'
|
|
32
|
+
- run: pip install build twine
|
|
33
|
+
- run: python -m build
|
|
34
|
+
- run: twine check dist/*
|
|
35
|
+
- uses: actions/upload-artifact@v7
|
|
36
|
+
with:
|
|
37
|
+
name: wheel
|
|
38
|
+
path: dist/*.whl
|
|
39
|
+
- uses: actions/upload-artifact@v7
|
|
40
|
+
with:
|
|
41
|
+
name: sdist
|
|
42
|
+
path: dist/*.tar.gz
|
|
43
|
+
|
|
44
|
+
build-packages:
|
|
45
|
+
runs-on: ubuntu-latest
|
|
46
|
+
needs: build-wheel
|
|
47
|
+
name: Build packages
|
|
48
|
+
steps:
|
|
49
|
+
- uses: actions/checkout@v6
|
|
50
|
+
- uses: actions/setup-python@v6
|
|
51
|
+
with:
|
|
52
|
+
python-version: '3.11'
|
|
53
|
+
- name: Install nfpm
|
|
54
|
+
run: |
|
|
55
|
+
echo 'deb [trusted=yes] https://repo.goreleaser.com/apt/ /' | sudo tee /etc/apt/sources.list.d/goreleaser.list
|
|
56
|
+
sudo apt-get update
|
|
57
|
+
sudo apt-get install -y nfpm
|
|
58
|
+
- run: pip install build
|
|
59
|
+
- uses: actions/download-artifact@v8
|
|
60
|
+
with:
|
|
61
|
+
name: wheel
|
|
62
|
+
path: dist/
|
|
63
|
+
- run: bash packaging/build-packages.sh
|
|
64
|
+
- uses: actions/upload-artifact@v7
|
|
65
|
+
with:
|
|
66
|
+
name: packages
|
|
67
|
+
path: |
|
|
68
|
+
dist/*.deb
|
|
69
|
+
dist/*.rpm
|
|
70
|
+
|
|
71
|
+
upload-release:
|
|
72
|
+
runs-on: ubuntu-latest
|
|
73
|
+
needs:
|
|
74
|
+
- build-wheel
|
|
75
|
+
- build-packages
|
|
76
|
+
name: Upload to release
|
|
77
|
+
steps:
|
|
78
|
+
- uses: actions/download-artifact@v8
|
|
79
|
+
with:
|
|
80
|
+
name: wheel
|
|
81
|
+
path: dist/
|
|
82
|
+
- uses: actions/download-artifact@v8
|
|
83
|
+
with:
|
|
84
|
+
name: packages
|
|
85
|
+
path: dist/
|
|
86
|
+
- run: ls -lh dist/
|
|
87
|
+
- name: Create or upload release
|
|
88
|
+
env:
|
|
89
|
+
GH_TOKEN: ${{ github.token }}
|
|
90
|
+
GH_REPO: ${{ github.repository }}
|
|
91
|
+
run: |
|
|
92
|
+
if ! gh release view "${{ github.ref_name }}" >/dev/null 2>&1; then
|
|
93
|
+
gh release create "${{ github.ref_name }}" --generate-notes
|
|
94
|
+
fi
|
|
95
|
+
gh release upload "${{ github.ref_name }}" \
|
|
96
|
+
dist/*.whl dist/*.deb dist/*.rpm \
|
|
97
|
+
--clobber
|
|
98
|
+
|
|
99
|
+
publish-pypi:
|
|
100
|
+
runs-on: ubuntu-latest
|
|
101
|
+
needs: build-wheel
|
|
102
|
+
name: Publish to PyPI
|
|
103
|
+
environment: pypi
|
|
104
|
+
permissions:
|
|
105
|
+
contents: read
|
|
106
|
+
id-token: write
|
|
107
|
+
steps:
|
|
108
|
+
- uses: actions/download-artifact@v8
|
|
109
|
+
with:
|
|
110
|
+
name: wheel
|
|
111
|
+
path: dist/
|
|
112
|
+
- uses: actions/download-artifact@v8
|
|
113
|
+
with:
|
|
114
|
+
name: sdist
|
|
115
|
+
path: dist/
|
|
116
|
+
- run: ls -lh dist/
|
|
117
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# Distribution / packaging
|
|
7
|
+
build/
|
|
8
|
+
dist/
|
|
9
|
+
*.egg-info/
|
|
10
|
+
*.egg
|
|
11
|
+
|
|
12
|
+
# Virtual environments
|
|
13
|
+
.venv/
|
|
14
|
+
venv/
|
|
15
|
+
ENV/
|
|
16
|
+
|
|
17
|
+
# IDE
|
|
18
|
+
.idea/
|
|
19
|
+
.vscode/
|
|
20
|
+
*.swp
|
|
21
|
+
*.swo
|
|
22
|
+
|
|
23
|
+
# Testing
|
|
24
|
+
.pytest_cache/
|
|
25
|
+
.coverage
|
|
26
|
+
htmlcov/
|
|
27
|
+
|
|
28
|
+
# OS
|
|
29
|
+
.DS_Store
|
|
30
|
+
Thumbs.db
|
|
31
|
+
|
|
32
|
+
# Claude Code
|
|
33
|
+
.claude/
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.11.0] - 2026-11-09
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- `zpbs:change-detection` ZFS property (`legacy`, `data` or `metadata`), passed to `proxmox-backup-client` as `--change-detection-mode` for that dataset. Scheduled runs now honour a change-detection mode; before this, the mode could be set only with `run --change-detection-mode`, which the systemd timer never passes ([#1](https://github.com/ndemarco/zpbs-backup/issues/1)). The command-line flag still works and overrides the property for that run. A malformed value set with raw `zfs set` is reported and that dataset excluded, like the other zpbs properties. `status --json` gains a `change_detection` field.
|
|
12
|
+
- `zpbs_backup_dataset_last_backup_timestamp_seconds{dataset="<zfs dataset>"}`: Unix time of each dataset's newest snapshot on the backup server (after this run's backup if it succeeded, otherwise the newest existing one). A dataset with no snapshot has no series. `run --force` now reads the newest snapshot too, so the metric is present on forced runs.
|
|
13
|
+
- Unknown-property detection: each run logs a syslog warning (dataset and property name) for every locally set `zpbs:*` property zpbs-backup does not recognise, and exports `zpbs_backup_unknown_properties{dataset="..."}` with the count, only for datasets that have at least one. `status` lists them per dataset and `status --json` gains `unknown_properties`.
|
|
14
|
+
- Publishing to PyPI from the release workflow via trusted publishing, after `twine check` on the built wheel and sdist.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
- syslog is now controlled only by `ZPBS_SYSLOG`. Before, `ZPBS_NOTIFY=false` and `run --no-notify` also silenced syslog; they now silence only the hook script.
|
|
18
|
+
- `zpbs-backup set` rejects a property name it does not know, with an error listing the valid names. Before, any `zpbs:*` name was accepted.
|
|
19
|
+
- `set backup=false --clear` and `inherit all` clear every locally set `zpbs:*` property on the dataset, not only the known ones.
|
|
20
|
+
- The run's metrics are rendered once; the Pushgateway and the textfile receive identical content.
|
|
21
|
+
|
|
22
|
+
### Removed
|
|
23
|
+
- **Breaking:** the `zpbs_backup_last_success_timestamp_seconds` metric and its state file `/var/lib/zpbs-backup/state.json`. The file is no longer read or written; delete it by hand if you want it gone.
|
|
24
|
+
- The hidden `zpbs-backup notify` command group. Use `send-test-notification` or `show-config`.
|
|
25
|
+
- The `/usr/local/bin/pbs-send-notification` hook path. Rename the script to `/usr/local/bin/zpbs-send-notification`.
|
|
26
|
+
- The lock file no longer holds a process ID.
|
|
27
|
+
- `.gitlab-ci.yml` and `MANIFEST.in`.
|
|
28
|
+
|
|
29
|
+
### Breaking: stale-backup alerts
|
|
30
|
+
Alerts on `zpbs_backup_last_success_timestamp_seconds` will go silent, because the series no longer exists. Replace them with a per-dataset rule:
|
|
31
|
+
|
|
32
|
+
```yaml
|
|
33
|
+
- alert: ZpbsBackupStale
|
|
34
|
+
expr: time() - zpbs_backup_dataset_last_backup_timestamp_seconds > 2 * 86400
|
|
35
|
+
for: 15m
|
|
36
|
+
annotations:
|
|
37
|
+
summary: "No backup of {{ $labels.dataset }} on {{ $labels.instance }} for over 2 days"
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Datasets on a `weekly` or `monthly` schedule need a longer threshold (for example 9 days and 35 days). Pair it with an alert for runs that stop happening:
|
|
41
|
+
|
|
42
|
+
```yaml
|
|
43
|
+
- alert: ZpbsBackupRunMissing
|
|
44
|
+
expr: absent(zpbs_backup_last_run_timestamp_seconds)
|
|
45
|
+
for: 15m
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## [0.10.0] - 2026-09-23
|
|
49
|
+
|
|
50
|
+
### Added
|
|
51
|
+
- Continuous integration on every pull request and every branch push (`.github/workflows/ci.yml`): the test suite, `ruff` and `mypy` each run as their own required check. Before this, a workflow existed only for tag pushes, so a pull request reported no checks at all. `ruff` and `mypy` are configured in `pyproject.toml`; the repository passes all three.
|
|
52
|
+
- Configurable backup-id separator via `ZPBS_BACKUP_ID_SEPARATOR` (env or config file; default `-`). A multi-character separator such as `--` yields **reversible** backup-ids — you can recover the dataset path from the PBS group name by stripping the host and splitting on the separator — which the default `-` cannot guarantee, since `-` is also legal in ZFS dataset names (`pool/a-b` and `pool/a/b` both flatten to `pool-a-b`). The separator joins the hostname prefix too, so IDs split uniformly; it is validated against the PBS backup-id character set. Default behavior is unchanged.
|
|
53
|
+
|
|
54
|
+
### Removed
|
|
55
|
+
- **Breaking:** zpbs-backup no longer sends mail itself. `ZPBS_NOTIFY_EMAIL` and the `sendmail`/`mail` fallback are gone; a reader using it should install a script at `/usr/local/bin/zpbs-send-notification` instead (see the README's Notifications section). syslog, the script hook, `ZPBS_NOTIFY`/`--no-notify`, and `send-test-notification` are unaffected.
|
|
56
|
+
|
|
57
|
+
## [0.9.1] - 2026-09-19
|
|
58
|
+
|
|
59
|
+
### Fixed
|
|
60
|
+
- `zpbs_backup.prom` is now written mode `0644`. It was `0600` (the `mkstemp` default), so node_exporter, running as its own unprivileged user, could not read it and the metrics never reached Prometheus.
|
|
61
|
+
|
|
62
|
+
## [0.9.0] - 2026-09-18
|
|
63
|
+
|
|
64
|
+
### Added
|
|
65
|
+
- `ZPBS_TEXTFILE_DIR` environment variable: set to a node_exporter textfile collector directory to write run metrics to `<dir>/zpbs_backup.prom` instead of (or alongside) pushing to a Pushgateway. Same six metric names as the Pushgateway path. The write is atomic (temp file plus `os.replace`).
|
|
66
|
+
- `ZPBS_TEXTFILE_DIR` added to the diagnostic env-var display list next to `ZPBS_PUSHGATEWAY`.
|
|
67
|
+
|
|
68
|
+
### Notes
|
|
69
|
+
- `ZPBS_PUSHGATEWAY` and `ZPBS_TEXTFILE_DIR` are independent: set either, both, or neither. A missing or unwritable textfile directory logs an error to stderr and never fails the backup run.
|
|
70
|
+
|
|
71
|
+
## [0.8.0] - 2026-05-20
|
|
72
|
+
|
|
73
|
+
### Added
|
|
74
|
+
- Skip-unchanged optimization (default on): the `run` command skips datasets whose live filesystem is byte-identical to a snapshot already captured by the previous backup. Uses native ZFS `written` property and snapshot creation time — no state, no extra IO. For pools of hundreds of mostly-dormant datasets this elides nearly all PBS calls.
|
|
75
|
+
- Pre-flight clock-skew probe against the PBS HTTP `Date:` header. If skew exceeds the 60s safety margin (or the probe fails), the skip-unchanged optimization is disabled for that run and the tool falls back to schedule-only behavior. Never blocks the run.
|
|
76
|
+
|
|
77
|
+
### Notes
|
|
78
|
+
- `--force` continues to back up everything regardless of either check.
|
|
79
|
+
- The skew check is one HTTPS HEAD per run; not user-configurable.
|
|
80
|
+
|
|
81
|
+
## [0.7.0] - 2026-05-14
|
|
82
|
+
|
|
83
|
+
### Added
|
|
84
|
+
- `--change-detection-mode` option on `run`, passed through to `proxmox-backup-client`: `legacy` (full content read and hash comparison), `data` (content-based), or `metadata` (inode/mtime-based, fastest on large datasets). Unset leaves the client's own default in place.
|
|
85
|
+
- Prometheus Pushgateway integration: push 6 metrics after each backup run (last run timestamp, last success timestamp, duration, successful/failed/skipped dataset counts)
|
|
86
|
+
- `ZPBS_PUSHGATEWAY` environment variable: set to Pushgateway base URL to enable metrics; unset or empty disables (no-op)
|
|
87
|
+
- State persistence at `/var/lib/zpbs-backup/state.json`: `last_success_timestamp_seconds` survives failed runs so the staleness alert reflects the true last success
|
|
88
|
+
- `ZPBS_PUSHGATEWAY` added to diagnostic env-var display list
|
|
89
|
+
- GitHub Actions release workflow: a `v*` tag builds the wheel and the `.deb`/`.rpm` packages and publishes them as release assets.
|
|
90
|
+
|
|
91
|
+
### Notes
|
|
92
|
+
- The metrics and state-persistence work above was written against a `0.6.0` version bump that was never tagged or released. It first reached users here, in 0.7.0. No 0.6.0 release exists; a package reporting that version was built by hand from an untagged commit.
|
|
93
|
+
|
|
94
|
+
## [0.5.2] - 2026-02-12
|
|
95
|
+
|
|
96
|
+
### Fixed
|
|
97
|
+
- `status` showing "683 months ago" or "never" for recently backed-up datasets — PBS `list` returns `last-backup` field, not `backup-time`; now checks both
|
|
98
|
+
- Timestamp parsing handles missing, zero, or string (ISO 8601) values from PBS
|
|
99
|
+
|
|
100
|
+
### Added
|
|
101
|
+
- Automatically skip datasets with `canmount=off` or `mounted=no` (organizational/hierarchy-only datasets that hold no data)
|
|
102
|
+
|
|
103
|
+
## [0.5.0] - 2026-02-09
|
|
104
|
+
|
|
105
|
+
### Added
|
|
106
|
+
- `run --bg` / `run -b`: trigger backup in background via systemd, returns immediately
|
|
107
|
+
- POSIX short options for all commands: `-n` (dry-run), `-f` (force), `-d` (dataset), `-j` (json), `-v` (verbose), `-b` (background), `-c` (clear), `-s` (show-only), `-o` (orphans), `-r` (recursive)
|
|
108
|
+
|
|
109
|
+
## [0.4.2] - 2026-02-09
|
|
110
|
+
|
|
111
|
+
### Fixed
|
|
112
|
+
- Use actual ZFS mountpoint instead of assuming `/{dataset_name}` — fixes backup failures for datasets whose mountpoint differs from their name
|
|
113
|
+
- Skip datasets with `mountpoint=none` or `mountpoint=legacy` instead of failing
|
|
114
|
+
|
|
115
|
+
## [0.4.1] - 2026-02-08
|
|
116
|
+
|
|
117
|
+
### Changed
|
|
118
|
+
- `get` command: argument order changed to `get DATASET [PROPERTY]` — property defaults to showing all
|
|
119
|
+
- CLI hides `zpbs:` prefix from all user-facing output (use short names: backup, schedule, etc.)
|
|
120
|
+
- Permission denied errors now suggest using sudo
|
|
121
|
+
- Helpful hint on PBS privilege separation when connection check fails
|
|
122
|
+
|
|
123
|
+
## [0.4.0] - 2026-02-08
|
|
124
|
+
|
|
125
|
+
### Added
|
|
126
|
+
- `show-config` command: display active PBS configuration, source, and connectivity check
|
|
127
|
+
- `--verbose` flag shows all config sources in priority order
|
|
128
|
+
- `--json` flag for machine-parseable output (suitable for automation)
|
|
129
|
+
- `send-test-notification` command (replaces `notify test`)
|
|
130
|
+
- New config variables aligned with PBS API token terminology:
|
|
131
|
+
- `PBS_API_TOKEN_SECRET` (preferred over `PBS_PASSWORD`)
|
|
132
|
+
- `PBS_USER`, `PBS_API_TOKEN_NAME`, `PBS_SERVER`, `PBS_DATASTORE` (individual parts)
|
|
133
|
+
- Auto-composition of `PBS_REPOSITORY` from individual parts when not set explicitly
|
|
134
|
+
- Config source tracking: shows which file or env var provided each setting
|
|
135
|
+
- Per-user config file: `~/.config/zpbs-backup/pbs.conf`
|
|
136
|
+
- 40 new tests for config module
|
|
137
|
+
|
|
138
|
+
### Changed
|
|
139
|
+
- Config file search simplified to 2 locations: `~/.config/zpbs-backup/pbs.conf` and `/etc/zpbs-backup/pbs.conf`
|
|
140
|
+
- Removed legacy `/root` config file paths
|
|
141
|
+
- `notify test` and `notify config` are now hidden deprecated aliases
|
|
142
|
+
|
|
143
|
+
### Deprecated
|
|
144
|
+
- `notify test` — use `send-test-notification` instead
|
|
145
|
+
- `notify config` — use `show-config` instead
|
|
146
|
+
|
|
147
|
+
## [0.3.2] - 2026-02-06
|
|
148
|
+
|
|
149
|
+
### Added
|
|
150
|
+
- Upfront PBS connectivity check in status, audit, and run commands
|
|
151
|
+
- 30-second timeout on all PBS query commands (prevents indefinite hangs)
|
|
152
|
+
|
|
153
|
+
### Fixed
|
|
154
|
+
- Commands no longer hang when PBS server is unreachable
|
|
155
|
+
|
|
156
|
+
## [0.3.1] - 2026-02-06
|
|
157
|
+
|
|
158
|
+
### Fixed
|
|
159
|
+
- Graceful handling when non-root user lacks permission to read config files
|
|
160
|
+
- PBS namespace listing crash when API returns plain strings instead of objects
|
|
161
|
+
|
|
162
|
+
## [0.3.0] - 2026-02-06
|
|
163
|
+
|
|
164
|
+
### Added
|
|
165
|
+
- Native `.deb` and `.rpm` package builds via nfpm
|
|
166
|
+
- Makefile with `make packages`, `make deb`, `make rpm` targets
|
|
167
|
+
- GitLab CI/CD pipeline for building and publishing system packages
|
|
168
|
+
- GitLab Releases with downloadable `.deb` and `.rpm` assets
|
|
169
|
+
- GitHub Releases with `.deb` and `.rpm` assets (via GitLab CI mirror)
|
|
170
|
+
- Support for both amd64 and arm64 architectures
|
|
171
|
+
|
|
172
|
+
### Changed
|
|
173
|
+
- Version is now derived from `__init__.py` as single source of truth
|
|
174
|
+
(`pyproject.toml` uses dynamic versioning via hatchling)
|
|
175
|
+
|
|
176
|
+
## [0.2.0] - 2026-02-02
|
|
177
|
+
|
|
178
|
+
### Added
|
|
179
|
+
- Syslog support for centralized logging
|
|
180
|
+
- Email notification system with `notify` command group
|
|
181
|
+
- `zpbs-backup notify test` - Test notification configuration
|
|
182
|
+
- `zpbs-backup notify config` - Show notification settings
|
|
183
|
+
- Shell variable interpolation in PBS configuration files
|
|
184
|
+
- Hierarchical namespace creation (auto-creates parent namespaces)
|
|
185
|
+
- `--no-notify` flag for `run` command
|
|
186
|
+
|
|
187
|
+
### Changed
|
|
188
|
+
- Configuration files now support variable interpolation (e.g., `${PBS_USER}`)
|
|
189
|
+
- Improved error handling and logging throughout
|
|
190
|
+
|
|
191
|
+
## [0.1.0] - Initial Release
|
|
192
|
+
|
|
193
|
+
### Added
|
|
194
|
+
- Initial implementation of zpbs-backup
|
|
195
|
+
- Auto-discovery of ZFS datasets via custom properties
|
|
196
|
+
- Property inheritance through dataset hierarchy
|
|
197
|
+
- Schedule-aware backups (daily/weekly/monthly)
|
|
198
|
+
- Priority-based backup ordering
|
|
199
|
+
- Retention policy management
|
|
200
|
+
- Dry-run mode for all operations
|
|
201
|
+
- Audit mode for orphaned backups and missed datasets
|
|
202
|
+
- CLI commands: status, run, audit, prune, get, set, inherit
|
|
203
|
+
- Systemd service and timer units
|
|
204
|
+
- Configuration via environment variables or config files
|
|
205
|
+
- PBS namespace support with auto-derivation
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Nick DeMarco
|
|
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.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
.PHONY: wheel packages deb rpm clean test lint typecheck check
|
|
2
|
+
|
|
3
|
+
VERSION := $(shell python3 -c "import re; f=open('src/zpbs_backup/__init__.py').read(); print(re.search(r'__version__\s*=\s*\"(.*?)\"', f).group(1))")
|
|
4
|
+
|
|
5
|
+
wheel:
|
|
6
|
+
python3 -m build --wheel
|
|
7
|
+
|
|
8
|
+
packages:
|
|
9
|
+
bash packaging/build-packages.sh
|
|
10
|
+
|
|
11
|
+
deb:
|
|
12
|
+
ARCHES=amd64 bash packaging/build-packages.sh
|
|
13
|
+
@echo "Built: dist/zpbs-backup_$(VERSION)_amd64.deb"
|
|
14
|
+
|
|
15
|
+
rpm:
|
|
16
|
+
ARCHES=amd64 bash packaging/build-packages.sh
|
|
17
|
+
@echo "Built: dist/zpbs-backup-$(VERSION).x86_64.rpm"
|
|
18
|
+
|
|
19
|
+
test:
|
|
20
|
+
pytest
|
|
21
|
+
|
|
22
|
+
lint:
|
|
23
|
+
ruff check .
|
|
24
|
+
|
|
25
|
+
typecheck:
|
|
26
|
+
mypy
|
|
27
|
+
|
|
28
|
+
check: test lint typecheck
|
|
29
|
+
|
|
30
|
+
clean:
|
|
31
|
+
rm -rf build/ dist/ src/*.egg-info
|