Add Gatus auto-sync from Caddyfile, promote ensure_yq to lib/common.sh

Adds one Gatus endpoint per Caddy site block automatically, tagged
group: caddy-sync — the sync only ever adds/removes entries in that
exact group, so anything added by hand (the default external checks,
a custom endpoint) is never touched regardless of what the Caddyfile
looks like. Offered at install time (syncs once immediately) and, if
systemd is available, scheduled via a timer every 15 minutes so a site
added or removed later gets picked up without re-running the installer
— matches the "schedule that checks the Caddyfile" shape asked for.

Domain extraction tracks actual brace depth (reusing the same approach
as remove_service's Caddy block removal) rather than a naive
line-by-line scan, so it correctly skips the global options block and
parenthesized snippet definitions like (authelia) without needing to
special-case them by name.

Verified end-to-end against a real Caddyfile/config.yaml fixture with
the actual mikefarah/yq binary: initial sync adds the right entries
and leaves the default "external" group alone, a second run with no
Caddyfile changes is a true no-op (0 added, 0 removed), and changing
the Caddyfile (removing one site, adding another) correctly adds the
new endpoint and removes only the stale one.

Also fixes a real gap surfaced while building this: ensure_yq (used by
both gatus.sh now and onlyoffice.sh already) checked `command -v yq`
alone, which a box can satisfy with a completely different, incompatible
yq — confirmed live in this environment, Debian/Ubuntu's own `yq`
apt package is kislyuk/yq (a Python jq-wrapper) which silently errors
on mikefarah/yq's `e '.path' file` syntax every caller here depends on.
Now checks the version string actually identifies as mikefarah's
before trusting it, installing to /usr/local/bin (which precedes
/usr/bin on Ubuntu's default PATH) if not. Promoted ensure_yq itself
from onlyoffice.sh (its only previous user) to lib/common.sh now that
gatus.sh needs the same thing, so both share one implementation.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H4k6J1qXXyYxhGEgnJaMvn
This commit is contained in:
Claude
2026-08-11 04:26:08 +00:00
parent d4ebbe715b
commit cd003dbaf3
3 changed files with 185 additions and 13 deletions
+27
View File
@@ -244,6 +244,33 @@ ensure_caddy_network() {
&& log_info "Created Docker network ${_net} (needed by Caddy-fronted services)"
}
# Installs yq v4 (Go binary release, not the Python click-based yq some
# distros package under the same name) if not already present. Shared by
# any service that needs to patch YAML config robustly instead of
# hand-rolling sed/awk text surgery — originally lived only in
# services/onlyoffice.sh (FileBrowser config patching); promoted here once
# services/gatus.sh needed the same thing (endpoint sync from Caddyfile),
# so both use one implementation instead of two copies drifting apart.
ensure_yq() {
# Not just `command -v yq` — confirmed live, a box can already have
# `yq` on PATH that's actually kislyuk/yq (the Python jq-wrapper apt
# packages under the same name on Debian/Ubuntu), which silently
# errors on mikefarah's `e '.path' file` syntax every caller here
# uses ("argument files: can't open '.path'"). Check the version
# string actually identifies as mikefarah's before trusting it.
yq --version 2>/dev/null | grep -q mikefarah && return 0
log_info "Installing yq..."
local _arch; _arch=$(uname -m)
local _binary="yq_linux_amd64"
[[ "$_arch" == "aarch64" || "$_arch" == "arm64" ]] && _binary="yq_linux_arm64"
# /usr/local/bin precedes /usr/bin on Ubuntu's default PATH, so this
# correctly shadows a wrong /usr/bin/yq without needing to touch or
# remove it (something else on the box may depend on the real one).
curl -fsSL "https://github.com/mikefarah/yq/releases/latest/download/${_binary}" \
-o /usr/local/bin/yq && chmod +x /usr/local/bin/yq \
&& log_success "yq installed" || log_warning "yq install failed"
}
# Enables UFW if it isn't already active. Call this AFTER the caller has
# already added its own `ufw allow` rules for whatever it needs — this only
# flips UFW from inactive to active, it doesn't add rules for the calling