Files
computer-playbook/utils/handler/vault.py
T
kevinveenbirkenbachandClaude Opus 5 5137465c6f style: delete every comment the comments-valid policy rejects
3529 invalid comments in 528 files, grandfathered in while the linter
only saw the working-tree diff. Removal ran through the linter's own
collectors, so detection and deletion cannot drift: a comment whose
opener line is blank before it and whose closer line is blank after it
loses all its lines, anything else loses only the comment region, and a
line left holding a bare delimiter goes with it.

Five files keep their comments behind a nocheck marker instead. Four are
upstream configs whose inline docs are upstream's own (mautrix, systemd
journald, nextcloud-oidc-login, MediaWiki). In littlejs the
commented-out entries ARE the deactivation mechanism for an example
tile, so deleting them would delete configuration.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-18 03:22:02 +02:00

64 lines
2.1 KiB
Python

import subprocess
from typing import Any
import yaml
from yaml.dumper import SafeDumper
from yaml.loader import SafeLoader
class VaultScalar(str):
"""A subclass of str to represent vault-encrypted strings."""
__slots__ = ()
def _vault_constructor(loader, node):
"""Load a !vault block as a VaultScalar so the tag survives a round-trip."""
return VaultScalar(node.value)
def _vault_representer(dumper, data):
"""Custom representer to dump VaultScalar as literal blocks."""
return dumper.represent_scalar("!vault", data, style="|")
SafeLoader.add_constructor("!vault", _vault_constructor)
getattr(yaml, "CSafeLoader", SafeLoader).add_constructor("!vault", _vault_constructor)
SafeDumper.add_representer(VaultScalar, _vault_representer)
class VaultHandler:
def __init__(self, vault_password_file: str):
self.vault_password_file = vault_password_file
def encrypt_string(self, value: str, name: str) -> str:
"""Encrypt a string using ansible-vault."""
cmd = [
"ansible-vault",
"encrypt_string",
"--stdin-name",
name,
"--vault-password-file",
self.vault_password_file,
]
proc = subprocess.run(
cmd, input=value, capture_output=True, text=True, check=False
)
if proc.returncode != 0:
raise RuntimeError(f"ansible-vault encrypt_string failed:\n{proc.stderr}")
return proc.stdout
def encrypt_leaves(self, branch: dict[str, Any], vault_pw: str):
"""Recursively encrypt all leaves (plain text values) under the credentials section."""
for key, value in branch.items():
if isinstance(value, dict):
self.encrypt_leaves(value, vault_pw)
elif isinstance(value, str) and not value.lstrip().startswith(
"$ANSIBLE_VAULT"
):
snippet = self.encrypt_string(value, key)
lines = snippet.splitlines()
indent = len(lines[1]) - len(lines[1].lstrip())
body = "\n".join(line[indent:] for line in lines[1:])
branch[key] = VaultScalar(body)