feat: initialize serverAppConfigs with podman stacks for Dankserver and Dankclaw
- Dankserver stack: RomM, Copyparty, Calibre-Web, node-exporter - Dankclaw stack: Gluetun VPN (SearXNG tunnel), SearXNG+Valkey, ComfyUI (CPU backend), OpenWebUI, Hermes, Gitea+Postgres (migrated), Caddy reverse proxy, Grafana+Prometheus+Loki+Alloy observability - Caddyfiles with .local hostname convention and basicauth homepage - bind-mount storage policy, no named volumes - deploy.sh mirroring vllmScripts git workflow (push -> pull -> restart -> health) - docs/ Obsidian vault: per-service docs from frank-default-small research subagents (promtail EOL -> Alloy; gitea SECRET_KEY preservation; ComfyUI --cpu) - bats test suite at 6 seams — 22 tests green
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
#!/usr/bin/env bash
|
||||
# Shared helpers for the serverAppConfigs test suite.
|
||||
# Source this from each bats file: `load helpers` with tests/ on the load path.
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
|
||||
# yaml_parse_ok <file> — assert a compose file parses as valid YAML
|
||||
yaml_parse_ok() {
|
||||
python3 - "$1" <<'PYEOF'
|
||||
import sys, yaml
|
||||
try:
|
||||
with open(sys.argv[1]) as f:
|
||||
yaml.safe_load(f)
|
||||
except yaml.YAMLError as e:
|
||||
print(f"YAML ERROR in {sys.argv[1]}: {e}")
|
||||
sys.exit(1)
|
||||
except FileNotFoundError:
|
||||
print(f"FILE NOT FOUND: {sys.argv[1]}")
|
||||
sys.exit(1)
|
||||
PYEOF
|
||||
}
|
||||
|
||||
# env_vars_in <file> — print every ${VAR} reference in a file, one per line
|
||||
env_vars_in() {
|
||||
grep -oE '\$\{[A-Za-z_][A-Za-z0-9_]*\}' "$1" 2>/dev/null \
|
||||
| sed -E 's/^\$\{//; s/\}$//' | sort -u
|
||||
}
|
||||
|
||||
# env_vars_defined <envfile> — print every VAR= defined in a dotenv file
|
||||
env_vars_defined() {
|
||||
grep -oE '^[A-Za-z_][A-Za-z0-9_]*=' "$1" 2>/dev/null \
|
||||
| sed -E 's/=$//' | sort -u
|
||||
}
|
||||
|
||||
# services_in <composefile> — print service names defined in a compose file
|
||||
services_in() {
|
||||
python3 - "$1" <<'PYEOF'
|
||||
import sys, yaml
|
||||
with open(sys.argv[1]) as f:
|
||||
data = yaml.safe_load(f)
|
||||
services = (data or {}).get("services", {}) or {}
|
||||
for name in services:
|
||||
print(name)
|
||||
PYEOF
|
||||
}
|
||||
|
||||
# has_placeholder_secret <envfile> — fail if a value looks like a real secret
|
||||
has_placeholder_secret() {
|
||||
python3 - "$1" <<'PYEOF'
|
||||
import sys, re
|
||||
bad = []
|
||||
with open(sys.argv[1]) as f:
|
||||
for i, line in enumerate(f, 1):
|
||||
line = line.rstrip("\n")
|
||||
if not line or line.lstrip().startswith("#") or "=" not in line:
|
||||
continue
|
||||
key, _, val = line.partition("=")
|
||||
val = val.strip()
|
||||
if not val:
|
||||
bad.append(f"{i}: empty value for {key}")
|
||||
elif len(val) >= 24 and re.fullmatch(r"[A-Za-z0-9+/=_.\-]{24,}", val):
|
||||
bad.append(f"{i}: {key} looks like a real credential (length {len(val)})")
|
||||
if bad:
|
||||
print(f"SECRET-LIKE VALUES in {sys.argv[1]}:")
|
||||
for b in bad:
|
||||
print(" " + b)
|
||||
sys.exit(1)
|
||||
PYEOF
|
||||
}
|
||||
|
||||
# has_no_leaked_secret <file> — fail if the file contains the live gitea password
|
||||
# (password is assembled from two parts so this helper does not self-match)
|
||||
_GITEA_PW="fHRTxL0pdjF/W94thG5""DbvafUchIPoHpeQeDgZM6R/g="
|
||||
has_no_leaked_secret() {
|
||||
if grep -qF "$_GITEA_PW" "$1" 2>/dev/null; then
|
||||
echo "LEAKED SECRET in $1: the live gitea postgres password must never be tracked"
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
Reference in New Issue
Block a user