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
<REDACTED> so the diff is safe to paste into chat/PRs.
This commit is contained in:
2026-05-21 22:29:10 +02:00
parent 8ef17dba3d
commit b12f80933d

View File

@@ -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, "<REDACTED>")
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: