#!/usr/bin/env bash set -eEuo pipefail IFS=$'\n\t' ### Configuration ### REMOTE_USER="crowetic" REMOTE_HOST="devnet-nodes.qortal.link" REMOTE_PORT=22225 REMOTE_BASE_DIR="/qortal" # stop.sh, start.sh, db/, qortal.jar live here LOCAL_SYNC_DIR="/mnt/4TB-WDB/DATA/QortalCloud/DevNet" MAX_LOCAL_AGE_DAYS=2 # roll new remote archive only if local copy ≥ this KEEP_LOCAL_ARCHIVES=2 # keep this many archives locally ####################### log(){ echo "[$(date +'%H:%M:%S')] $*"; } trap 'log "ERROR: script failed at line $LINENO"; exit 1' ERR # Function to retry rsync with exponential backoff rsync_with_retry() { local max_attempts=3 local attempt=1 local success=0 while (( attempt <= max_attempts )); do if "$@"; then success=1 break else log "rsync attempt $attempt failed. Retrying in $((attempt * 5)) seconds..." sleep $((attempt * 5)) ((attempt++)) fi done if (( success == 0 )); then log "ERROR: rsync failed after $max_attempts attempts" exit 1 fi } # 1) Inspect the newest local archive log "Inspecting local archives…" LATEST_LOCAL=$(ls -1t "${LOCAL_SYNC_DIR}"/db-Devnet-*.7z 2>/dev/null | head -1 || true) if [[ -n "$LATEST_LOCAL" ]]; then TS=$(stat -c %Y "$LATEST_LOCAL") AGE=$(( ( $(date +%s) - TS ) / 86400 )) log "→ Found ${LATEST_LOCAL##*/}, age=${AGE}d" else log "→ No local archives found." AGE=$((MAX_LOCAL_AGE_DAYS+1)) fi # 2) Conditionally roll a new archive on remote if (( AGE >= MAX_LOCAL_AGE_DAYS )); then NEW_ARCH="db-Devnet-$(date +%Y%m%d_%H%M%S).7z" log "→ Local ≥${MAX_LOCAL_AGE_DAYS}d old; creating ${NEW_ARCH} on remote" ssh -p"${REMOTE_PORT}" "${REMOTE_USER}@${REMOTE_HOST}" bash -l </dev/null || { echo "ERROR: install p7zip-full"; exit 1; } # stop.sh (ignore failure) if [[ -x ./stop.sh ]]; then set +e; ./stop.sh; set -e fi [[ -d db ]] || { echo "ERROR: db/ missing"; exit 1; } 7z a "${NEW_ARCH}" db # restart (best effort) if [[ -x ./start.sh ]]; then ./start.sh fi # prune remote: keep only the newest 1 archive ls -1t db-Devnet-*.7z \ | tail -n +2 \ | xargs -r rm -f -- EOF # fetch the newly created archive log "Rsyncing new archive → local" rsync_with_retry rsync -avPz -e "ssh -p${REMOTE_PORT}" \ "${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_BASE_DIR}/${NEW_ARCH}" \ "${LOCAL_SYNC_DIR}/" else log "→ Local archive is fresh (<${MAX_LOCAL_AGE_DAYS}d); skipping archive creation." fi # 3) Always rsync qortal.jar log "Rsyncing qortal.jar → local (primary)" rsync_with_retry rsync -avPz -e "ssh -p${REMOTE_PORT}" \ "${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_BASE_DIR}/qortal.jar" \ "${LOCAL_SYNC_DIR}/" log "Rsyncing qortal.jar → local devnet folder" PRIMARY_JAR="${LOCAL_SYNC_DIR}/qortal.jar" DEVNET_JAR="${LOCAL_SYNC_DIR}/qortal-DevNet/qortal.jar" if [[ -f "$PRIMARY_JAR" ]]; then rsync_with_retry rsync -a "$PRIMARY_JAR" "$DEVNET_JAR" else log "ERROR: no local qortal.jar found after rsync" exit 1 fi log "Rsyncing settings.json → local (primary)" rsync_with_retry rsync -avPz -e "ssh -p${REMOTE_PORT}" \ "${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_BASE_DIR}/settings.json" \ "${LOCAL_SYNC_DIR}/" log "Rsyncing settings.json → local devnet folder" rsync_with_retry rsync -a \ "${LOCAL_SYNC_DIR}/settings.json" \ "${LOCAL_SYNC_DIR}/qortal-DevNet/settings.json" log "Updating hash.txt from the newest local qortal.jar" PRIMARY_JAR="${LOCAL_SYNC_DIR}/qortal.jar" DEVNET_JAR="${LOCAL_SYNC_DIR}/qortal-DevNet/qortal.jar" HASH_INPUT="" if [[ -f "$PRIMARY_JAR" && -f "$DEVNET_JAR" ]]; then if [[ "$PRIMARY_JAR" -nt "$DEVNET_JAR" ]]; then HASH_INPUT="$PRIMARY_JAR" else HASH_INPUT="$DEVNET_JAR" fi elif [[ -f "$PRIMARY_JAR" ]]; then HASH_INPUT="$PRIMARY_JAR" elif [[ -f "$DEVNET_JAR" ]]; then HASH_INPUT="$DEVNET_JAR" else log "ERROR: no local qortal.jar found to hash" exit 1 fi md5sum "$HASH_INPUT" | awk '{print $1}' > "${LOCAL_SYNC_DIR}/hash.txt" log "hash.txt updated from ${HASH_INPUT}" SRC_DIR="${LOCAL_SYNC_DIR}/qortal-DevNet" ARCHIVE="${LOCAL_SYNC_DIR}/qortal-DevNet-MAIN.7z" TMP_DIR="${LOCAL_SYNC_DIR}/temp" TMP_ARCHIVE="${LOCAL_SYNC_DIR}/temp.7z" log() { echo "[$(date +'%F %T')] $*"; } # 1. If there is no archive, or if any file in SRC_DIR is newer than ARCHIVE, rebuild. if [[ ! -f "$ARCHIVE" ]] || find "$SRC_DIR" -type f -newer "$ARCHIVE" | read; then log "Changes detected (or no archive yet). Rebuilding 7z…" # Clean temp rm -rf "$TMP_DIR" mkdir -p "$TMP_DIR" # Mirror the tree in temp rsync_with_retry rsync -a --delete "$SRC_DIR/" "$TMP_DIR/" # Create the archive 7z a "$TMP_ARCHIVE" "$TMP_DIR" # Atomically replace the old archive mv -f "$TMP_ARCHIVE" "$ARCHIVE" log "Archive updated at $ARCHIVE" else log "No changes since last archive; skipping rebuild." fi # 4) Prune local archives: keep only the newest $KEEP_LOCAL_ARCHIVES log "Pruning local archives to the ${KEEP_LOCAL_ARCHIVES} most recent" cd "${LOCAL_SYNC_DIR}" ls -1t db-Devnet-*.7z \ | tail -n +$((KEEP_LOCAL_ARCHIVES+1)) \ | xargs -r rm -f -- log "✔ Done."