From b12f80933dcdc2c8cff4d2945911262d92712c51 Mon Sep 17 00:00:00 2001 From: dtoro Date: Thu, 21 May 2026 22:29:10 +0200 Subject: [PATCH] render-vps-configs: print masked unified diff in --dry-run Lets the operator see exactly what would change on the VPS before applying. Secret values from the decrypted sops files are masked as so the diff is safe to paste into chat/PRs. --- bin/homelab | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/bin/homelab b/bin/homelab index aba2f93..aa3ef04 100755 --- a/bin/homelab +++ b/bin/homelab @@ -703,6 +703,7 @@ def _vps_send(remote_path: str, content: str, mode: str) -> None: def cmd_render_vps_configs(args: argparse.Namespace) -> int: """Re-render /etc/turnserver.conf + /opt/management.json on the IONOS netbird VPS from templates in vps/, substituting secrets decrypted from sops.""" + import difflib # Decrypt the two sops files we need. turn = _decrypt_secret("turn-shared-secret") auth = _decrypt_secret("netbird-authentik-oidc") @@ -727,6 +728,7 @@ def cmd_render_vps_configs(args: argparse.Namespace) -> int: plans.append({ **t, "rendered": rendered, + "current": current, "changed": current != rendered, "current_present": cat.returncode == 0 and bool(current), }) @@ -743,8 +745,29 @@ def cmd_render_vps_configs(args: argparse.Namespace) -> int: print("Nothing to do — every target matches the rendered template.") return 0 + # Show diffs (always — both dry-run and live). + print() + for p in plans: + if not p["changed"]: + continue + print(f"--- diff for {p['remote']} ---") + # Mask any sops-decrypted secret values so they don't print to stdout. + def _mask(s): + for v in subs.values(): + s = s.replace(v, "") + return s + diff = difflib.unified_diff( + _mask(p["current"]).splitlines(keepends=True), + _mask(p["rendered"]).splitlines(keepends=True), + fromfile=f"vps:{p['remote']}", + tofile=f"rendered:{p['tmpl']}", + n=3, + ) + sys.stdout.writelines(diff) + print() + if args.dry_run: - print("\n--- dry-run: not applying ---") + print("--- dry-run: not applying ---") return 0 if not args.yes: