release: sync public installer asset install.sh
This commit is contained in:
+24
-24
@@ -41,8 +41,8 @@ MAINTENANCE_ACTION=
|
|||||||
FUSE_PATH_INPUT=
|
FUSE_PATH_INPUT=
|
||||||
confirm_fuse_action() {
|
confirm_fuse_action() {
|
||||||
local confirmation
|
local confirmation
|
||||||
confirmation=$(prompt_tty '该操作将停止并重新启动 app;输入 FUSE_CONFIRM 继续: ')
|
confirmation=$(prompt_tty '该操作将停止并重新启动应用,输入 1 继续,其他内容取消: ')
|
||||||
[[ $confirmation == FUSE_CONFIRM ]] || die '未确认 FUSE 生命周期操作'
|
[[ $confirmation == 1 ]] || die '已取消挂载操作'
|
||||||
}
|
}
|
||||||
require_public_app_installation() {
|
require_public_app_installation() {
|
||||||
local missing=()
|
local missing=()
|
||||||
@@ -80,14 +80,14 @@ print_fuse_status() {
|
|||||||
if [[ -n $mount_id && $source == xymediavault ]] && { [[ $fstype == fuse.xymediavault ]] || [[ $fstype == fuse ]]; }; then
|
if [[ -n $mount_id && $source == xymediavault ]] && { [[ $fstype == fuse.xymediavault ]] || [[ $fstype == fuse ]]; }; then
|
||||||
printf '挂载身份:owned xymediavault/fuse.xymediavault(ID=%s)\n' "$mount_id" >>$TTY_OUT
|
printf '挂载身份:owned xymediavault/fuse.xymediavault(ID=%s)\n' "$mount_id" >>$TTY_OUT
|
||||||
elif [[ -n $mount_id ]]; then
|
elif [[ -n $mount_id ]]; then
|
||||||
printf '挂载身份:foreign/unknown(ID=%s source=%s fstype=%s)\n' "$mount_id" "$source" "$fstype" >>$TTY_OUT
|
printf '挂载身份:外部或未知(ID=%s 来源=%s 类型=%s)\n' "$mount_id" "$source" "$fstype" >>$TTY_OUT
|
||||||
else
|
else
|
||||||
printf '挂载身份:无法确认\n' >>$TTY_OUT
|
printf '挂载身份:无法确认\n' >>$TTY_OUT
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
health=$(curl --silent --show-error --max-time 5 -o /dev/null -w '%{http_code}' "http://127.0.0.1:${API_PORT}/api/health" 2>/dev/null || true)
|
health=$(curl --silent --show-error --max-time 5 -o /dev/null -w '%{http_code}' "http://127.0.0.1:${API_PORT}/api/health" 2>/dev/null || true)
|
||||||
[[ $health =~ ^[0-9]{3}$ ]] || health='无法连接'
|
[[ $health =~ ^[0-9]{3}$ ]] || health='无法连接'
|
||||||
printf 'app health:%s\n' "$health" >>$TTY_OUT
|
printf '应用健康状态:%s\n' "$health" >>$TTY_OUT
|
||||||
}
|
}
|
||||||
while (($#)); do
|
while (($#)); do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
@@ -100,7 +100,7 @@ while (($#)); do
|
|||||||
--skip-components) SKIP_COMPONENTS=1; shift;;
|
--skip-components) SKIP_COMPONENTS=1; shift;;
|
||||||
--existing-db) EXISTING_DB=1; shift;;
|
--existing-db) EXISTING_DB=1; shift;;
|
||||||
-h|--help) usage; exit 0;;
|
-h|--help) usage; exit 0;;
|
||||||
*) die "unknown option: $1";;
|
*) die "未知选项:$1";;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
die() { printf '\n\033[31mxymediavault installer: %s\033[0m\n' "$*" >&2; [[ -n $INSTALL_LOG ]] && printf '详情日志:%s\n' "$INSTALL_LOG" >&2; exit 1; }
|
die() { printf '\n\033[31mxymediavault installer: %s\033[0m\n' "$*" >&2; [[ -n $INSTALL_LOG ]] && printf '详情日志:%s\n' "$INSTALL_LOG" >&2; exit 1; }
|
||||||
@@ -109,11 +109,11 @@ cleanup_installer_tmp() { [[ -z ${INSTALL_TMP_DIR:-} ]] || rm -rf -- "$INSTALL_T
|
|||||||
trap cleanup_installer_tmp EXIT
|
trap cleanup_installer_tmp EXIT
|
||||||
init_installer_log() {
|
init_installer_log() {
|
||||||
local tmp_parent=${TMPDIR:-/tmp} mode
|
local tmp_parent=${TMPDIR:-/tmp} mode
|
||||||
[[ -d $tmp_parent && ! -L $tmp_parent ]] || die 'TMPDIR must be an existing non-symlink directory'
|
[[ -d $tmp_parent && ! -L $tmp_parent ]] || die '临时目录必须存在且不能是符号链接目录'
|
||||||
if (( EUID == 0 )); then
|
if (( EUID == 0 )); then
|
||||||
mode=$(stat -c '%a' "$tmp_parent")
|
mode=$(stat -c '%a' "$tmp_parent")
|
||||||
[[ $(stat -c '%u' "$tmp_parent") -eq 0 ]] || die 'TMPDIR must be owned by root when running as root'
|
[[ $(stat -c '%u' "$tmp_parent") -eq 0 ]] || die '以管理员权限运行时,临时目录必须由 root 所有'
|
||||||
[[ $((mode & 022)) -eq 0 || $((mode & 1000)) -ne 0 ]] || die 'TMPDIR must not be group/world writable unless sticky'
|
[[ $((mode & 022)) -eq 0 || $((mode & 1000)) -ne 0 ]] || die '临时目录不能允许组用户或其他用户写入,除非启用了粘滞位'
|
||||||
fi
|
fi
|
||||||
umask 077
|
umask 077
|
||||||
INSTALL_TMP_DIR=$(mktemp -d "$tmp_parent/xymediavault-install.XXXXXX") || die '无法创建私有安装临时目录'
|
INSTALL_TMP_DIR=$(mktemp -d "$tmp_parent/xymediavault-install.XXXXXX") || die '无法创建私有安装临时目录'
|
||||||
@@ -154,8 +154,8 @@ run_visible_logged() {
|
|||||||
}
|
}
|
||||||
usage() { sed -n '1,20p' "$0"; }
|
usage() { sed -n '1,20p' "$0"; }
|
||||||
read_tty() { if ((TTY_TEST_MODE)); then [[ $TTY_TEST_INDEX -lt ${#TTY_TEST_LINES[@]} ]] || return 1; printf -v "$1" '%s' "${TTY_TEST_LINES[$TTY_TEST_INDEX]}"; TTY_TEST_INDEX=$((TTY_TEST_INDEX + 1)); else IFS= read -r "$1" <"$TTY_IN"; fi; }
|
read_tty() { if ((TTY_TEST_MODE)); then [[ $TTY_TEST_INDEX -lt ${#TTY_TEST_LINES[@]} ]] || return 1; printf -v "$1" '%s' "${TTY_TEST_LINES[$TTY_TEST_INDEX]}"; TTY_TEST_INDEX=$((TTY_TEST_INDEX + 1)); else IFS= read -r "$1" <"$TTY_IN"; fi; }
|
||||||
prompt_tty() { [[ -r $TTY_IN && -w $TTY_OUT ]] || die 'interactive menu requires a TTY; use menu option 1 from a terminal'; printf '%s' "$1" >>$TTY_OUT; read_tty value || die 'unable to read terminal input'; printf '%s' "$value"; }
|
prompt_tty() { [[ -r $TTY_IN && -w $TTY_OUT ]] || die '交互菜单需要终端,请在终端中运行安装器'; printf '%s' "$1" >>$TTY_OUT; read_tty value || die '无法读取终端输入'; printf '%s' "$value"; }
|
||||||
prompt_tty_into() { [[ -r $TTY_IN && -w $TTY_OUT ]] || die 'interactive menu requires a TTY; use menu option 1 from a terminal'; printf '%s' "$2" >>$TTY_OUT; read_tty "$1" || die 'unable to read terminal input'; }
|
prompt_tty_into() { [[ -r $TTY_IN && -w $TTY_OUT ]] || die '交互菜单需要终端,请在终端中运行安装器'; printf '%s' "$2" >>$TTY_OUT; read_tty "$1" || die '无法读取终端输入'; }
|
||||||
controller_token_file() { printf '%s/secrets/xiaoya-controller-token' "$INSTALL_DIR"; }
|
controller_token_file() { printf '%s/secrets/xiaoya-controller-token' "$INSTALL_DIR"; }
|
||||||
ensure_xiaoya_dir() {
|
ensure_xiaoya_dir() {
|
||||||
local required=${1:-0}
|
local required=${1:-0}
|
||||||
@@ -165,10 +165,10 @@ ensure_xiaoya_dir() {
|
|||||||
[[ $XIAOYA_DIR = /* && $XIAOYA_DIR != / ]] || die '小雅目录必须是绝对路径且不能是根目录'
|
[[ $XIAOYA_DIR = /* && $XIAOYA_DIR != / ]] || die '小雅目录必须是绝对路径且不能是根目录'
|
||||||
if [[ -d $XIAOYA_DIR ]] && [[ -n $(find "$XIAOYA_DIR" -mindepth 1 -maxdepth 1 -print -quit) ]]; then
|
if [[ -d $XIAOYA_DIR ]] && [[ -n $(find "$XIAOYA_DIR" -mindepth 1 -maxdepth 1 -print -quit) ]]; then
|
||||||
printf '检测到已有非空小雅目录:%s\n' "$XIAOYA_DIR" >>$TTY_OUT
|
printf '检测到已有非空小雅目录:%s\n' "$XIAOYA_DIR" >>$TTY_OUT
|
||||||
printf '请选择 attach / install-controller / separate / return:' >>$TTY_OUT
|
printf '请选择:1) 继续使用此目录 0) 返回:' >>$TTY_OUT
|
||||||
read_tty choice || die 'unable to read terminal input'
|
read_tty choice || die '无法读取终端输入'
|
||||||
[[ $choice == attach || $choice == install-controller || $choice == separate || $choice == return ]] || die '无效选择,已停止以保护已有数据'
|
[[ $choice == 1 || $choice == 0 ]] || die '无效选择,已停止以保护已有数据'
|
||||||
[[ $choice != return ]] || exit 0
|
[[ $choice != 0 ]] || exit 0
|
||||||
fi
|
fi
|
||||||
mkdir -p "$XIAOYA_DIR" "$INSTALL_DIR/secrets"
|
mkdir -p "$XIAOYA_DIR" "$INSTALL_DIR/secrets"
|
||||||
}
|
}
|
||||||
@@ -178,10 +178,10 @@ write_controller_token() {
|
|||||||
}
|
}
|
||||||
menu() {
|
menu() {
|
||||||
[[ $# -eq 0 ]] || return 0
|
[[ $# -eq 0 ]] || return 0
|
||||||
[[ -r $TTY_IN && -w $TTY_OUT ]] || die 'no TTY: interactive installer menu cannot run from a non-interactive pipe'
|
[[ -r $TTY_IN && -w $TTY_OUT ]] || die '没有可用终端:安装菜单不能通过非交互管道运行'
|
||||||
while :; do
|
while :; do
|
||||||
printf '\nXyMediaVault 安装器\n1) 仅安装管理平台\n2) 安装管理平台和本机小雅\n3) 安装管理平台并连接远程小雅控制器\n4) 仅安装小雅控制器\n5) 状态/诊断/维护\n0) 退出\n选择:' >>$TTY_OUT
|
printf '\nXyMediaVault 安装器\n1) 仅安装管理平台\n2) 安装管理平台和本机小雅\n3) 安装管理平台并连接远程小雅控制器\n4) 仅安装小雅控制器\n5) 状态/诊断/维护\n0) 退出\n选择:' >>$TTY_OUT
|
||||||
read_tty choice || die 'unable to read terminal input'
|
read_tty choice || die '无法读取终端输入'
|
||||||
case "$choice" in
|
case "$choice" in
|
||||||
1) return 0;;
|
1) return 0;;
|
||||||
2) ensure_xiaoya_dir; printf '小雅目录已准备:%s;将安装小雅和小雅控制器。\n' "$XIAOYA_DIR" >>$TTY_OUT; write_controller_token; CONTROLLER_URL='http://xymedia-controller:19090'; MENU_PROFILES='xiaoya xiaoya-control'; return 0;;
|
2) ensure_xiaoya_dir; printf '小雅目录已准备:%s;将安装小雅和小雅控制器。\n' "$XIAOYA_DIR" >>$TTY_OUT; write_controller_token; CONTROLLER_URL='http://xymedia-controller:19090'; MENU_PROFILES='xiaoya xiaoya-control'; return 0;;
|
||||||
@@ -189,7 +189,7 @@ menu() {
|
|||||||
4) ensure_xiaoya_dir 1; if [[ ${XYMEDIA_INSTALL_TEST_DISCOVERY_ONLY:-0} != 1 && -z $CONTROLLER_BIND_EXPLICIT ]]; then prompt_tty_into CONTROLLER_BIND_ADDRESS "控制器监听地址 [${CONTROLLER_BIND_ADDRESS}]: "; CONTROLLER_BIND_ADDRESS=${CONTROLLER_BIND_ADDRESS:-0.0.0.0}; fi; if [[ ${XYMEDIA_INSTALL_TEST_DISCOVERY_ONLY:-0} != 1 && -z $CONTROLLER_PORT_EXPLICIT ]]; then prompt_tty_into CONTROLLER_PORT "控制器端口 [${CONTROLLER_PORT}]: "; CONTROLLER_PORT=${CONTROLLER_PORT:-19090}; fi; write_controller_token; CONTROLLER_ONLY=1; printf '小雅控制器安装目录已准备。\n' >>$TTY_OUT; MENU_PROFILES='xiaoya-control'; return 0;;
|
4) ensure_xiaoya_dir 1; if [[ ${XYMEDIA_INSTALL_TEST_DISCOVERY_ONLY:-0} != 1 && -z $CONTROLLER_BIND_EXPLICIT ]]; then prompt_tty_into CONTROLLER_BIND_ADDRESS "控制器监听地址 [${CONTROLLER_BIND_ADDRESS}]: "; CONTROLLER_BIND_ADDRESS=${CONTROLLER_BIND_ADDRESS:-0.0.0.0}; fi; if [[ ${XYMEDIA_INSTALL_TEST_DISCOVERY_ONLY:-0} != 1 && -z $CONTROLLER_PORT_EXPLICIT ]]; then prompt_tty_into CONTROLLER_PORT "控制器端口 [${CONTROLLER_PORT}]: "; CONTROLLER_PORT=${CONTROLLER_PORT:-19090}; fi; write_controller_token; CONTROLLER_ONLY=1; printf '小雅控制器安装目录已准备。\n' >>$TTY_OUT; MENU_PROFILES='xiaoya-control'; return 0;;
|
||||||
5) while :; do
|
5) while :; do
|
||||||
printf '\n维护菜单\n1) 查看状态\n2) 一键挂载本地媒体库\n3) 启动/重启已配置挂载\n4) 停用本地媒体库挂载\n0) 返回\n选择:' >>$TTY_OUT
|
printf '\n维护菜单\n1) 查看状态\n2) 一键挂载本地媒体库\n3) 启动/重启已配置挂载\n4) 停用本地媒体库挂载\n0) 返回\n选择:' >>$TTY_OUT
|
||||||
read_tty maintenance_choice || die 'unable to read terminal input'
|
read_tty maintenance_choice || die '无法读取终端输入'
|
||||||
case "$maintenance_choice" in
|
case "$maintenance_choice" in
|
||||||
1) require_public_app_installation || continue; print_fuse_status;;
|
1) require_public_app_installation || continue; print_fuse_status;;
|
||||||
2) require_public_app_installation || continue; prompt_tty_into FUSE_PATH_INPUT '宿主机媒体库目录(必须为空):'; confirm_fuse_action; MAINTENANCE_ACTION=configure; return 0;;
|
2) require_public_app_installation || continue; prompt_tty_into FUSE_PATH_INPUT '宿主机媒体库目录(必须为空):'; confirm_fuse_action; MAINTENANCE_ACTION=configure; return 0;;
|
||||||
@@ -228,15 +228,15 @@ validate_container_name() {
|
|||||||
[[ $XIAOYA_CONTAINER =~ ^[A-Za-z0-9_][A-Za-z0-9_.-]{0,254}$ ]] || die '小雅容器名称不合法,不能包含换行或 Compose 特殊字符'
|
[[ $XIAOYA_CONTAINER =~ ^[A-Za-z0-9_][A-Za-z0-9_.-]{0,254}$ ]] || die '小雅容器名称不合法,不能包含换行或 Compose 特殊字符'
|
||||||
}
|
}
|
||||||
progress '检查安装环境'
|
progress '检查安装环境'
|
||||||
[[ $INSTALL_DIR = /* && $INSTALL_DIR != / ]] || die 'install directory must be an absolute non-root path'
|
[[ $INSTALL_DIR = /* && $INSTALL_DIR != / ]] || die '安装目录必须是绝对路径且不能是根目录'
|
||||||
guard_existing_install_tree
|
guard_existing_install_tree
|
||||||
init_installer_log
|
init_installer_log
|
||||||
command -v docker >/dev/null || die 'Docker is required'
|
command -v docker >/dev/null || die '需要安装 Docker'
|
||||||
if docker compose version >/dev/null 2>&1; then COMPOSE=(docker compose); elif command -v docker-compose >/dev/null; then COMPOSE=(docker-compose); else die 'Docker Compose is required'; fi
|
if docker compose version >/dev/null 2>&1; then COMPOSE=(docker compose); elif command -v docker-compose >/dev/null; then COMPOSE=(docker-compose); else die '需要安装 Docker Compose'; fi
|
||||||
docker info >/dev/null 2>&1 || die 'cannot connect to Docker'
|
docker info >/dev/null 2>&1 || die '无法连接 Docker,请确认 Docker 已启动'
|
||||||
command -v curl >/dev/null || die 'curl is required'
|
command -v curl >/dev/null || die '需要安装 curl'
|
||||||
command -v zstd >/dev/null || die 'zstd is required for release archives'
|
command -v zstd >/dev/null || die '需要安装 zstd,用于解压发布包'
|
||||||
command -v python3 >/dev/null || die 'python3 is required for safe archive extraction'
|
command -v python3 >/dev/null || die '需要安装 python3,用于安全解压发布包'
|
||||||
if [[ -n $MAINTENANCE_ACTION ]]; then
|
if [[ -n $MAINTENANCE_ACTION ]]; then
|
||||||
require_public_app_installation || die '维护操作仅支持已有完整 app 安装'
|
require_public_app_installation || die '维护操作仅支持已有完整 app 安装'
|
||||||
case "$MAINTENANCE_ACTION" in
|
case "$MAINTENANCE_ACTION" in
|
||||||
|
|||||||
Reference in New Issue
Block a user