#!/usr/bin/env bash set -euo pipefail SERVICE_NAME="${SERVICE_NAME:-skd}" UPDATE_URL="${SKD_UPDATE_URL:-https://update.wlkns.org}" UPDATE_TOKEN="${SKD_UPDATE_TOKEN:-}" INSTALL_DIR="${INSTALL_DIR:-/opt/sk}" STAGING_DIR="" MANIFEST_FILE="" ARTIFACT_FILE="" BACKUP_DIR="" log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" } cleanup() { if [[ -n "${STAGING_DIR}" && -d "${STAGING_DIR}" ]]; then rm -rf "${STAGING_DIR}" fi if [[ -n "${MANIFEST_FILE}" && -f "${MANIFEST_FILE}" ]]; then rm -f "${MANIFEST_FILE}" fi if [[ -n "${ARTIFACT_FILE}" && -f "${ARTIFACT_FILE}" ]]; then rm -f "${ARTIFACT_FILE}" fi } trap cleanup EXIT require_cmd() { if ! command -v "$1" >/dev/null 2>&1; then echo "Required command not found: $1" >&2 exit 1 fi } require_cmd curl require_cmd tar require_cmd sha256sum require_cmd python3 log "Fetching update manifest from ${UPDATE_URL}..." MANIFEST_FILE="$(mktemp)" if [[ -n "${UPDATE_TOKEN}" ]]; then curl -fsS -H "Authorization: Bearer ${UPDATE_TOKEN}" "${UPDATE_URL}" -o "${MANIFEST_FILE}" else curl -fsS "${UPDATE_URL}" -o "${MANIFEST_FILE}" fi read_manifest() { python3 - <<'PY' "${MANIFEST_FILE}" import json import sys path = sys.argv[1] with open(path, "r", encoding="utf-8") as fh: data = json.load(fh) for key in ("version", "artifact_url", "sha256"): if key not in data: sys.exit(f"Manifest missing '{key}'") print(data["version"]) print(data["artifact_url"]) print(data["sha256"]) PY } readarray -t manifest_values < <(read_manifest) VERSION="${manifest_values[0]}" ARTIFACT_URL="${manifest_values[1]}" ARTIFACT_SHA256="${manifest_values[2]}" log "Downloading artifact ${ARTIFACT_URL} (version ${VERSION})..." ARTIFACT_FILE="$(mktemp --suffix=.tar.gz)" if [[ -n "${UPDATE_TOKEN}" ]]; then curl -fsS -H "Authorization: Bearer ${UPDATE_TOKEN}" "${ARTIFACT_URL}" -o "${ARTIFACT_FILE}" else curl -fsS "${ARTIFACT_URL}" -o "${ARTIFACT_FILE}" fi log "Verifying checksum..." CALC_SHA="$(sha256sum "${ARTIFACT_FILE}" | awk '{print $1}')" if [[ "${CALC_SHA}" != "${ARTIFACT_SHA256}" ]]; then echo "Checksum mismatch: expected ${ARTIFACT_SHA256} got ${CALC_SHA}" >&2 exit 1 fi STAGING_DIR="$(mktemp -d /opt/sk_update.XXXXXX)" log "Extracting to staging ${STAGING_DIR}..." tar -xzf "${ARTIFACT_FILE}" -C "${STAGING_DIR}" BACKUP_DIR="/opt/sk_backup_${VERSION}_$(date +%s)" log "Stopping service ${SERVICE_NAME}..." sudo systemctl stop "${SERVICE_NAME}.service" log "Swapping ${INSTALL_DIR} -> ${BACKUP_DIR}..." if [[ -d "${INSTALL_DIR}" ]]; then sudo mv "${INSTALL_DIR}" "${BACKUP_DIR}" fi sudo mv "${STAGING_DIR}" "${INSTALL_DIR}" STAGING_DIR="" log "Starting service ${SERVICE_NAME}..." if sudo systemctl start "${SERVICE_NAME}.service"; then log "Update applied successfully." else log "Service failed to start, rolling back..." sudo rm -rf "${INSTALL_DIR}" if [[ -d "${BACKUP_DIR}" ]]; then sudo mv "${BACKUP_DIR}" "${INSTALL_DIR}" fi sudo systemctl start "${SERVICE_NAME}.service" || true exit 1 fi