104 lines
3.6 KiB
Bash
Executable File
104 lines
3.6 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# install-backup-cron.sh
|
|
# -----------------------
|
|
# One-shot installer for the Africa Alert SQLite backup cron job.
|
|
#
|
|
# Idempotent: re-running is a no-op if the cron entry is already present
|
|
# (detected by `crontab -l | grep -q africa-alert`).
|
|
#
|
|
# Default schedule: every 12 hours (00:00 and 12:00 UTC).
|
|
# Override with CRON_SCHEDULE env var, e.g.
|
|
# CRON_SCHEDULE="0 */6 * * *" sudo ./install-backup-cron.sh
|
|
#
|
|
# This script is meant to be run on the production Ubuntu 22.04 host.
|
|
# It is NOT a Windows script. On a Windows dev box, only
|
|
# `bash -n install-backup-cron.sh` (syntax check) is meaningful.
|
|
|
|
set -eu
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
|
|
|
|
BACKUP_SCRIPT="${SCRIPT_DIR}/africa-alert-backup.sh"
|
|
CRON_SCHEDULE="${CRON_SCHEDULE:-0 */12 * * *}" # every 12h, on the hour
|
|
LOG_DIR="${LOG_DIR:-${PROJECT_ROOT}/logs}"
|
|
INSTALL_LOG="${LOG_DIR}/backup-install.log"
|
|
|
|
# Marker used by both the grep-based idempotency check and as a comment
|
|
# inside the crontab so an operator reading `crontab -l` knows what the
|
|
# line does. Keep the marker stable — other tooling relies on it.
|
|
CRON_MARKER="africa-alert-backup"
|
|
|
|
mkdir -p "${LOG_DIR}"
|
|
touch "${INSTALL_LOG}"
|
|
|
|
log() {
|
|
local msg="[$(date -u +%Y-%m-%dT%H:%M:%SZ)] [install-cron] $*"
|
|
echo "${msg}" | tee -a "${INSTALL_LOG}" >&2
|
|
}
|
|
|
|
if [ ! -x "${BACKUP_SCRIPT}" ]; then
|
|
log "ERROR: backup script not found or not executable: ${BACKUP_SCRIPT}"
|
|
log "Hint: chmod +x ${BACKUP_SCRIPT}"
|
|
exit 1
|
|
fi
|
|
|
|
# Idempotency check: does the current crontab already contain our marker?
|
|
if command -v crontab >/dev/null 2>&1; then
|
|
EXISTING="$(crontab -l 2>/dev/null || true)"
|
|
if echo "${EXISTING}" | grep -q "${CRON_MARKER}"; then
|
|
log "cron entry already installed; nothing to do."
|
|
log "current crontab:"
|
|
echo "${EXISTING}" | grep "${CRON_MARKER}" | sed 's/^/ /' | tee -a "${INSTALL_LOG}" >&2
|
|
log "to reinstall, first run: crontab -l | grep -v '${CRON_MARKER}' | crontab -"
|
|
exit 0
|
|
fi
|
|
else
|
|
log "WARN: crontab command not found; this is not a Linux host. Aborting."
|
|
exit 1
|
|
fi
|
|
|
|
# Build the cron line. The marker is a leading comment so it's greppable.
|
|
CRON_LINE="${CRON_SCHEDULE} /bin/bash ${BACKUP_SCRIPT} >> ${LOG_DIR}/backup.log 2>&1 # ${CRON_MARKER}"
|
|
|
|
# Install: append the new line to whatever crontab exists, or create one
|
|
# if none. `crontab -` reads from stdin.
|
|
{
|
|
echo "${EXISTING:-}"
|
|
echo "${CRON_LINE}"
|
|
} | crontab -
|
|
|
|
# Compute the next-run time. We use a small GNU-date trick: if now is
|
|
# earlier than the next scheduled firing, that's the next run; otherwise
|
|
# advance by 12h. This is a best-effort estimate — cron itself is the
|
|
# source of truth. Format: YYYY-MM-DD HH:MM:SS UTC.
|
|
compute_next_run() {
|
|
local now_epoch
|
|
now_epoch="$(date -u +%s)"
|
|
# Step forward in 12h blocks until we land in the future.
|
|
local candidate="${now_epoch}"
|
|
while [ "${candidate}" -le "${now_epoch}" ]; do
|
|
candidate=$((candidate + 12 * 3600))
|
|
done
|
|
# Snap to the next "round" 12h boundary (00:00 or 12:00 UTC) by
|
|
# subtracting (epoch mod 12h). This matches CRON_SCHEDULE default.
|
|
local bucket=$((candidate % (12 * 3600)))
|
|
candidate=$((candidate - bucket))
|
|
# If snapping pushed us into the past, add another 12h.
|
|
if [ "${candidate}" -le "${now_epoch}" ]; then
|
|
candidate=$((candidate + 12 * 3600))
|
|
fi
|
|
date -u -d "@${candidate}" +"%Y-%m-%d %H:%M:%S UTC"
|
|
}
|
|
|
|
NEXT_RUN="$(compute_next_run)"
|
|
|
|
log "installed cron entry:"
|
|
log " ${CRON_LINE}"
|
|
log "next scheduled run (estimate): ${NEXT_RUN}"
|
|
log "verify with: crontab -l | grep ${CRON_MARKER}"
|
|
log "uninstall with: crontab -l | grep -v '${CRON_MARKER}' | crontab -"
|
|
|
|
exit 0
|