feat: add json output for backup plans

This commit is contained in:
Eddie Nielsen 2026-03-24 14:46:37 +00:00
parent 2a8d7fc8b7
commit 1d2dc54964

View file

@ -1,4 +1,5 @@
import argparse
import json
import sys
from pathlib import Path
@ -49,7 +50,7 @@ def entry_to_dict(entry):
"exists": data.get("exists"),
"reason": data.get("reason"),
"compose_file": compose_file,
"source": data.get("source"),
"source": str(data.get("source")) if data.get("source") is not None else None,
}
@ -84,12 +85,23 @@ def normalize_entries(entries):
def build_plan(scan_root, entries, compose_count):
normalized = normalize_entries(entries)
return {
"root": str(scan_root),
"compose_files_found": compose_count,
"include": [e for e in normalized if e["bucket"] == "include"],
"review": [e for e in normalized if e["bucket"] == "review"],
"skip": [e for e in normalized if e["bucket"] == "skip"],
"summary": {
"include_count": sum(1 for e in normalized if e["bucket"] == "include"),
"review_count": sum(1 for e in normalized if e["bucket"] == "review"),
"skip_count": sum(1 for e in normalized if e["bucket"] == "skip"),
"missing_critical_count": sum(
1
for e in normalized
if e["bucket"] == "include" and not e.get("exists", True)
),
},
}
@ -141,6 +153,25 @@ def print_warnings(plan):
print(f" - {item['path']} (service={item['service']})")
def build_json_output(plan, borg_command=None):
output = {
"tool": "DockerVault",
"version": __version__,
"plan": plan,
"status": {
"ok": True,
"has_missing_critical": any(
not item.get("exists", True) for item in plan["include"]
),
},
}
if borg_command is not None:
output["borg_command"] = borg_command
return output
def build_parser():
parser = argparse.ArgumentParser(
description="DockerVault - Intelligent Docker backup discovery"
@ -169,6 +200,12 @@ def build_parser():
help="Generate borg command",
)
parser.add_argument(
"--json",
action="store_true",
help="Output machine-readable JSON",
)
parser.add_argument(
"--dry-run",
action="store_true",
@ -237,10 +274,7 @@ def main():
plan = build_plan(scan_root, entries, len(compose_files))
if not args.quiet:
print_plan(plan)
print_warnings(plan)
borg_shell_command = None
if args.repo:
try:
from .borg import build_borg_create_command, command_to_shell
@ -249,16 +283,33 @@ def main():
sys.exit(2)
try:
include_paths = [item["path"] for item in plan["include"] if item["path"] != "?"]
include_paths = [
item["path"] for item in plan["include"] if item["path"] != "?"
]
borg_command = build_borg_create_command(args.repo, include_paths)
print("\nSuggested borg create command")
print("============================")
print(command_to_shell(borg_command))
borg_shell_command = command_to_shell(borg_command)
except Exception as e:
print(f"ERROR: Failed to build borg command: {e}")
sys.exit(2)
if args.json:
print(
json.dumps(
build_json_output(plan, borg_shell_command),
indent=2,
sort_keys=False,
)
)
else:
if not args.quiet:
print_plan(plan)
print_warnings(plan)
if borg_shell_command:
print("\nSuggested borg create command")
print("============================")
print(borg_shell_command)
if args.automation:
has_missing = any(not item.get("exists", True) for item in plan["include"])
if has_missing: