Repository navigation
v0.3.7 — QA sweep
Post-ship QA sweep on v0.3.6 caught 10 issues across the CLI, all fixed. Every bug has a regression test.
Fixes
- LocalTransport `backup_dir` permission crash — hand-written `transport: local` configs without an explicit `backup_dir` now default to `{target_dir}/.satdeploy-backups` (writable) instead of `/opt/satdeploy/backups` (root-only).
- `push --force` no-op rollback bug — forcing an identical binary used to create a redundant backup of the current file, which became the newest backup entry and made the next `rollback` a silent no-op. Now `--force` of an identical file skips the backup entirely, so the chain stays honest.
- Ghost "deployed" status — `satdeploy status` showed "deployed" for apps that were configured but never pushed. Fixed: `LocalTransport.get_status` now reports only apps whose file is actually on the target.
- Hash-skip was silent on csp/local push — re-pushing the same file said "Deployed" as if it worked. Now prints "already deployed, use --force".
- `--require-clean` finally implemented — the flag was documented in README and CLAUDE.md but never existed. Now present: `satdeploy push --require-clean` refuses to deploy from a dirty git tree.
- `satdeploy --version` flag — now exists. Reads from `importlib.metadata` so there's one source of truth in `pyproject.toml`.
- `satdeploy config` now shows `target_dir` for the local transport (was silently omitted before).
- `satdeploy logs` error message no longer says "is a library" for apps that just have `service: null` — now names the real cause and points at the fix.
Tests
- +9 new regression tests in `tests/test_transport_local.py` covering the local transport interface, fresh deploy, hash-skip, force-identical-backup regression, status semantics, and rollback edge cases.
- 368 passing, 0 failures.
Full QA report: `.gstack/qa-reports/qa-report-satdeploy-cli-2026-04-14.md` (not shipped to PyPI).