ops: paivana and caddy config refresh

This commit is contained in:
Hernâni Marques 2026-09-09 00:52:54 +02:00
parent d8dbc6242b
commit 56f814a6de
No known key found for this signature in database
74 changed files with 4517 additions and 79 deletions

217
scripts/caddy/caddy-apply.sh Executable file
View file

@ -0,0 +1,217 @@
#!/usr/bin/env bash
# Generic: merge a site snippet into a Caddyfile + ACME http:// list + ports comment.
#
# Laptop (mirror only, no reload):
# bash scripts/caddy/caddy-apply.sh \
# --config configs/caddy/Caddyfile \
# --snippet configs/caddy/galene.hacktivism.ch.caddy \
# --site galene.hacktivism.ch --port 9029 --label galene \
# --no-reload
#
# Koopa live (root, Freigabe):
# sudo bash …/caddy-apply.sh \
# --snippet /home/hernani/koopa-admin-log/configs/caddy/galene.hacktivism.ch.caddy \
# --site galene.hacktivism.ch --port 9029 --label galene
set -euo pipefail
CADDY=/etc/caddy/Caddyfile
SNIPPET=""
SITE=""
PORT=""
LABEL=""
INSERT_AFTER="decidim.hacktivism.ch"
ACME_ANCHOR="http://decidim.hacktivism.ch"
NO_RELOAD=0
DRY=0
OLD_SITES=()
usage() {
sed -n '2,20p' "$0" | sed 's/^# \{0,1\}//'
echo "Options: --config PATH --snippet PATH --site HOST --port N --label NAME"
echo " --insert-after HOST --acme-anchor http://HOST --old-site HOST"
echo " --no-reload --dry-run"
}
while [[ $# -gt 0 ]]; do
case "$1" in
--config) CADDY=$2; shift 2 ;;
--snippet) SNIPPET=$2; shift 2 ;;
--site) SITE=$2; shift 2 ;;
--port) PORT=$2; shift 2 ;;
--label) LABEL=$2; shift 2 ;;
--insert-after) INSERT_AFTER=$2; shift 2 ;;
--acme-anchor) ACME_ANCHOR=$2; shift 2 ;;
--old-site) OLD_SITES+=("$2"); shift 2 ;;
--no-reload) NO_RELOAD=1; shift ;;
--dry-run) DRY=1; shift ;;
-h|--help) usage; exit 0 ;;
*) echo "ERROR: unknown arg: $1" >&2; usage >&2; exit 2 ;;
esac
done
[[ -n "$SNIPPET" && -n "$SITE" ]] || { echo "ERROR: --snippet and --site required" >&2; exit 2; }
[[ -f "$CADDY" ]] || { echo "ERROR: missing $CADDY" >&2; exit 1; }
[[ -f "$SNIPPET" ]] || { echo "ERROR: missing $SNIPPET" >&2; exit 1; }
LABEL="${LABEL:-$SITE}"
if [[ "$CADDY" == /etc/caddy/Caddyfile && "$(id -u)" -ne 0 && "$DRY" -eq 0 ]]; then
echo "ERROR: live Caddyfile needs root (or --dry-run / --config mirror)" >&2
exit 1
fi
ts=$(date +%Y%m%d-%H%M%S)
if [[ "$DRY" -eq 0 ]]; then
cp -a "$CADDY" "${CADDY}.bak-caddy-apply-${ts}"
echo "backup ${CADDY}.bak-caddy-apply-${ts}"
fi
export CADDY SNIPPET SITE PORT LABEL INSERT_AFTER ACME_ANCHOR DRY
export OLD_SITES_CSV
OLD_SITES_CSV=$(IFS=,; echo "${OLD_SITES[*]-}")
python3 - <<'PY'
import os, re, sys
from pathlib import Path
caddy = Path(os.environ["CADDY"])
snip = Path(os.environ["SNIPPET"]).read_text().rstrip() + "\n\n"
site = os.environ["SITE"]
port = os.environ.get("PORT") or ""
label = os.environ.get("LABEL") or site
insert_after = os.environ["INSERT_AFTER"]
acme_anchor = os.environ["ACME_ANCHOR"]
dry = os.environ.get("DRY") == "1"
old_sites = [s for s in os.environ.get("OLD_SITES_CSV", "").split(",") if s]
text = caddy.read_text()
orig = text
def drop_site_block(text: str, old_site: str) -> str:
marker_old = f"{old_site} {{"
i = text.find(marker_old)
if i < 0:
return text
j = text.find("\n}", i)
if j < 0:
return text
end = j + 2
while end < len(text) and text[end] == "\n":
end += 1
text = text[:i] + text[end:]
text = text.replace(f", http://{old_site}", "").replace(f"http://{old_site}, ", "")
text = text.replace(f"http://{old_site}", "")
print(f"OK: removed duplicate old block {old_site}")
return text
for old_site in old_sites:
if old_site in text and site not in text:
text = text.replace(old_site, site)
print(f"OK: renamed {old_site} → {site}")
elif old_site in text and site in text:
text = drop_site_block(text, old_site)
marker = f"{site} {{"
if marker in text:
print(f"OK: site block already present: {site}")
else:
insert_at = None
after = text.find(f"{insert_after} {{")
if after >= 0:
j = text.find("\n}", after)
if j >= 0:
insert_at = j + 2
while insert_at < len(text) and text[insert_at] == "\n":
insert_at += 1
if insert_at is None:
acme = text.find("http://taler.hacktivism.ch")
insert_at = acme if acme >= 0 else len(text)
text = text[:insert_at] + snip + text[insert_at:]
print(f"OK: inserted site block {site}")
http_tok = f"http://{site}"
if http_tok in text:
print(f"OK: ACME http list already has {http_tok}")
else:
# Prefer exact "anchor {" form; else append before " {" of the long http list
old = f"{acme_anchor} {{"
new = f"{acme_anchor}, {http_tok} {{"
if old in text:
text = text.replace(old, new, 1)
print(f"OK: added {http_tok} to ACME http list (anchor)")
else:
# Find the shared ACME line (starts with http://taler…)
m = re.search(r"(http://taler\.hacktivism\.ch[^\n]*?)(\s*\{)", text)
if not m:
raise SystemExit("ERROR: ACME http list not found")
line = m.group(1)
if http_tok in line:
print(f"OK: ACME http list already has {http_tok}")
else:
text = text[: m.start(1)] + line.rstrip() + f", {http_tok}" + text[m.end(1) :]
print(f"OK: added {http_tok} to ACME http list (append)")
# Ports comment header (first matching line starting with "# 9020 castopod")
if port:
token = f"{port} {label}"
def upd_hdr(line: str) -> str:
if token in line or f"| {port} " in line or f"| {port}|" in line:
return line
# insert before " | 9200 forgejo-ssh" if present, else before end
if "9200 forgejo-ssh" in line:
return line.replace(" | 9200 forgejo-ssh", f" | {token} | 9200 forgejo-ssh", 1)
if line.rstrip().endswith("forgejo-ssh"):
return line.rstrip() + f" | {token}\n"
return line.rstrip() + f" | {token}\n"
lines = text.splitlines(keepends=True)
changed = False
for i, line in enumerate(lines):
if line.startswith("# 9020 castopod"):
new_line = upd_hdr(line)
if new_line != line:
lines[i] = new_line if new_line.endswith("\n") else new_line + "\n"
print(f"OK: updated ports comment (+{token})")
changed = True
else:
print(f"OK: ports comment already has {token}")
break
if changed:
text = "".join(lines)
if text != orig:
if dry:
print(f"DRY: would write {caddy} ({len(text) - len(orig):+d} bytes)")
else:
caddy.write_text(text)
print(f"wrote {caddy}")
else:
print("no Caddyfile change")
PY
if [[ "$DRY" -eq 1 ]]; then
echo "dry-run done (no validate/reload)"
exit 0
fi
if command -v caddy >/dev/null 2>&1; then
echo "== validate =="
caddy validate --config "$CADDY"
else
echo "WARN: caddy binary missing — skip validate" >&2
fi
if [[ "$NO_RELOAD" -eq 1 ]]; then
echo "skip reload (--no-reload)"
exit 0
fi
if [[ "$CADDY" == /etc/caddy/Caddyfile ]] && command -v systemctl >/dev/null 2>&1; then
echo "== reload =="
if systemctl is-active --quiet caddy; then
systemctl reload caddy
else
echo "WARN: caddy unit not active — start it yourself" >&2
fi
else
echo "skip reload (not live path or no systemctl)"
fi