#!/bin/sh set -eu PROJECT_ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) OLD_PACKAGE="${1:-$PROJECT_ROOT/dist/imagefind-0.3.9-x86_64.fpk}" NEW_APP_ROOT="${2:-$PROJECT_ROOT/.build-fnos/imagefind/app}" PORT="${3:-18766}" NEW_PACKAGE_ROOT="${4:-$PROJECT_ROOT/fnos}" PYTHON_PATH="${5:-${IMAGEFIND_PYTHON_PATH:-}}" PASSWORD="imagefind-upgrade-smoke-password" [ -f "$OLD_PACKAGE" ] || { printf 'missing previous package: %s\n' "$OLD_PACKAGE" >&2; exit 1; } [ -f "$NEW_APP_ROOT/runtime/VERSION" ] && [ -d "$NEW_APP_ROOT/runtime/wheels" ] || { printf 'missing new packaged Python runtime: %s\n' "$NEW_APP_ROOT/runtime" >&2 exit 1 } OLD_PACKAGE=$(CDPATH= cd -- "$(dirname -- "$OLD_PACKAGE")" && pwd)/$(basename -- "$OLD_PACKAGE") NEW_APP_ROOT=$(CDPATH= cd -- "$NEW_APP_ROOT" && pwd) NEW_PACKAGE_ROOT=$(CDPATH= cd -- "$NEW_PACKAGE_ROOT" && pwd) WORK_ROOT=$(mktemp -d "$PROJECT_ROOT/.upgrade-smoke.XXXXXX") OLD_PACKAGE_ROOT="$WORK_ROOT/old-package" ACTIVE_APP_ROOT="$WORK_ROOT/app" OLD_APP_ROOT="$WORK_ROOT/old-app" DATA_ROOT="$WORK_ROOT/var" LIFECYCLE_LOG="$WORK_ROOT/fnos-upgrade.log" COOKIE_JAR="$WORK_ROOT/cookie.txt" OLD_CONTROL="$OLD_PACKAGE_ROOT/cmd/main" NEW_CONTROL="$NEW_PACKAGE_ROOT/cmd/main" cleanup() { set +e if [ -x "$NEW_CONTROL" ]; then IMAGEFIND_PYTHON_PATH="$PYTHON_PATH" TRIM_APPDEST="$ACTIVE_APP_ROOT" TRIM_PKGVAR="$DATA_ROOT" \ "$NEW_CONTROL" stop >/dev/null 2>&1 fi if [ -x "$OLD_CONTROL" ]; then TRIM_APPDEST="$ACTIVE_APP_ROOT" TRIM_PKGVAR="$DATA_ROOT" "$OLD_CONTROL" stop >/dev/null 2>&1 fi if [ "${IMAGEFIND_KEEP_UPGRADE_SMOKE:-false}" = "true" ]; then printf 'upgrade smoke workspace preserved at %s\n' "$WORK_ROOT" >&2 return fi case "$WORK_ROOT" in "$PROJECT_ROOT"/.upgrade-smoke.*) rm -rf -- "$WORK_ROOT" ;; *) printf 'refusing to clear unexpected upgrade smoke path: %s\n' "$WORK_ROOT" >&2 ;; esac } trap cleanup EXIT HUP INT TERM mkdir -p "$OLD_PACKAGE_ROOT" "$ACTIVE_APP_ROOT" tar xzf "$OLD_PACKAGE" -C "$OLD_PACKAGE_ROOT" cmd manifest tar xOf "$OLD_PACKAGE" app.tgz | tar xzf - -C "$ACTIVE_APP_ROOT" OLD_VERSION=$(sed -n 's/^version[[:space:]]*=[[:space:]]*//p' "$OLD_PACKAGE_ROOT/manifest" | head -n 1) NEW_VERSION=$(sed -n 's/^version[[:space:]]*=[[:space:]]*//p' "$NEW_PACKAGE_ROOT/manifest" | head -n 1) [ -n "$OLD_VERSION" ] && [ -n "$NEW_VERSION" ] || { printf 'unable to read package versions\n' >&2; exit 1; } [ "$OLD_VERSION" != "$NEW_VERSION" ] || { printf 'upgrade smoke requires two different versions\n' >&2; exit 1; } wait_for_version() { EXPECTED="$1" ATTEMPT=0 while [ "$ATTEMPT" -lt 90 ]; do STATUS=$(curl -fsS "http://127.0.0.1:$PORT/api/v1/status" 2>/dev/null || true) case "$STATUS" in *'"configured":true'*'"version":"'"$EXPECTED"'"'*) return 0 ;; esac ATTEMPT=$((ATTEMPT + 1)) sleep 1 done printf 'version %s did not become ready; lifecycle log follows:\n' "$EXPECTED" >&2 tail -100 "$DATA_ROOT/log/imagefind.log" >&2 || true return 1 } IMAGEFIND_PYTHON_PATH="$PYTHON_PATH" TRIM_APPDEST="$ACTIVE_APP_ROOT" TRIM_PKGVAR="$DATA_ROOT" \ TRIM_TEMP_LOGFILE="$LIFECYCLE_LOG" \ wizard_admin_password="$PASSWORD" wizard_admin_password_confirm="$PASSWORD" \ "$OLD_PACKAGE_ROOT/cmd/install_callback" TRIM_PKGVAR="$DATA_ROOT" wizard_direct_access=true wizard_direct_port="$PORT" \ "$OLD_PACKAGE_ROOT/cmd/config_callback" # A 0.3.15 PyInstaller service can be precisely what is broken on the target # NAS. Upgrade verification must therefore begin from its persisted data and # lifecycle callbacks, not require the old HTTP listener to become healthy. if [ "${IMAGEFIND_SMOKE_START_OLD:-false}" = "true" ]; then IMAGEFIND_PYTHON_PATH="$PYTHON_PATH" TRIM_APPDEST="$ACTIVE_APP_ROOT" TRIM_PKGVAR="$DATA_ROOT" \ "$OLD_CONTROL" start wait_for_version "$OLD_VERSION" fi printf '%s\n' 'preserve-across-upgrade' >"$DATA_ROOT/data/upgrade-smoke-marker" IMAGEFIND_PYTHON_PATH="$PYTHON_PATH" TRIM_APPDEST="$ACTIVE_APP_ROOT" TRIM_PKGVAR="$DATA_ROOT" \ "$OLD_CONTROL" stop TRIM_APPDEST="$ACTIVE_APP_ROOT" TRIM_PKGVAR="$DATA_ROOT" TRIM_TEMP_LOGFILE="$LIFECYCLE_LOG" \ "$NEW_PACKAGE_ROOT/cmd/upgrade_init" mv "$ACTIVE_APP_ROOT" "$OLD_APP_ROOT" ln -s "$NEW_APP_ROOT" "$ACTIVE_APP_ROOT" # Deliberately pass stale mismatched values from the previous wizard. # The upgrade must preserve the existing password and still succeed. TRIM_APPDEST="$ACTIVE_APP_ROOT" TRIM_PKGVAR="$DATA_ROOT" TRIM_TEMP_LOGFILE="$LIFECYCLE_LOG" \ IMAGEFIND_PYTHON_PATH="$PYTHON_PATH" \ wizard_admin_password=placeholder wizard_admin_password_confirm=different \ "$NEW_PACKAGE_ROOT/cmd/upgrade_callback" IMAGEFIND_PYTHON_PATH="$PYTHON_PATH" TRIM_APPDEST="$ACTIVE_APP_ROOT" TRIM_PKGVAR="$DATA_ROOT" \ "$NEW_CONTROL" start wait_for_version "$NEW_VERSION" [ "$(sed -n '1p' "$DATA_ROOT/data/upgrade-smoke-marker")" = "preserve-across-upgrade" ] || { printf 'upgrade did not preserve application data\n' >&2 exit 1 } LOGIN=$(curl -fsS -c "$COOKIE_JAR" -H 'Content-Type: application/json' \ --data "{\"password\":\"$PASSWORD\",\"remember_device\":false}" \ "http://127.0.0.1:$PORT/api/v1/auth/login") case "$LOGIN" in *'"csrf_token"'*) ;; *) printf 'upgrade did not preserve the administrator password\n' >&2; exit 1 ;; esac grep -q '升级准备完成' "$LIFECYCLE_LOG" grep -q '升级数据检查完成' "$LIFECYCLE_LOG" printf 'fnOS upgrade smoke passed: %s -> %s; data, access settings and password preserved\n' \ "$OLD_VERSION" "$NEW_VERSION"