395 lines
12 KiB
Bash
395 lines
12 KiB
Bash
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
IFS=$'\n\t'
|
|
|
|
MAIN_URL="https://cloud.qortal.org/s/devnet_download/download/qortal-DevNet-MAIN.7z"
|
|
DB_URL="https://cloud.qortal.org/s/QortalDevNetDatabaseLatest/download/db-DevNet-LATEST.7z"
|
|
IP_SCRIPT_URL="https://gitea.qortal.link/crowetic/qortal-DevNet-scripts/raw/branch/main/add-public-ip-to-settings-mac.sh"
|
|
DEFAULT_SETTINGS_URL="https://gitea.qortal.link/crowetic/qortal-DevNet-scripts/raw/branch/main/default-settings.json"
|
|
START_VALIDATION_URL="https://gitea.qortal.link/crowetic/qortal-DevNet-scripts/raw/branch/main/start-mac.sh"
|
|
QORT_SCRIPT_URL="https://gitea.qortal.link/crowetic/qortal-DevNet-scripts/raw/branch/main/qort"
|
|
DEPENDENCIES_URL="https://gitea.qortal.link/crowetic/qortal-DevNet-scripts/raw/branch/main/setup-dependencies-mac.sh"
|
|
STOP_SCRIPT_URL="https://gitea.qortal.link/crowetic/qortal-DevNet-scripts/raw/branch/main/stop-mac.sh"
|
|
PUBLISH_SHARE_TOKEN="PublishDevNetIPsHere"
|
|
PUBLISH_WEBDAV_URL="https://cloud.qortal.org/public.php/webdav"
|
|
|
|
SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
|
|
HOME_DIR="${HOME}"
|
|
BASE_DIR="${HOME_DIR}"
|
|
DEVNET_DIR=""
|
|
STATE_FILE=""
|
|
DOWNLOAD_DIR=""
|
|
MAIN_ARCHIVE=""
|
|
DB_ARCHIVE=""
|
|
DEFAULT_SETTINGS_LOCAL=""
|
|
IP_SCRIPT_LOCAL=""
|
|
START_VALIDATION_LOCAL=""
|
|
QORT_SCRIPT_LOCAL=""
|
|
DEPENDENCIES_LOCAL=""
|
|
STOP_SCRIPT_LOCAL=""
|
|
PORT_START_DEFAULT=23391
|
|
PORT_START=""
|
|
LISTEN_PORT=""
|
|
|
|
log() { echo "[$(date +'%F %T')] $*"; }
|
|
die() { log "ERROR: $*"; exit 1; }
|
|
|
|
need_cmd() { command -v "$1" >/dev/null 2>&1; }
|
|
|
|
ensure_dependencies() {
|
|
local missing=()
|
|
if ! need_cmd 7z; then
|
|
missing+=("7z")
|
|
fi
|
|
if ! need_cmd python3; then
|
|
missing+=("python3")
|
|
fi
|
|
if ! need_cmd curl && ! need_cmd wget; then
|
|
missing+=("curl/wget")
|
|
fi
|
|
if ! need_cmd jq; then
|
|
missing+=("jq")
|
|
fi
|
|
if ! need_cmd java; then
|
|
missing+=("java")
|
|
fi
|
|
|
|
if [ "${#missing[@]}" -eq 0 ]; then
|
|
return 0
|
|
fi
|
|
|
|
log "Missing dependencies detected: ${missing[*]}"
|
|
mkdir -p "$DOWNLOAD_DIR"
|
|
|
|
if [ -x "${SCRIPT_DIR}/setup-dependencies-mac.sh" ]; then
|
|
DEP_SCRIPT="${SCRIPT_DIR}/setup-dependencies-mac.sh"
|
|
else
|
|
DEP_SCRIPT="${DEPENDENCIES_LOCAL}"
|
|
if need_cmd curl; then
|
|
curl -fL --retry 3 --retry-delay 5 -o "$DEP_SCRIPT" "$DEPENDENCIES_URL"
|
|
elif need_cmd wget; then
|
|
wget -qO "$DEP_SCRIPT" "$DEPENDENCIES_URL"
|
|
else
|
|
die "Neither curl nor wget found; cannot download setup-dependencies-mac.sh."
|
|
fi
|
|
fi
|
|
|
|
chmod +x "$DEP_SCRIPT"
|
|
"$DEP_SCRIPT"
|
|
|
|
if ! need_cmd 7z || ! need_cmd python3 || ( ! need_cmd curl && ! need_cmd wget ) || ! need_cmd jq || ! need_cmd java; then
|
|
die "Dependencies are still missing after setup. Please install manually."
|
|
fi
|
|
}
|
|
|
|
read -r -t 20 -p "Use default base path ${HOME_DIR}? [Y/n] (auto-continue in 20s): " USE_DEFAULT_BASE || USE_DEFAULT_BASE=""
|
|
case "$USE_DEFAULT_BASE" in
|
|
n|N|no|NO)
|
|
read -r -t 20 -p "Enter the base path to install into (auto-continue in 20s): " BASE_DIR_INPUT || BASE_DIR_INPUT=""
|
|
if [[ -z "${BASE_DIR_INPUT}" ]]; then
|
|
die "Base path cannot be empty."
|
|
fi
|
|
BASE_DIR="${BASE_DIR_INPUT}"
|
|
;;
|
|
*)
|
|
BASE_DIR="${HOME_DIR}"
|
|
;;
|
|
esac
|
|
|
|
DEVNET_DIR="${BASE_DIR}/qortal-DevNet"
|
|
STATE_FILE="${BASE_DIR}/qortal-DevNet.setup.state"
|
|
DOWNLOAD_DIR="${BASE_DIR}/qortal-DevNet-downloads"
|
|
MAIN_ARCHIVE="${DOWNLOAD_DIR}/qortal-DevNet-MAIN.7z"
|
|
DB_ARCHIVE="${DOWNLOAD_DIR}/db-DevNet-LATEST.7z"
|
|
DEFAULT_SETTINGS_LOCAL="${DOWNLOAD_DIR}/default-settings.json"
|
|
IP_SCRIPT_LOCAL="${DOWNLOAD_DIR}/add-public-ip-to-settings-mac.sh"
|
|
START_VALIDATION_LOCAL="${DOWNLOAD_DIR}/start-mac.sh"
|
|
QORT_SCRIPT_LOCAL="${DOWNLOAD_DIR}/qort"
|
|
DEPENDENCIES_LOCAL="${DOWNLOAD_DIR}/setup-dependencies-mac.sh"
|
|
STOP_SCRIPT_LOCAL="${DOWNLOAD_DIR}/stop-mac.sh"
|
|
|
|
ensure_dependencies
|
|
|
|
read -r -t 20 -p "Use default port range ${PORT_START_DEFAULT}-$((${PORT_START_DEFAULT} + 3))? [Y/n] (auto-continue in 20s): " USE_DEFAULT_PORTS || USE_DEFAULT_PORTS=""
|
|
case "$USE_DEFAULT_PORTS" in
|
|
n|N|no|NO)
|
|
read -r -t 20 -p "Enter the starting port for the range (auto-continue in 20s): " PORT_START_INPUT || PORT_START_INPUT=""
|
|
if [[ -z "${PORT_START_INPUT}" ]]; then
|
|
die "Port start cannot be empty."
|
|
fi
|
|
if ! [[ "$PORT_START_INPUT" =~ ^[0-9]+$ ]]; then
|
|
die "Port start must be a number."
|
|
fi
|
|
PORT_START="$PORT_START_INPUT"
|
|
;;
|
|
*)
|
|
PORT_START="$PORT_START_DEFAULT"
|
|
;;
|
|
esac
|
|
|
|
if (( PORT_START < 1024 || PORT_START > 65532 )); then
|
|
die "Port start must be between 1024 and 65532."
|
|
fi
|
|
LISTEN_PORT=$((PORT_START + 1))
|
|
|
|
step_done() {
|
|
grep -Fxq "$1" "$STATE_FILE" 2>/dev/null
|
|
}
|
|
|
|
mark_done() {
|
|
printf '%s\n' "$1" >> "$STATE_FILE"
|
|
}
|
|
|
|
download_file() {
|
|
local url="$1"
|
|
local dest="$2"
|
|
|
|
if need_cmd curl; then
|
|
curl -fL --retry 3 --retry-delay 5 -o "$dest" "$url"
|
|
elif need_cmd wget; then
|
|
wget -qO "$dest" "$url"
|
|
else
|
|
die "Neither curl nor wget found. Please install one before running."
|
|
fi
|
|
}
|
|
|
|
ensure_file() {
|
|
local url="$1"
|
|
local dest="$2"
|
|
if [[ -s "$dest" ]]; then
|
|
log "Using existing $(basename "$dest")"
|
|
else
|
|
download_file "$url" "$dest"
|
|
fi
|
|
}
|
|
|
|
port_in_use() {
|
|
local port="$1"
|
|
if need_cmd lsof; then
|
|
lsof -iTCP:"$port" -sTCP:LISTEN >/dev/null 2>&1
|
|
elif need_cmd netstat; then
|
|
netstat -an | awk '{print $4}' | grep -Eq "[\.:]${port}\$"
|
|
else
|
|
return 2
|
|
fi
|
|
}
|
|
|
|
PORT_RANGE_IN_USE=false
|
|
PORT_CHECK_UNKNOWN=false
|
|
for p in "$PORT_START" "$((PORT_START + 1))" "$((PORT_START + 2))" "$((PORT_START + 3))"; do
|
|
if port_in_use "$p"; then
|
|
PORT_RANGE_IN_USE=true
|
|
break
|
|
elif [[ $? -eq 2 ]]; then
|
|
PORT_CHECK_UNKNOWN=true
|
|
fi
|
|
done
|
|
|
|
if [[ "$PORT_RANGE_IN_USE" = true ]]; then
|
|
log "Port range ${PORT_START}-$((${PORT_START} + 3)) appears in use; shifting by 10000"
|
|
PORT_START=$((PORT_START + 10000))
|
|
if (( PORT_START > 65532 )); then
|
|
die "Port range exceeds 65535 after shifting."
|
|
fi
|
|
LISTEN_PORT=$((PORT_START + 1))
|
|
elif [[ "$PORT_CHECK_UNKNOWN" = true ]]; then
|
|
log "Warning: unable to detect if ports are in use; continuing with selected range."
|
|
fi
|
|
|
|
get_public_ip() {
|
|
local ip=""
|
|
local url=""
|
|
local urls="https://api.ipify.org https://ipv4.icanhazip.com https://checkip.amazonaws.com https://ifconfig.me https://canhazip.com"
|
|
local curl_opts="-fsS --max-time 8 -A Mozilla/5.0"
|
|
|
|
for url in $urls; do
|
|
if need_cmd curl; then
|
|
ip="$(curl $curl_opts "$url" 2>/dev/null || true)"
|
|
elif need_cmd wget; then
|
|
ip="$(wget -qO- "$url" || true)"
|
|
else
|
|
return 1
|
|
fi
|
|
ip="$(echo "$ip" | tr -d ' \n\r')"
|
|
if echo "$ip" | grep -Eq '^[0-9]{1,3}(\.[0-9]{1,3}){3}$'; then
|
|
echo "$ip"
|
|
return 0
|
|
fi
|
|
done
|
|
|
|
if need_cmd dig; then
|
|
ip="$(dig +short myip.opendns.com @resolver1.opendns.com | head -n 1 | tr -d ' \n\r')"
|
|
if echo "$ip" | grep -Eq '^[0-9]{1,3}(\.[0-9]{1,3}){3}$'; then
|
|
echo "$ip"
|
|
return 0
|
|
fi
|
|
fi
|
|
|
|
return 1
|
|
}
|
|
|
|
publish_public_ip() {
|
|
local ip="$1"
|
|
local port="$2"
|
|
local host_name="$3"
|
|
local tmp_file="$4"
|
|
local remote_name="${host_name}.txt"
|
|
local url="${PUBLISH_WEBDAV_URL}/${remote_name}"
|
|
local alt_name=""
|
|
local alt_url=""
|
|
local http_code=""
|
|
|
|
printf '%s:%s\n' "$ip" "$port" > "$tmp_file"
|
|
|
|
if need_cmd curl; then
|
|
http_code="$(curl -sS -u "${PUBLISH_SHARE_TOKEN}:" -T "$tmp_file" -o /dev/null -w "%{http_code}" "$url" || true)"
|
|
if [[ "$http_code" != "200" && "$http_code" != "201" && "$http_code" != "204" ]]; then
|
|
alt_name="${host_name}-$(date -u +'%Y%m%d_%H%M%S').txt"
|
|
alt_url="${PUBLISH_WEBDAV_URL}/${alt_name}"
|
|
http_code="$(curl -sS -u "${PUBLISH_SHARE_TOKEN}:" -T "$tmp_file" -o /dev/null -w "%{http_code}" "$alt_url" || true)"
|
|
if [[ "$http_code" != "200" && "$http_code" != "201" && "$http_code" != "204" ]]; then
|
|
return 1
|
|
fi
|
|
fi
|
|
elif need_cmd wget; then
|
|
wget --method=PUT --body-file="$tmp_file" --user="$PUBLISH_SHARE_TOKEN" --password="" -qO- "$url" >/dev/null
|
|
else
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
if ! need_cmd 7z; then
|
|
die "7z not found. Please install p7zip/7z before running."
|
|
fi
|
|
|
|
log "Step 1/7: Download files..."
|
|
mkdir -p "$DOWNLOAD_DIR"
|
|
ensure_file "$MAIN_URL" "$MAIN_ARCHIVE"
|
|
ensure_file "$DB_URL" "$DB_ARCHIVE"
|
|
ensure_file "$DEFAULT_SETTINGS_URL" "$DEFAULT_SETTINGS_LOCAL"
|
|
ensure_file "$IP_SCRIPT_URL" "$IP_SCRIPT_LOCAL"
|
|
ensure_file "$START_VALIDATION_URL" "$START_VALIDATION_LOCAL"
|
|
ensure_file "$QORT_SCRIPT_URL" "$QORT_SCRIPT_LOCAL"
|
|
ensure_file "$STOP_SCRIPT_URL" "$STOP_SCRIPT_LOCAL"
|
|
chmod +x "$IP_SCRIPT_LOCAL" "$START_VALIDATION_LOCAL" "$STOP_SCRIPT_LOCAL" "$QORT_SCRIPT_LOCAL"
|
|
if ! step_done "01-download-files"; then
|
|
mark_done "01-download-files"
|
|
fi
|
|
|
|
if ! step_done "02-prepare-devnet-dir"; then
|
|
log "Step 2/7: Prepare devnet directory..."
|
|
if [[ -d "$DEVNET_DIR" ]]; then
|
|
BACKUP_DIR="${HOME_DIR}/backup-qortal-DevNet"
|
|
if [[ -e "$BACKUP_DIR" ]]; then
|
|
BACKUP_DIR="${BACKUP_DIR}-$(date +%Y%m%d_%H%M%S)"
|
|
fi
|
|
log "Existing ${DEVNET_DIR} found; moving to ${BACKUP_DIR}"
|
|
mv "$DEVNET_DIR" "$BACKUP_DIR"
|
|
fi
|
|
log "Creating ${DEVNET_DIR}"
|
|
mkdir -p "$DEVNET_DIR"
|
|
mark_done "02-prepare-devnet-dir"
|
|
fi
|
|
|
|
if ! step_done "03-extract-main"; then
|
|
log "Step 3/7: Extract devnet main archive..."
|
|
7z x "$MAIN_ARCHIVE" -o"$DEVNET_DIR" >/dev/null
|
|
if [[ -d "${DEVNET_DIR}/temp" ]]; then
|
|
log "Normalizing devnet main files from temp/ into ${DEVNET_DIR}"
|
|
rsync -a "${DEVNET_DIR}/temp/" "${DEVNET_DIR}/"
|
|
rm -rf "${DEVNET_DIR}/temp"
|
|
fi
|
|
mark_done "03-extract-main"
|
|
fi
|
|
|
|
if ! step_done "04-extract-db"; then
|
|
log "Step 4/7: Extract devnet DB archive..."
|
|
if [[ -d "${DEVNET_DIR}/db" ]]; then
|
|
DB_BACKUP="${DEVNET_DIR}/db.bak-$(date +%Y%m%d_%H%M%S)"
|
|
log "Existing db folder found; moving to ${DB_BACKUP}"
|
|
mv "${DEVNET_DIR}/db" "$DB_BACKUP"
|
|
fi
|
|
7z x "$DB_ARCHIVE" -o"$DEVNET_DIR" >/dev/null
|
|
if [[ ! -d "${DEVNET_DIR}/db" ]]; then
|
|
die "Expected db/ folder in devnet DB archive, but it was not found."
|
|
fi
|
|
mark_done "04-extract-db"
|
|
fi
|
|
|
|
if ! step_done "05-configure-settings"; then
|
|
log "Step 5/7: Configure settings..."
|
|
SETTINGS_PATH="${DEVNET_DIR}/settings.json"
|
|
if [[ -f "$SETTINGS_PATH" ]]; then
|
|
SETTINGS_BACKUP="${DEVNET_DIR}/backup-settings.json"
|
|
if [[ -e "$SETTINGS_BACKUP" ]]; then
|
|
SETTINGS_BACKUP="${SETTINGS_BACKUP}-$(date +%Y%m%d_%H%M%S)"
|
|
fi
|
|
log "Existing settings.json found; moving to ${SETTINGS_BACKUP}"
|
|
mv "$SETTINGS_PATH" "$SETTINGS_BACKUP"
|
|
fi
|
|
cp -f "$DEFAULT_SETTINGS_LOCAL" "$SETTINGS_PATH"
|
|
|
|
IP_SCRIPT_PATH="${DEVNET_DIR}/add-public-ip-to-settings.sh"
|
|
cp -f "$IP_SCRIPT_LOCAL" "$IP_SCRIPT_PATH"
|
|
chmod +x "$IP_SCRIPT_PATH"
|
|
|
|
log "Running public IP script..."
|
|
( cd "$DEVNET_DIR" && QORTAL_PORT_START="$PORT_START" "$IP_SCRIPT_PATH" )
|
|
mark_done "05-configure-settings"
|
|
fi
|
|
|
|
if ! step_done "06-publish-public-ip"; then
|
|
log "Step 6/7: Publish public IP..."
|
|
HOST_NAME="$(hostname -s 2>/dev/null || hostname)"
|
|
PUBLISH_TMP="${DOWNLOAD_DIR}/devnet-public-ip.txt"
|
|
PUBLIC_IP="$(get_public_ip || true)"
|
|
if [[ -n "$PUBLIC_IP" ]]; then
|
|
if publish_public_ip "$PUBLIC_IP" "$LISTEN_PORT" "$HOST_NAME" "$PUBLISH_TMP"; then
|
|
log "Published ${PUBLIC_IP}:${LISTEN_PORT} for ${HOST_NAME}"
|
|
else
|
|
log "Warning: failed to publish public IP to Nextcloud share."
|
|
fi
|
|
else
|
|
log "Warning: could not determine public IP to publish."
|
|
fi
|
|
mark_done "06-publish-public-ip"
|
|
fi
|
|
|
|
if ! step_done "07-install-start-script"; then
|
|
log "Step 7/7: Install start/stop scripts..."
|
|
START_VALIDATION_PATH="${DEVNET_DIR}/start-mac.sh"
|
|
cp -f "$START_VALIDATION_LOCAL" "$START_VALIDATION_PATH"
|
|
chmod +x "$START_VALIDATION_PATH"
|
|
|
|
log "Replacing start.sh with start-mac.sh"
|
|
if [[ -f "${DEVNET_DIR}/start.sh" ]]; then
|
|
START_BACKUP="${DEVNET_DIR}/start.sh.bak-$(date +%Y%m%d_%H%M%S)"
|
|
mv "${DEVNET_DIR}/start.sh" "$START_BACKUP"
|
|
log "Backed up existing start.sh to ${START_BACKUP}"
|
|
fi
|
|
cp -f "$START_VALIDATION_PATH" "${DEVNET_DIR}/start.sh"
|
|
|
|
if [[ -f "${DEVNET_DIR}/stop-mac.sh" ]]; then
|
|
STOP_BACKUP="${DEVNET_DIR}/stop-mac.sh.bak-$(date +%Y%m%d_%H%M%S)"
|
|
mv "${DEVNET_DIR}/stop-mac.sh" "$STOP_BACKUP"
|
|
log "Backed up existing stop-mac.sh to ${STOP_BACKUP}"
|
|
fi
|
|
cp -f "$STOP_SCRIPT_LOCAL" "${DEVNET_DIR}/stop-mac.sh"
|
|
chmod +x "${DEVNET_DIR}/stop-mac.sh"
|
|
|
|
if [[ -f "${DEVNET_DIR}/stop.sh" ]]; then
|
|
STOP_ALIAS_BACKUP="${DEVNET_DIR}/stop.sh.bak-$(date +%Y%m%d_%H%M%S)"
|
|
mv "${DEVNET_DIR}/stop.sh" "$STOP_ALIAS_BACKUP"
|
|
log "Backed up existing stop.sh to ${STOP_ALIAS_BACKUP}"
|
|
fi
|
|
cp -f "$STOP_SCRIPT_LOCAL" "${DEVNET_DIR}/stop.sh"
|
|
chmod +x "${DEVNET_DIR}/stop.sh"
|
|
|
|
log "Installing qort helper..."
|
|
cp -f "$QORT_SCRIPT_LOCAL" "${DEVNET_DIR}/qort"
|
|
chmod +x "${DEVNET_DIR}/qort"
|
|
mark_done "07-install-start-script"
|
|
fi
|
|
|
|
log "Done."
|