From 730625e36cd6b15782cc9609eda4e52b934ef918 Mon Sep 17 00:00:00 2001 From: CroneKorkN Date: Sun, 10 May 2026 15:36:19 +0200 Subject: [PATCH] libs/hooks/bin: add one-line module docstrings and # purpose: headers every libs/*.py and hooks/*.py now starts with a one-line module docstring; every bin/* script starts with a `# purpose:` header. discovery-by-`ls`-and-read instead of by index. Co-Authored-By: Claude Opus 4.7 (1M context) --- bin/mikrotik-firmware-updater | 1 + bin/passwords-for | 1 + bin/rcon | 1 + bin/script_template | 1 + bin/sync_1password | 1 + bin/timestamp_icloud_photos_for_nextcloud | 1 + bin/upgrade_and_restart_all | 1 + bin/wake | 1 + bin/wireguard-client-config | 1 + hooks/known_hosts.py | 2 ++ hooks/skip_local_nodes.py | 2 ++ hooks/test_ptr_records.py | 2 ++ hooks/unique_node_ids.py | 3 +++ hooks/wake_on_lan.py | 3 +++ libs/apt.py | 2 ++ libs/bind.py | 2 ++ libs/derive_string.py | 1 + libs/grafana.py | 2 ++ libs/hashable.py | 2 ++ libs/hmac.py | 2 ++ libs/ini.py | 2 ++ libs/ip.py | 2 ++ libs/local.py | 2 ++ libs/nextcloud.py | 3 +++ libs/nginx.py | 3 +++ libs/postgres.py | 2 ++ libs/rsa.py | 2 ++ libs/ssh.py | 2 ++ libs/systemd.py | 2 ++ libs/tools.py | 2 ++ libs/version.py | 2 ++ libs/wireguard.py | 2 ++ libs/wol.py | 2 ++ 33 files changed, 60 insertions(+) diff --git a/bin/mikrotik-firmware-updater b/bin/mikrotik-firmware-updater index 3a26429..de18437 100755 --- a/bin/mikrotik-firmware-updater +++ b/bin/mikrotik-firmware-updater @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# purpose: upgrade RouterOS and routerboard firmware on `bundle:routeros` (or any selector) — usage: mikrotik-firmware-updater [...] [--yes]. from argparse import ArgumentParser from time import sleep diff --git a/bin/passwords-for b/bin/passwords-for index d0c3e51..8afbd26 100755 --- a/bin/passwords-for +++ b/bin/passwords-for @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# purpose: print node.password and selected metadata-key passwords for one node — usage: passwords-for . from bundlewrap.repo import Repository from os.path import realpath, dirname diff --git a/bin/rcon b/bin/rcon index 41c2705..f83eb04 100755 --- a/bin/rcon +++ b/bin/rcon @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# purpose: send an RCON command to a left4dead2 server defined in node metadata — usage: rcon (list) | rcon . from sys import argv from os.path import realpath, dirname diff --git a/bin/script_template b/bin/script_template index 52e5297..e354612 100755 --- a/bin/script_template +++ b/bin/script_template @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# purpose: starter template for new operator scripts under bin/. from bundlewrap.repo import Repository from os.path import realpath, dirname diff --git a/bin/sync_1password b/bin/sync_1password index c7bf3ca..c006f7c 100755 --- a/bin/sync_1password +++ b/bin/sync_1password @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# purpose: upsert one 1Password login per `bundle:routeros` node, keyed on the bw node id. from bundlewrap.repo import Repository from os.path import realpath, dirname diff --git a/bin/timestamp_icloud_photos_for_nextcloud b/bin/timestamp_icloud_photos_for_nextcloud index a5a4daa..30d6144 100644 --- a/bin/timestamp_icloud_photos_for_nextcloud +++ b/bin/timestamp_icloud_photos_for_nextcloud @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# purpose: add missing EXIF/QuickTime timestamps to photos in a directory using mdls + exiftool — usage: timestamp_icloud_photos_for_nextcloud -d . from subprocess import check_output, CalledProcessError from datetime import datetime, timedelta diff --git a/bin/upgrade_and_restart_all b/bin/upgrade_and_restart_all index 8c25c5b..492b496 100755 --- a/bin/upgrade_and_restart_all +++ b/bin/upgrade_and_restart_all @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# purpose: apt-update and full-upgrade every non-dummy debian node, then reboot in WireGuard-aware order. from bundlewrap.repo import Repository from os.path import realpath, dirname diff --git a/bin/wake b/bin/wake index 78415ad..dd0051b 100755 --- a/bin/wake +++ b/bin/wake @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# purpose: wake one node via WoL by name — usage: wake . from bundlewrap.repo import Repository from os.path import realpath, dirname diff --git a/bin/wireguard-client-config b/bin/wireguard-client-config index 0cd1d5d..b038094 100755 --- a/bin/wireguard-client-config +++ b/bin/wireguard-client-config @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# purpose: print or QR-render a WireGuard client config from htz.mails metadata — usage: wireguard-client-config . from bundlewrap.repo import Repository from os.path import realpath, dirname diff --git a/hooks/known_hosts.py b/hooks/known_hosts.py index d3ae014..61dec0e 100644 --- a/hooks/known_hosts.py +++ b/hooks/known_hosts.py @@ -1,3 +1,5 @@ +"""known_hosts: pre-apply hook that writes all nodes' ssh/is_known_as entries to ~/.ssh/known_hosts_ckn.""" + from os.path import expanduser diff --git a/hooks/skip_local_nodes.py b/hooks/skip_local_nodes.py index 1f419c6..24393e5 100644 --- a/hooks/skip_local_nodes.py +++ b/hooks/skip_local_nodes.py @@ -1,3 +1,5 @@ +"""skip_local_nodes: skip apply on `localhost` nodes whose metadata `id` doesn't match this host's local_id.""" + from bundlewrap.exceptions import SkipNode def node_apply_start(repo, node, interactive, **kwargs): diff --git a/hooks/test_ptr_records.py b/hooks/test_ptr_records.py index cbccf50..a868c60 100644 --- a/hooks/test_ptr_records.py +++ b/hooks/test_ptr_records.py @@ -1,3 +1,5 @@ +"""test_ptr_records: bw-test gate verifying live A/PTR DNS records for the `mailserver` group via `dig @9.9.9.9`.""" + from subprocess import check_output diff --git a/hooks/unique_node_ids.py b/hooks/unique_node_ids.py index 450a830..50bc4b5 100644 --- a/hooks/unique_node_ids.py +++ b/hooks/unique_node_ids.py @@ -1,3 +1,6 @@ +"""unique_node_ids: bw-test + pre-apply gate ensuring metadata `id` is unique across all nodes.""" + + def test_unique_node_ids(repo): ids = {} for node in repo.nodes: diff --git a/hooks/wake_on_lan.py b/hooks/wake_on_lan.py index 35519ce..944d3ad 100644 --- a/hooks/wake_on_lan.py +++ b/hooks/wake_on_lan.py @@ -1,3 +1,6 @@ +"""wake_on_lan: pre-apply / pre-run hook that wakes a node via libs.wol before bw connects to it.""" + + def wake_on_lan(node): node.repo.libs.wol.wake(node) diff --git a/libs/apt.py b/libs/apt.py index 97e2d65..b97064d 100644 --- a/libs/apt.py +++ b/libs/apt.py @@ -1,3 +1,5 @@ +"""apt: render sources.list.d entries and resolve apt-key file extensions, with codename/version templating.""" + from re import match from glob import glob from os.path import join, basename, exists diff --git a/libs/bind.py b/libs/bind.py index b3e40f7..201f9bf 100644 --- a/libs/bind.py +++ b/libs/bind.py @@ -1,3 +1,5 @@ +"""bind: DNS view-routing helpers — match A/AAAA records against internal vs external views via repo-wide collation.""" + from ipaddress import ip_address def _values_from_all_nodes(type, name, zone): diff --git a/libs/derive_string.py b/libs/derive_string.py index 828103c..291f7d1 100644 --- a/libs/derive_string.py +++ b/libs/derive_string.py @@ -1,4 +1,5 @@ #!/usr/bin/env python +"""derive_string: deterministic byte-string derivation from a seed via ChaCha20 keystream.""" from hashlib import sha3_256 from itertools import count, islice diff --git a/libs/grafana.py b/libs/grafana.py index 73dc1ec..852fdb4 100644 --- a/libs/grafana.py +++ b/libs/grafana.py @@ -1,3 +1,5 @@ +"""grafana: panel and Flux-query generators for Mako-templated dashboards under data/grafana/rows/.""" + from mako.template import Template from copy import deepcopy diff --git a/libs/hashable.py b/libs/hashable.py index ab9aa71..a9e2df4 100644 --- a/libs/hashable.py +++ b/libs/hashable.py @@ -1,3 +1,5 @@ +"""hashable: dict/set subclasses with stable __hash__ via canonical JSON — lets you nest dicts/sets inside sets in metadata.""" + import json from functools import total_ordering diff --git a/libs/hmac.py b/libs/hmac.py index 44321c3..0499c60 100644 --- a/libs/hmac.py +++ b/libs/hmac.py @@ -1,3 +1,5 @@ +"""hmac: base64-encoded HMAC-SHA512 helper.""" + import hmac, hashlib, base64 def hmac_sha512(secret, iv): diff --git a/libs/ini.py b/libs/ini.py index f530144..38efaa3 100644 --- a/libs/ini.py +++ b/libs/ini.py @@ -1,3 +1,5 @@ +"""ini: case-preserving ConfigParser parse/dumps for INI-style files where exact key casing must round-trip.""" + from configparser import ConfigParser import json from bundlewrap.metadata import MetadataJSONEncoder diff --git a/libs/ip.py b/libs/ip.py index b0dc78f..42f1352 100644 --- a/libs/ip.py +++ b/libs/ip.py @@ -1,3 +1,5 @@ +"""ip: A/AAAA-record derivation from a node's `network` metadata + cross-node IP collection.""" + from ipaddress import ip_address, ip_interface diff --git a/libs/local.py b/libs/local.py index 5bb776f..5ec5a21 100644 --- a/libs/local.py +++ b/libs/local.py @@ -1,3 +1,5 @@ +"""local: identifier of the bw host itself (read from ~/.config/bundlewrap/local_id) — paired with hooks/skip_local_nodes.py.""" + from os.path import expanduser, exists from functools import cache diff --git a/libs/nextcloud.py b/libs/nextcloud.py index 78e4086..cab160a 100644 --- a/libs/nextcloud.py +++ b/libs/nextcloud.py @@ -1,2 +1,5 @@ +"""nextcloud: build `sudo -u www-data php occ ...` shell strings for use in actions.""" + + def occ(command, *args, **kwargs): return f"""sudo -u www-data php /opt/nextcloud/occ {command} {' '.join(args)} {' '.join(f'--{name.replace("_", "-")}' + (f'={value}' if value else '') for name, value in kwargs.items())}""" diff --git a/libs/nginx.py b/libs/nginx.py index dc83e25..2177c72 100644 --- a/libs/nginx.py +++ b/libs/nginx.py @@ -1,3 +1,6 @@ +"""nginx: recursive nginx config-block rendering from a nested dict.""" + + def render_config(config): return '\n'.join(render_lines(config)) diff --git a/libs/postgres.py b/libs/postgres.py index ed80036..3e61d4a 100644 --- a/libs/postgres.py +++ b/libs/postgres.py @@ -1,3 +1,5 @@ +"""postgres: SCRAM-SHA-256 password-hash generation for postgres role provisioning.""" + from base64 import standard_b64encode from hashlib import pbkdf2_hmac, sha256 import hmac diff --git a/libs/rsa.py b/libs/rsa.py index 60caabd..b3c905d 100644 --- a/libs/rsa.py +++ b/libs/rsa.py @@ -1,3 +1,5 @@ +"""rsa: deterministic RSA private-key generation backed by a seeded PRNG.""" + # https://stackoverflow.com/a/18266970 from Crypto.PublicKey import RSA diff --git a/libs/ssh.py b/libs/ssh.py index 1a5576d..a0f9cf8 100644 --- a/libs/ssh.py +++ b/libs/ssh.py @@ -1,3 +1,5 @@ +"""ssh: deterministic ed25519 keypair generation and salted known_hosts hashing.""" + from base64 import b64decode, b64encode from hashlib import sha3_224, sha1 from functools import cache diff --git a/libs/systemd.py b/libs/systemd.py index 2315cc2..173c1be 100644 --- a/libs/systemd.py +++ b/libs/systemd.py @@ -1,3 +1,5 @@ +"""systemd: unit-file rendering (Mako) and a reusable hardening-options dict for sandboxed services.""" + from mako.template import Template template = ''' diff --git a/libs/tools.py b/libs/tools.py index 1756e7b..d4a00de 100644 --- a/libs/tools.py +++ b/libs/tools.py @@ -1,3 +1,5 @@ +"""tools: identifier→IPs resolver (node|group|cidr), subnet-set deduplication, and bundle-requirement assertion.""" + from ipaddress import ip_address, ip_network, IPv4Address, IPv4Network from bundlewrap.exceptions import NoSuchGroup, NoSuchNode, BundleError diff --git a/libs/version.py b/libs/version.py index aaed9aa..35b78cb 100644 --- a/libs/version.py +++ b/libs/version.py @@ -1,3 +1,5 @@ +"""version: comparable Version class for dotted-int version strings.""" + from functools import total_ordering diff --git a/libs/wireguard.py b/libs/wireguard.py index 283f561..edbab82 100644 --- a/libs/wireguard.py +++ b/libs/wireguard.py @@ -1,3 +1,5 @@ +"""wireguard: deterministic WireGuard private/public key + PSK derivation, backed by repo.vault.random_bytes_as_base64_for.""" + import base64 from nacl.public import PrivateKey from nacl.encoding import Base64Encoder diff --git a/libs/wol.py b/libs/wol.py index d54cde8..9da95b0 100644 --- a/libs/wol.py +++ b/libs/wol.py @@ -1,3 +1,5 @@ +"""wol: trigger a `wol-sleeper` waker node to run its configured wake command.""" + from bundlewrap.utils.ui import io from bundlewrap.utils.text import yellow, bold