#!/bin/bash set -eu PACKAGE_ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) APP_ROOT="${TRIM_APPDEST:-$PACKAGE_ROOT/app}" DATA_ROOT="${TRIM_PKGVAR:-$PACKAGE_ROOT/var}" VERSION_FILE="$APP_ROOT/runtime/VERSION" WHEEL_ROOT="$APP_ROOT/runtime/wheels" CORE_REQUIREMENTS="$APP_ROOT/runtime/runtime-core.txt" RUNTIME_ROOT="$DATA_ROOT/runtime" fail() { printf '%s\n' "$1" >&2 exit 1 } [ -f "$VERSION_FILE" ] || fail "ImageFind 运行环境初始化失败:安装包缺少版本信息。" [ -d "$WHEEL_ROOT" ] || fail "ImageFind 运行环境初始化失败:安装包缺少核心 wheelhouse。" [ -f "$CORE_REQUIREMENTS" ] || fail "ImageFind 运行环境初始化失败:安装包缺少核心依赖锁文件。" VERSION=$(sed -n '1p' "$VERSION_FILE") case "$VERSION" in ''|*[!0-9.]* ) fail "ImageFind 运行环境初始化失败:版本信息无效。" ;; esac PACKAGE_ID=$(sed -n '2p' "$VERSION_FILE") case "$PACKAGE_ID" in ''|*[!0-9a-f]* ) fail "ImageFind 运行环境初始化失败:安装包指纹无效。" ;; esac [ "${#PACKAGE_ID}" -eq 64 ] || fail "ImageFind 运行环境初始化失败:安装包指纹长度无效。" if [ -n "${IMAGEFIND_PYTHON_PATH:-}" ]; then SYSTEM_PYTHON="$IMAGEFIND_PYTHON_PATH" else SYSTEM_PYTHON="/var/apps/python312/target/bin/python3" fi [ -x "$SYSTEM_PYTHON" ] || fail "ImageFind 需要 fnOS Python 3.12 依赖,请先确认 python312 已安装。" PYTHON_VERSION=$($SYSTEM_PYTHON -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")' 2>/dev/null) \ || fail "ImageFind 无法运行 fnOS Python 3.12。" [ "$PYTHON_VERSION" = "3.12" ] || fail "ImageFind 需要 Python 3.12,当前为 $PYTHON_VERSION。" mkdir -p "$RUNTIME_ROOT" TARGET="$RUNTIME_ROOT/core-$VERSION" STAGE="$RUNTIME_ROOT/.core-$VERSION.tmp.$$" CURRENT="$RUNTIME_ROOT/current" runtime_valid() { [ -x "$1/bin/python" ] || return 1 [ -f "$1/.imagefind-package-id" ] || return 1 [ "$(sed -n '1p' "$1/.imagefind-package-id")" = "$PACKAGE_ID" ] || return 1 RUNTIME_VERSION=$( IMAGEFIND_DATA_DIR="$DATA_ROOT/data" "$1/bin/python" -c \ 'import imagefind; print(imagefind.__version__)' 2>"$RUNTIME_ROOT/version-check.err" ) || return 1 [ "$RUNTIME_VERSION" = "$VERSION" ] } if ! runtime_valid "$TARGET"; then rm -rf "$STAGE" "$SYSTEM_PYTHON" -m venv "$STAGE" \ || { rm -rf "$STAGE"; fail "ImageFind 无法创建 Python 3.12 核心运行环境。"; } APP_WHEEL="" for candidate in "$APP_ROOT"/runtime/imagefind-*.whl; do if [ -f "$candidate" ]; then APP_WHEEL="$candidate" break fi done [ -n "$APP_WHEEL" ] \ || { rm -rf "$STAGE"; fail "ImageFind 运行环境初始化失败:安装包缺少应用 wheel。"; } if ! "$STAGE/bin/python" -m pip install \ --disable-pip-version-check --no-input --no-index \ --find-links "$WHEEL_ROOT" --requirement "$CORE_REQUIREMENTS" "$APP_WHEEL"; then rm -rf "$STAGE" fail "ImageFind 核心运行环境离线安装失败,请重新安装应用。" fi printf '%s\n' "$PACKAGE_ID" >"$STAGE/.imagefind-package-id" runtime_valid "$STAGE" \ || { if [ -s "$RUNTIME_ROOT/version-check.err" ]; then cat "$RUNTIME_ROOT/version-check.err" >&2 || true fi rm -rf "$STAGE" fail "ImageFind 核心运行环境校验失败。" } if [ -e "$TARGET" ]; then BACKUP="$RUNTIME_ROOT/core-$VERSION.invalid.$(date +%s)" mv "$TARGET" "$BACKUP" fi mv "$STAGE" "$TARGET" fi LINK="$RUNTIME_ROOT/.current.$$" rm -f "$LINK" ln -s "$TARGET" "$LINK" if [ -d "$CURRENT" ] && [ ! -L "$CURRENT" ]; then mv "$CURRENT" "$RUNTIME_ROOT/current.previous.$(date +%s)" fi # mv follows a destination symlink that points at a directory and would place # LINK *inside* the old runtime. Replace the directory entry itself so an # upgrade atomically switches current even when it is a valid or dangling # symlink left by an earlier release. if ! "$SYSTEM_PYTHON" -c \ 'import os,sys; os.replace(sys.argv[1], sys.argv[2])' \ "$LINK" "$CURRENT"; then rm -f "$LINK" fail "ImageFind 核心运行环境切换失败:无法原子更新 current。" fi if ! runtime_valid "$CURRENT"; then if [ -L "$CURRENT" ]; then CURRENT_LINK=$(readlink "$CURRENT" 2>/dev/null || printf '%s' '无法读取') elif [ -e "$CURRENT" ]; then CURRENT_LINK='不是符号链接' else CURRENT_LINK='不存在' fi if [ -x "$CURRENT/bin/python" ]; then ACTUAL_VERSION=$( IMAGEFIND_DATA_DIR="$DATA_ROOT/data" "$CURRENT/bin/python" -c \ 'import imagefind; print(imagefind.__version__)' 2>"$RUNTIME_ROOT/version-check.err" \ || printf '%s' '无法读取' ) else ACTUAL_VERSION='Python 不可执行' fi fail "ImageFind 核心运行环境切换失败:期望版本 $VERSION,current=$CURRENT_LINK,实际版本=$ACTUAL_VERSION。" fi