Files
my-cd-webhook/install/install.sh
T
fisher 3a6fafb5c7 Generic host/repo/branch-aware CD on adnanh/webhook
Replaces the per-project deploy scripts and self-contained webhook
receivers with one manifest-driven implementation that lives outside the
application repositories and can be updated independently of them.

First targets: domaindingo test and prod on s5.
2026-08-23 07:49:52 +00:00

136 lines
4.7 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# Install the CD webhook receiver on this host.
#
# Idempotent: re-run it after editing targets.json, after rotating a secret, or
# after pulling a new version of this repository. It re-renders the hooks file
# and restarts the daemon; it never touches a project repository or uas-ng.
#
# Prerequisites:
# * adnanh/webhook on PATH (or WEBHOOK_BIN pointing at it)
# * a host-local secrets file, see secrets.env.example
# * this host present in targets.json under "hosts"
set -Eeuo pipefail
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
readonly REPO_ROOT="$(cd -- "${SCRIPT_DIR}/.." && pwd)"
readonly CONFIG_DIR="${XDG_CONFIG_HOME:-${HOME}/.config}/cd-webhook"
readonly HOOKS_FILE="${CONFIG_DIR}/hooks.json"
readonly SECRETS_FILE="${CONFIG_DIR}/secrets.env"
readonly SYSTEMD_DIR="${HOME}/.config/systemd/user"
readonly UNIT_NAME="cd-webhook.service"
info() { printf '\n\033[1m==> %s\033[0m\n' "$*"; }
ok() { printf ' ok %s\n' "$*"; }
die() { printf '\n\033[31mERROR\033[0m %s\n' "$*" >&2; exit 1; }
# ------------------------------------------------------------ prerequisites --
info "Checking prerequisites"
for tool in python3 docker curl flock stdbuf; do
command -v "$tool" >/dev/null 2>&1 || die "required tool not found: ${tool}"
done
ok "python3, docker, curl, flock, stdbuf"
WEBHOOK_BIN="${WEBHOOK_BIN:-$(command -v webhook || true)}"
[[ -n "$WEBHOOK_BIN" && -x "$WEBHOOK_BIN" ]] || die \
"adnanh/webhook not found. Install it, or set WEBHOOK_BIN=/path/to/webhook.
Debian/Ubuntu: sudo apt install webhook
Or a release binary from https://github.com/adnanh/webhook/releases"
ok "webhook binary: ${WEBHOOK_BIN}"
docker info >/dev/null 2>&1 || die \
"cannot talk to Docker as $(whoami). The deploy runs as this user, so this
account needs working access to the Docker socket it deploys through."
ok "docker reachable as $(whoami)"
# ------------------------------------------------------------- host config ---
info "Resolving this host in targets.json"
eval "$("${REPO_ROOT}/bin/cd-target" host-config)"
ok "host ${CD_HOST}, binding ${CD_BIND}:${CD_PORT}"
mapfile -t TARGETS < <("${REPO_ROOT}/bin/cd-target" list)
[[ ${#TARGETS[@]} -gt 0 ]] || die "no enabled targets for ${CD_HOST} in targets.json"
for target in "${TARGETS[@]}"; do
ok "target ${target}"
done
# ----------------------------------------------------------------- secrets ---
info "Checking the secrets file"
mkdir -p "$CONFIG_DIR"
chmod 700 "$CONFIG_DIR"
if [[ ! -f "$SECRETS_FILE" ]]; then
install -m 0600 "${REPO_ROOT}/secrets.env.example" "$SECRETS_FILE"
die "created a template at ${SECRETS_FILE}
Fill in one secret per repository, then re-run this installer.
The same value must be set as the Secret on the Gitea webhook."
fi
chmod 600 "$SECRETS_FILE"
ok "${SECRETS_FILE}"
# ------------------------------------------------------------------- hooks ---
info "Rendering hooks for ${CD_HOST}"
"${REPO_ROOT}/bin/cd-render-hooks" --output "$HOOKS_FILE" --secrets "$SECRETS_FILE"
# ------------------------------------------------------------------ systemd --
info "Installing the user service"
mkdir -p "$SYSTEMD_DIR"
sed -e "s|@WEBHOOK_BIN@|${WEBHOOK_BIN}|g" \
-e "s|@HOOKS_FILE@|${HOOKS_FILE}|g" \
-e "s|@BIND@|${CD_BIND}|g" \
-e "s|@PORT@|${CD_PORT}|g" \
"${REPO_ROOT}/etc/cd-webhook.service" > "${SYSTEMD_DIR}/${UNIT_NAME}"
ok "${SYSTEMD_DIR}/${UNIT_NAME}"
systemctl --user daemon-reload
systemctl --user enable "$UNIT_NAME" >/dev/null
systemctl --user restart "$UNIT_NAME"
ok "enabled and restarted"
# Survive logout, same as the other user services in this fleet.
loginctl enable-linger "$(whoami)" >/dev/null 2>&1 || true
sleep 1
systemctl --user is-active --quiet "$UNIT_NAME" \
|| die "the service did not stay running:
systemctl --user status ${UNIT_NAME}
journalctl --user -u ${UNIT_NAME} -n 50 --no-pager"
# ------------------------------------------------------------------ summary --
info "Done. Configure these webhooks in Gitea"
echo
printf ' %-28s %s\n' "Target URL" "http://${CD_BIND}:${CD_PORT}/hooks/<id> (below)"
printf ' %-28s %s\n' "HTTP Method" "POST"
printf ' %-28s %s\n' "Content Type" "application/json"
printf ' %-28s %s\n' "Trigger On" "Push Events"
printf ' %-28s %s\n' "Secret" "the matching value from ${SECRETS_FILE}"
echo
"${REPO_ROOT}/bin/cd-render-hooks" --redact >/dev/null 2>&1 || true
python3 - "$HOOKS_FILE" "$CD_BIND" "$CD_PORT" <<'PY'
import json, sys
hooks_file, bind, port = sys.argv[1], sys.argv[2], sys.argv[3]
with open(hooks_file) as fh:
hooks = json.load(fh)
for hook in hooks:
print(f" http://{bind}:{port}/hooks/{hook['id']}")
PY
echo
echo " Logs: journalctl --user -u ${UNIT_NAME} -f"
echo " Status: ${REPO_ROOT}/bin/cd-status"
echo