feat(backup)!: opt-in hard restart and mandatory backups-dir

Rename --docker-compose-hard-restart-required to --hard-compose-restart
and change its default from ["mailu"] to [] (nargs="*"): the compose
down/up is now opt-in, so compose hosts pass "mailu" while swarm hosts,
where the dir is a stack whose overlay network collides with compose up,
pass nothing. Make --backups-dir mandatory (no /var/lib/backup/ default)
so a run can never silently target the wrong backup root.

BREAKING CHANGE: the old flag name is removed, the implicit mailu default
is gone, and --backups-dir must be passed explicitly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-12 14:53:13 +02:00
parent 45d3b0ad7c
commit 331931d617
3 changed files with 63 additions and 9 deletions

View File

@@ -247,8 +247,6 @@ def main() -> int:
print("Finished volume backups.", flush=True) print("Finished volume backups.", flush=True)
print("Handling Docker Compose services...", flush=True) print("Handling Docker Compose services...", flush=True)
handle_docker_compose_services( handle_docker_compose_services(args.compose_dir, args.hard_compose_restart)
args.compose_dir, args.docker_compose_hard_restart_required
)
return 0 return 0

View File

@@ -17,10 +17,10 @@ def parse_args() -> argparse.Namespace:
help="Path to the parent directory containing docker-compose setups", help="Path to the parent directory containing docker-compose setups",
) )
p.add_argument( p.add_argument(
"--docker-compose-hard-restart-required", "--hard-compose-restart",
nargs="+", nargs="*",
default=["mailu"], default=[],
help="Compose dir names that require 'docker-compose down && up -d' (default: mailu)", help="Compose dir names that require 'docker-compose down && up -d' (default: none; pass e.g. 'mailu' under compose where the DB cannot be backed up hot)",
) )
p.add_argument( p.add_argument(
@@ -35,8 +35,8 @@ def parse_args() -> argparse.Namespace:
) )
p.add_argument( p.add_argument(
"--backups-dir", "--backups-dir",
default="/var/lib/backup/", required=True,
help="Backup root directory (default: /var/lib/backup/)", help="Backup root directory (e.g. /var/lib/backup/)",
) )
p.add_argument( p.add_argument(

View File

@@ -251,5 +251,61 @@ class TestCompose(unittest.TestCase):
) )
class HardRestartArgTests(unittest.TestCase):
"""The hard-restart list defaults to empty (no compose down/up); callers
opt in per dir, e.g. compose hosts pass 'mailu' while swarm hosts, where
the dir is a stack whose overlay network collides with compose up, pass
nothing."""
def _parse(self, extra: List[str]):
import sys
from baudolo.backup import cli
argv = [
"baudolo",
"--compose-dir",
"/tmp",
"--backups-dir",
"/tmp/backup",
"--database-containers",
"postgres",
"--images-no-stop-required",
"redis",
*extra,
]
with patch.object(sys, "argv", argv):
return cli.parse_args()
def test_default_is_empty(self) -> None:
args = self._parse([])
self.assertEqual(args.hard_compose_restart, [])
def test_empty_flag_stays_empty(self) -> None:
args = self._parse(["--hard-compose-restart"])
self.assertEqual(args.hard_compose_restart, [])
def test_explicit_names_preserved(self) -> None:
args = self._parse(["--hard-compose-restart", "mailu", "foo"])
self.assertEqual(args.hard_compose_restart, ["mailu", "foo"])
def test_backups_dir_is_required(self) -> None:
import sys
from baudolo.backup import cli
argv = [
"baudolo",
"--compose-dir",
"/tmp",
"--database-containers",
"postgres",
"--images-no-stop-required",
"redis",
]
with patch.object(sys, "argv", argv), self.assertRaises(SystemExit):
cli.parse_args()
if __name__ == "__main__": if __name__ == "__main__":
unittest.main(verbosity=2) unittest.main(verbosity=2)