refactor: simplify start.sh to standard webmention-sender pattern

Remove complex PID tracking, readiness probe, graceful-shutdown and
daemon-parent machinery. Replace with the minimal polling loop from
the @rmdes/indiekit-endpoint-webmention-sender README: generate JWT,
POST to 127.0.0.1 (local), sleep 300s. Also drop the related tuning
vars from .env (WEBMENTION_SENDER_READY_TIMEOUT, STOP_TIMEOUT,
INDIEKIT_STOP_TIMEOUT, KILL_DAEMON_PARENT_ON_SHUTDOWN).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
svemagie
2026-03-11 19:47:46 +01:00
parent fefd91dcf5
commit 9c90498cbc

View File

@@ -3,239 +3,6 @@ set -eu
cd /usr/local/indiekit
NODE_BIN="${NODE_BIN:-/usr/local/bin/node}"
WEBMENTION_POLL_PID=""
INDIEKIT_PID=""
SHUTDOWN_IN_PROGRESS=0
WEBMENTION_STOP_TIMEOUT="${WEBMENTION_SENDER_STOP_TIMEOUT:-5}"
INDIEKIT_STOP_TIMEOUT="${INDIEKIT_STOP_TIMEOUT:-20}"
WEBMENTION_READY_TIMEOUT="${WEBMENTION_SENDER_READY_TIMEOUT:-180}"
KILL_DAEMON_PARENT_ON_SHUTDOWN="${KILL_DAEMON_PARENT_ON_SHUTDOWN:-1}"
case "$WEBMENTION_STOP_TIMEOUT" in
''|*[!0-9]*) WEBMENTION_STOP_TIMEOUT=5 ;;
esac
case "$INDIEKIT_STOP_TIMEOUT" in
''|*[!0-9]*) INDIEKIT_STOP_TIMEOUT=20 ;;
esac
case "$WEBMENTION_READY_TIMEOUT" in
''|*[!0-9]*) WEBMENTION_READY_TIMEOUT=60 ;;
esac
case "$KILL_DAEMON_PARENT_ON_SHUTDOWN" in
''|*[!0-9]*) KILL_DAEMON_PARENT_ON_SHUTDOWN=1 ;;
esac
is_pid_alive() {
_pid="$1"
if [ -z "$_pid" ] || ! kill -0 "$_pid" 2>/dev/null; then
return 1
fi
# FreeBSD can report zombies as existing PIDs; exclude them from "alive".
if command -v ps >/dev/null 2>&1; then
_state="$(ps -o stat= -p "$_pid" 2>/dev/null || true)"
case "$_state" in
*Z*) return 1 ;;
esac
fi
return 0
}
wait_for_pid_exit() {
_pid="$1"
_timeout="$2"
_elapsed=0
while is_pid_alive "$_pid"; do
if [ "$_elapsed" -ge "$_timeout" ]; then
return 1
fi
sleep 1
_elapsed=$((_elapsed + 1))
done
wait "$_pid" 2>/dev/null || true
return 0
}
stop_webmention_poller() {
if [ -n "${WEBMENTION_POLL_PID}" ] && is_pid_alive "${WEBMENTION_POLL_PID}"; then
kill "${WEBMENTION_POLL_PID}" 2>/dev/null || true
if ! wait_for_pid_exit "${WEBMENTION_POLL_PID}" "${WEBMENTION_STOP_TIMEOUT}"; then
kill -9 "${WEBMENTION_POLL_PID}" 2>/dev/null || true
wait "${WEBMENTION_POLL_PID}" 2>/dev/null || true
fi
fi
WEBMENTION_POLL_PID=""
}
stop_indiekit_server() {
if [ -n "${INDIEKIT_PID}" ] && is_pid_alive "${INDIEKIT_PID}"; then
kill "${INDIEKIT_PID}" 2>/dev/null || true
if ! wait_for_pid_exit "${INDIEKIT_PID}" "${INDIEKIT_STOP_TIMEOUT}"; then
echo "[indiekit] Shutdown timeout after ${INDIEKIT_STOP_TIMEOUT}s; forcing kill" >&2
kill -9 "${INDIEKIT_PID}" 2>/dev/null || true
wait "${INDIEKIT_PID}" 2>/dev/null || true
fi
fi
INDIEKIT_PID=""
}
stop_daemon_parent() {
if [ "$KILL_DAEMON_PARENT_ON_SHUTDOWN" != "1" ]; then
return
fi
if ! command -v ps >/dev/null 2>&1; then
return
fi
_ppid="${PPID:-}"
if [ -z "$_ppid" ] || ! kill -0 "$_ppid" 2>/dev/null; then
return
fi
_parent_cmd="$(ps -o command= -p "$_ppid" 2>/dev/null || true)"
case "$_parent_cmd" in
daemon:\ *\(daemon\)*|*/daemon\ *)
kill "$_ppid" 2>/dev/null || true
;;
esac
}
start_webmention_poller() {
if [ "${WEBMENTION_SENDER_AUTO_POLL:-1}" != "1" ]; then
return
fi
WEBMENTION_SENDER_HOST="${WEBMENTION_SENDER_HOST:-127.0.0.1}"
WEBMENTION_SENDER_PORT="${WEBMENTION_SENDER_PORT:-${PORT:-3000}}"
WEBMENTION_SENDER_PATH="${WEBMENTION_SENDER_MOUNT_PATH:-/webmention-sender}"
WEBMENTION_SENDER_ORIGIN="${WEBMENTION_SENDER_ORIGIN:-${PUBLICATION_URL:-${SITE_URL:-}}}"
WEBMENTION_SENDER_INTERVAL="${WEBMENTION_SENDER_POLL_INTERVAL:-300}"
case "$WEBMENTION_SENDER_PATH" in
/*) ;;
*) WEBMENTION_SENDER_PATH="/$WEBMENTION_SENDER_PATH" ;;
esac
case "$WEBMENTION_SENDER_INTERVAL" in
''|*[!0-9]*) WEBMENTION_SENDER_INTERVAL=300 ;;
esac
WEBMENTION_SENDER_ORIGIN="${WEBMENTION_SENDER_ORIGIN%/}"
if ! command -v curl >/dev/null 2>&1; then
echo "[webmention] curl not found; skipping auto-send polling" >&2
return
fi
if [ -z "$WEBMENTION_SENDER_ORIGIN" ]; then
echo "[webmention] SITE_URL/PUBLICATION_URL missing; skipping auto-send polling" >&2
return
fi
# If host includes protocol (http/https), use as-is, else prepend http://
case "$WEBMENTION_SENDER_HOST" in
http*://*) WEBMENTION_SENDER_ENDPOINT="${WEBMENTION_SENDER_HOST}${WEBMENTION_SENDER_PATH}" ;;
*) WEBMENTION_SENDER_ENDPOINT="http://${WEBMENTION_SENDER_HOST}:${WEBMENTION_SENDER_PORT}${WEBMENTION_SENDER_PATH}" ;;
esac
# Wait for the local endpoint to answer (any HTTP status) before polling.
WEBMENTION_READY_ELAPSED=0
while true; do
if ! is_pid_alive "${INDIEKIT_PID}"; then
echo "[webmention] Indiekit exited before poller startup; skipping" >&2
return
fi
WEBMENTION_READY_CODE="$(
curl -sS -o /dev/null -m 2 -w '%{http_code}' "${WEBMENTION_SENDER_ENDPOINT}" 2>/dev/null || true
)"
case "$WEBMENTION_READY_CODE" in
200) break ;;
''|000|502|503|504) ;;
*) echo "[webmention] Waiting for HTTP 200, got $WEBMENTION_READY_CODE" >&2 ;;
esac
if [ "$WEBMENTION_READY_ELAPSED" -ge "$WEBMENTION_READY_TIMEOUT" ]; then
echo "[webmention] Startup readiness timeout after ${WEBMENTION_READY_TIMEOUT}s; starting poller anyway" >&2
break
fi
sleep 1
WEBMENTION_READY_ELAPSED=$((WEBMENTION_READY_ELAPSED + 1))
done
(
echo "[webmention] Starting auto-send polling every ${WEBMENTION_SENDER_INTERVAL}s (${WEBMENTION_SENDER_ENDPOINT})"
while true; do
if ! is_pid_alive "${INDIEKIT_PID}"; then
echo "[webmention] Indiekit stopped; exiting poller"
break
fi
TOKEN="$({
WEBMENTION_ORIGIN="$WEBMENTION_SENDER_ORIGIN" \
WEBMENTION_SECRET="$SECRET" \
"$NODE_BIN" -e '
const jwt = require("jsonwebtoken");
const me = process.env.WEBMENTION_ORIGIN;
const secret = process.env.WEBMENTION_SECRET;
if (!me || !secret) process.exit(1);
process.stdout.write(
jwt.sign({ me, scope: "update" }, secret, { expiresIn: "5m" }),
);
' 2>/dev/null;
} || true)"
if [ -n "$TOKEN" ]; then
RESULT="$(curl -sS -X POST "${WEBMENTION_SENDER_ENDPOINT}?token=${TOKEN}" 2>&1 || true)"
if [ -n "$RESULT" ]; then
echo "[webmention] $(date '+%Y-%m-%d %H:%M:%S') - $RESULT"
else
echo "[webmention] $(date '+%Y-%m-%d %H:%M:%S') - ok"
fi
else
echo "[webmention] $(date '+%Y-%m-%d %H:%M:%S') - token generation failed"
fi
sleep "$WEBMENTION_SENDER_INTERVAL"
done
) &
WEBMENTION_POLL_PID="$!"
}
shutdown() {
if [ "${SHUTDOWN_IN_PROGRESS}" = "1" ]; then
return
fi
SHUTDOWN_IN_PROGRESS=1
trap '' INT TERM HUP
# Stop poller first so shutdown does not generate connection-refused spam.
stop_webmention_poller
stop_indiekit_server
stop_daemon_parent
}
trap 'shutdown; exit 0' INT TERM HUP
# Optional: load environment from local .env file
# (dotenv syntax, supports spaces in values).
@@ -293,20 +60,37 @@ done
"${NODE_BIN}" node_modules/@indiekit/indiekit/bin/cli.js serve --config indiekit.config.mjs &
INDIEKIT_PID="$!"
start_webmention_poller
# Webmention sender — polls every 5 minutes (see @rmdes/indiekit-endpoint-webmention-sender README)
WEBMENTION_POLL_INTERVAL="${WEBMENTION_SENDER_POLL_INTERVAL:-300}"
WEBMENTION_ENDPOINT="http://127.0.0.1:${PORT:-3000}${WEBMENTION_SENDER_MOUNT_PATH:-/webmention-sender}"
WEBMENTION_ORIGIN="${PUBLICATION_URL:-${SITE_URL:-}}"
# Keep the parent shell responsive to TERM/HUP from rc(8)/daemon while the
# Node process runs. A blocking wait can delay trap execution on some shells.
INDIEKIT_EXIT_CODE=0
(
echo "[webmention] Starting auto-send polling every ${WEBMENTION_POLL_INTERVAL}s (${WEBMENTION_ENDPOINT})"
while true; do
TOKEN="$(
WEBMENTION_ORIGIN="$WEBMENTION_ORIGIN" WEBMENTION_SECRET="$SECRET" \
"${NODE_BIN}" -e '
const jwt = require("jsonwebtoken");
const me = process.env.WEBMENTION_ORIGIN;
const secret = process.env.WEBMENTION_SECRET;
if (!me || !secret) process.exit(1);
process.stdout.write(jwt.sign({ me, scope: "update" }, secret, { expiresIn: "5m" }));
' 2>/dev/null || true
)"
while is_pid_alive "${INDIEKIT_PID}"; do
sleep 1
done
if [ -n "$TOKEN" ]; then
RESULT="$(curl -sS -X POST "${WEBMENTION_ENDPOINT}?token=${TOKEN}" 2>&1 || true)"
echo "[webmention] $(date '+%Y-%m-%d %H:%M:%S') - ${RESULT:-ok}"
else
echo "[webmention] $(date '+%Y-%m-%d %H:%M:%S') - token generation failed"
fi
sleep "$WEBMENTION_POLL_INTERVAL"
done
) &
POLLER_PID="$!"
trap 'kill "${POLLER_PID}" 2>/dev/null || true' EXIT INT TERM
set +e
wait "${INDIEKIT_PID}"
INDIEKIT_EXIT_CODE="$?"
set -e
stop_webmention_poller
exit "${INDIEKIT_EXIT_CODE}"