ops: paivana and caddy config refresh
This commit is contained in:
parent
d8dbc6242b
commit
56f814a6de
74 changed files with 4517 additions and 79 deletions
217
scripts/caddy/caddy-apply.sh
Executable file
217
scripts/caddy/caddy-apply.sh
Executable 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue