Fix curl-pipe-bash install menu on Linux.
Avoid unbound BASH_SOURCE under set -u, attach stdin to /dev/tty for interactive prompts, and handle empty menu input without looping. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+36
-13
@@ -3,6 +3,7 @@
|
|||||||
# 用法:
|
# 用法:
|
||||||
# curl -fsSL https://git.bz121.com/dekun/LocalNav/raw/main/scripts/install.sh | bash
|
# curl -fsSL https://git.bz121.com/dekun/LocalNav/raw/main/scripts/install.sh | bash
|
||||||
# bash install.sh
|
# bash install.sh
|
||||||
|
# curl -fsSL .../install.sh | bash -s -- install -y
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
DEFAULT_REPO="https://git.bz121.com/dekun/LocalNav.git"
|
DEFAULT_REPO="https://git.bz121.com/dekun/LocalNav.git"
|
||||||
@@ -15,20 +16,42 @@ need_cmd() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
run_navctl() {
|
||||||
|
local navctl_py="$1"
|
||||||
|
shift
|
||||||
|
# curl | bash 时 stdin 是管道,无法交互;改从终端设备读取
|
||||||
|
if [ ! -t 0 ]; then
|
||||||
|
if [ ! -r /dev/tty ]; then
|
||||||
|
echo "[FAIL] 当前为非交互环境且无法打开 /dev/tty" >&2
|
||||||
|
echo "请改用: curl -fsSL .../install.sh -o install.sh && bash install.sh" >&2
|
||||||
|
echo "或非交互安装: curl -fsSL .../install.sh | bash -s -- install -y" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
exec python3 "$navctl_py" "$@" </dev/tty
|
||||||
|
fi
|
||||||
|
exec python3 "$navctl_py" "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
download_and_run() {
|
||||||
|
local tmp_dir
|
||||||
|
tmp_dir="$(mktemp -d)"
|
||||||
|
trap 'rm -rf "$tmp_dir"' EXIT
|
||||||
|
echo "[OK] 正在下载部署管理脚本..."
|
||||||
|
curl -fsSL "${RAW_BASE}/navctl.py" -o "${tmp_dir}/navctl.py"
|
||||||
|
curl -fsSL "${RAW_BASE}/nav_common.py" -o "${tmp_dir}/nav_common.py"
|
||||||
|
export PYTHONPATH="${tmp_dir}:${PYTHONPATH:-}"
|
||||||
|
run_navctl "${tmp_dir}/navctl.py" "$@"
|
||||||
|
}
|
||||||
|
|
||||||
need_cmd python3
|
need_cmd python3
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" 2>/dev/null && pwd || true)"
|
# curl | bash 时 BASH_SOURCE 可能未设置;仅当脚本为真实文件时才走本地路径
|
||||||
NAVCTL_PY="${SCRIPT_DIR}/navctl.py"
|
script_path="${BASH_SOURCE[0]:-}"
|
||||||
|
if [[ -n "$script_path" && -f "$script_path" ]]; then
|
||||||
if [ ! -f "$NAVCTL_PY" ]; then
|
script_dir="$(cd "$(dirname "$script_path")" && pwd)"
|
||||||
TMP_DIR="$(mktemp -d)"
|
if [[ -f "${script_dir}/navctl.py" ]]; then
|
||||||
trap 'rm -rf "$TMP_DIR"' EXIT
|
run_navctl "${script_dir}/navctl.py" "$@"
|
||||||
echo "[OK] 正在下载部署管理脚本..."
|
fi
|
||||||
curl -fsSL "${RAW_BASE}/navctl.py" -o "${TMP_DIR}/navctl.py"
|
|
||||||
curl -fsSL "${RAW_BASE}/nav_common.py" -o "${TMP_DIR}/nav_common.py"
|
|
||||||
export PYTHONPATH="${TMP_DIR}:${PYTHONPATH:-}"
|
|
||||||
exec python3 "${TMP_DIR}/navctl.py" "$@"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cd "$(dirname "$NAVCTL_PY")"
|
download_and_run "$@"
|
||||||
exec python3 "$NAVCTL_PY" "$@"
|
|
||||||
|
|||||||
@@ -41,12 +41,25 @@ def _fail(msg: str) -> None:
|
|||||||
|
|
||||||
|
|
||||||
def _pause() -> None:
|
def _pause() -> None:
|
||||||
|
if not sys.stdin.isatty():
|
||||||
|
return
|
||||||
try:
|
try:
|
||||||
input("\n按回车返回菜单...")
|
input("\n按回车返回菜单...")
|
||||||
except (EOFError, KeyboardInterrupt):
|
except (EOFError, KeyboardInterrupt):
|
||||||
print()
|
print()
|
||||||
|
|
||||||
|
|
||||||
|
def _require_tty(action: str) -> None:
|
||||||
|
if sys.stdin.isatty():
|
||||||
|
return
|
||||||
|
print(f"[FAIL] 当前环境无法交互式{action}(stdin 不是终端)", file=sys.stderr)
|
||||||
|
print("请使用以下方式之一:", file=sys.stderr)
|
||||||
|
print(" bash scripts/install.sh", file=sys.stderr)
|
||||||
|
print(" python3 scripts/navctl.py install -y", file=sys.stderr)
|
||||||
|
print(" curl -fsSL .../install.sh | bash -s -- install -y", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
def prompt_value(label: str, default: str) -> str:
|
def prompt_value(label: str, default: str) -> str:
|
||||||
try:
|
try:
|
||||||
raw = input(f"{label} [{default}]: ").strip()
|
raw = input(f"{label} [{default}]: ").strip()
|
||||||
@@ -118,6 +131,8 @@ def run_deploy(project_dir: Path, py_exe: str) -> None:
|
|||||||
|
|
||||||
|
|
||||||
def action_install(*, repo: str = "", dest: str = "", yes: bool = False) -> None:
|
def action_install(*, repo: str = "", dest: str = "", yes: bool = False) -> None:
|
||||||
|
if not yes:
|
||||||
|
_require_tty("安装")
|
||||||
print()
|
print()
|
||||||
print("=" * 50)
|
print("=" * 50)
|
||||||
print(" 一键部署安装")
|
print(" 一键部署安装")
|
||||||
@@ -168,6 +183,7 @@ def action_update(*, dest: str = "") -> None:
|
|||||||
|
|
||||||
|
|
||||||
def action_uninstall(*, dest: str = "") -> None:
|
def action_uninstall(*, dest: str = "") -> None:
|
||||||
|
_require_tty("卸载")
|
||||||
print()
|
print()
|
||||||
print("=" * 50)
|
print("=" * 50)
|
||||||
print(" 一键卸载")
|
print(" 一键卸载")
|
||||||
@@ -249,6 +265,7 @@ def render_menu(project_dir: Path | None) -> None:
|
|||||||
|
|
||||||
|
|
||||||
def interactive_menu() -> None:
|
def interactive_menu() -> None:
|
||||||
|
_require_tty("打开菜单")
|
||||||
print(f"LocalNav 部署管理 · {platform.system()} {platform.release()}")
|
print(f"LocalNav 部署管理 · {platform.system()} {platform.release()}")
|
||||||
while True:
|
while True:
|
||||||
project_dir = resolve_project_dir()
|
project_dir = resolve_project_dir()
|
||||||
@@ -259,6 +276,11 @@ def interactive_menu() -> None:
|
|||||||
print("\n再见。")
|
print("\n再见。")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if not choice:
|
||||||
|
_warn("请输入 0-3")
|
||||||
|
_pause()
|
||||||
|
continue
|
||||||
|
|
||||||
if choice == "1":
|
if choice == "1":
|
||||||
try:
|
try:
|
||||||
action_install()
|
action_install()
|
||||||
|
|||||||
Reference in New Issue
Block a user