From c41a4560c17fbb757dbb658d7dafc87587fefeab Mon Sep 17 00:00:00 2001 From: stephan Date: Sun, 28 Dec 2025 23:44:19 +0100 Subject: [PATCH] code: add update client prototype script --- CHANGELOG.md | 1 + scripts/update_client.sh | 112 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100755 scripts/update_client.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index a02a301..5da2326 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ By: Codex (GPT-5) | 28.12.2025 | ⚙️ Code | ID: Update-Config Keys in Settings/ENV/README definiert. By: Codex (GPT-5) | | 28.12.2025 | 📝 Req | ID: Update-Flow fuer Client dokumentiert. By: Codex (GPT-5) | | 28.12.2025 | 📝 Req | ID: Update-Flow Prototyp dokumentiert. By: Codex (GPT-5) | +| 28.12.2025 | ⚙️ Code | ID: Update-Client Prototyp-Skript hinzugefuegt. By: Codex (GPT-5) | --- ## Legende diff --git a/scripts/update_client.sh b/scripts/update_client.sh new file mode 100755 index 0000000..b9aea5c --- /dev/null +++ b/scripts/update_client.sh @@ -0,0 +1,112 @@ +#!/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