This commit is contained in:
root
2026-05-18 11:04:52 +00:00
parent efa5533032
commit 088a5a6429
6 changed files with 136 additions and 29 deletions

View File

@@ -26,13 +26,22 @@ MDNS_HOST="${HOSTNAME_ALIAS:-live}.local"
getent hosts "$MDNS_HOST" >/dev/null 2>&1 && echo "$MDNS_HOST resolves: yes" || echo "$MDNS_HOST resolves: fail" getent hosts "$MDNS_HOST" >/dev/null 2>&1 && echo "$MDNS_HOST resolves: yes" || echo "$MDNS_HOST resolves: fail"
section "config" section "config"
if [[ -f "$REPO/config/system-stack.env" ]]; then
echo "repo stack env: present ($REPO/config/system-stack.env)"
else
echo "repo stack env: missing"
fi
if [[ -f /opt/live/config/system.json ]]; then if [[ -f /opt/live/config/system.json ]]; then
echo "system.json: present" echo "system.json: present"
elif [[ -f "$REPO/config/system-stack.env" ]]; then
echo "system.json: not used by repo-managed install"
else else
echo "system.json: MISSING (run install-core)" echo "system.json: MISSING (run install-core)"
fi fi
if [[ -f /opt/live/generated/system-stack.env ]]; then if [[ -f /opt/live/generated/system-stack.env ]]; then
echo "generated env: present" echo "generated env: present"
elif [[ -f "$REPO/config/system-stack.env" ]]; then
echo "generated env: repo-managed via config/system-stack.env"
else else
echo "generated env: missing" echo "generated env: missing"
fi fi

View File

@@ -6,5 +6,6 @@ tqdm>=4.67.1
httpx[http2]>=0.28.1 httpx[http2]>=0.28.1
PyExecJS>=1.5.1 PyExecJS>=1.5.1
fastapi>=0.115.0 fastapi>=0.115.0
eval-type-backport>=0.2.0
uvicorn[standard]>=0.30.0 uvicorn[standard]>=0.30.0
python-multipart>=0.0.9 python-multipart>=0.0.9

View File

@@ -262,6 +262,9 @@ def _try_local_console_build() -> tuple[bool, str]:
return True, "up-to-date" return True, "up-to-date"
print("[launch] 正在构建 Web 控制台…") print("[launch] 正在构建 Web 控制台…")
for path in (CONSOLE / "node_modules", CONSOLE / ".next"):
if path.exists():
shutil.rmtree(path, ignore_errors=True)
lock = CONSOLE / "package-lock.json" lock = CONSOLE / "package-lock.json"
if lock.is_file(): if lock.is_file():
rc = _run_console_cmd([npm, "ci"], cwd=CONSOLE, env=env) rc = _run_console_cmd([npm, "ci"], cwd=CONSOLE, env=env)
@@ -290,9 +293,18 @@ def _docker_console_build() -> bool:
print(f"[launch] 本机 Node 不足,改用 Docker 构建: {DOCKER_NODE_IMAGE}") print(f"[launch] 本机 Node 不足,改用 Docker 构建: {DOCKER_NODE_IMAGE}")
script = ( script = (
"set -e; " "set -e; "
"rm -rf /tmp/web-console; "
"mkdir -p /tmp/web-console; "
"cd /workspace/web-console; " "cd /workspace/web-console; "
"if [ ! -d node_modules ]; then npm ci || npm install; fi; " "tar --exclude=node_modules --exclude=.next --exclude=out -cf - . | tar -C /tmp/web-console -xf -; "
"npm run build" "cd /tmp/web-console; "
"rm -rf node_modules .next out; "
"export HOME=/tmp npm_config_cache=/tmp/npm-cache; "
"if [ -f package-lock.json ]; then npm ci || npm install; else npm install; fi; "
"test -x node_modules/.bin/next; "
"npm run build; "
"rm -rf /workspace/web-console/out; "
"cp -a out /workspace/web-console/out"
) )
cmd = [ cmd = [
docker, docker,
@@ -300,6 +312,10 @@ def _docker_console_build() -> bool:
"--rm", "--rm",
"-u", "-u",
f"{os.getuid()}:{os.getgid()}", f"{os.getuid()}:{os.getgid()}",
"-e",
"HOME=/tmp",
"-e",
"npm_config_cache=/tmp/npm-cache",
"-v", "-v",
f"{ROOT}:/workspace", f"{ROOT}:/workspace",
"-w", "-w",
@@ -390,7 +406,10 @@ def _start_docker_console_dev(*, host: str, port: str, next_port: str) -> subpro
script = ( script = (
"set -e; " "set -e; "
"cd /workspace/web-console; " "cd /workspace/web-console; "
"if [ ! -d node_modules ]; then npm ci || npm install; fi; " "if [ ! -x node_modules/.bin/next ]; then rm -rf node_modules .next; fi; "
"if [ ! -d node_modules ]; then "
"if [ -f package-lock.json ]; then npm ci || npm install; else npm install; fi; "
"fi; "
"npm run dev -- --hostname 0.0.0.0 --port 3000" "npm run dev -- --hostname 0.0.0.0 --port 3000"
) )
cmd = [ cmd = [
@@ -404,6 +423,10 @@ def _start_docker_console_dev(*, host: str, port: str, next_port: str) -> subpro
"-e", "-e",
f"API_PROXY_TARGET={proxy_target}", f"API_PROXY_TARGET={proxy_target}",
"-e", "-e",
"HOME=/tmp",
"-e",
"npm_config_cache=/tmp/npm-cache",
"-e",
"HOST=0.0.0.0", "HOST=0.0.0.0",
"-e", "-e",
"PORT=3000", "PORT=3000",

View File

@@ -0,0 +1,45 @@
#!/usr/bin/env bash
set -euo pipefail
SELF_DIR="$(cd "$(dirname "$0")" && pwd)"
INSTALL_TAG="all"
. "$SELF_DIR/lib/common.sh"
ensure_root "$@"
load_env
ensure_live_user
normalize_repo_root
ensure_runtime_dirs_owned_by_live
install_base_packages
install_media_packages
configure_adb_udev
install_nodejs
install_docker_stack
install_android_panel
prepare_python_runtime
prepare_web_console
install_live_console_service
configure_sudoers
configure_mdns
configure_wifi_profile
install_shellcrash
install_browser_stack
install_neko_browser
install_redroid
prepare_mitmproxy
prepare_vscode_server
prepare_uptime_kuma
prepare_openclaw
install_pulseaudio_service
install_kmscube_service
install_webtty
install_cockpit
install_netdata
prepare_homepage
prepare_filebrowser
prepare_srs
run_hardware_probe
install_live_edge_proxy
ensure_runtime_dirs_owned_by_live
print_summary "all"

View File

@@ -12,8 +12,7 @@ case "$PROFILE" in
exec bash "$ROOT_DIR/scripts/linux/install_business.sh" "${@:2}" exec bash "$ROOT_DIR/scripts/linux/install_business.sh" "${@:2}"
;; ;;
all|full) all|full)
bash "$ROOT_DIR/scripts/linux/install_hub.sh" "${@:2}" exec bash "$ROOT_DIR/scripts/linux/install_all.sh" "${@:2}"
bash "$ROOT_DIR/scripts/linux/install_business.sh" "${@:2}"
;; ;;
*) *)
cat <<USAGE cat <<USAGE

View File

@@ -701,31 +701,48 @@ install_docker_stack() {
prepare_python_runtime() { prepare_python_runtime() {
log "Preparing Python virtual environment" log "Preparing Python virtual environment"
chown -R live:live "$ROOT_DIR" chown -R live:live "$ROOT_DIR"
rm -rf "$ROOT_DIR/.venv" local req_hash req_stamp
as_live "python3 -m venv .venv" || { req_stamp="$ROOT_DIR/.venv/.requirements.sha256"
log "venv create failed; retry once with --clear" req_hash="$(sha256sum "$ROOT_DIR/requirements.txt" "$ROOT_DIR/pyproject.toml" 2>/dev/null | sha256sum | awk '{print $1}')"
if ! as_live "test -x .venv/bin/python && .venv/bin/python -V >/dev/null 2>&1"; then
rm -rf "$ROOT_DIR/.venv" rm -rf "$ROOT_DIR/.venv"
if ! as_live "python3 -m venv .venv"; then as_live "python3 -m venv .venv" || {
log "WARN: venv still failed; using a shim over the system Python" log "venv create failed; retry once with --clear"
rm -rf "$ROOT_DIR/.venv" rm -rf "$ROOT_DIR/.venv"
create_python_venv_shim "$ROOT_DIR/.venv" if ! as_live "python3 -m venv .venv"; then
fi log "WARN: venv still failed; using a shim over the system Python"
} rm -rf "$ROOT_DIR/.venv"
if ! as_live ". .venv/bin/activate && pip install --upgrade pip && pip install -r requirements.txt"; then create_python_venv_shim "$ROOT_DIR/.venv"
log "pip install failed; retry with --no-cache-dir" fi
as_live ". .venv/bin/activate && pip install --upgrade pip && pip install --no-cache-dir -r requirements.txt" || \ }
log "WARN: Python deps incomplete; control plane may fail until fixed manually" else
log "Reusing existing Python virtual environment"
fi fi
if [ -f "$req_stamp" ] && [ "$(cat "$req_stamp" 2>/dev/null)" = "$req_hash" ]; then
log "Python requirements unchanged; skipping pip install"
return 0
fi
if ! as_live ". .venv/bin/activate && python -m pip install --upgrade pip && python -m pip install -r requirements.txt"; then
log "pip install failed; retry with --no-cache-dir"
if ! as_live ". .venv/bin/activate && python -m pip install --upgrade pip && python -m pip install --no-cache-dir -r requirements.txt"; then
log "WARN: Python deps incomplete; control plane may fail until fixed manually"
return 0
fi
fi
printf '%s\n' "$req_hash" >"$req_stamp"
chown live:live "$req_stamp" 2>/dev/null || true
} }
prepare_web_console() { prepare_web_console() {
log "Building web console" log "Building web console"
if as_live "cd web-console && if [ -f package-lock.json ]; then npm ci; else npm install; fi && npm run build"; then if as_live ". .venv/bin/activate && python scripts/launch.py --build-only --no-env-check"; then
return 0 return 0
fi fi
log "web console build failed; cleaning node_modules and retrying" log "web console build failed; cleaning artifacts and retrying"
as_live "cd web-console && rm -rf node_modules .next && npm install && npm run build" || \ as_live "cd web-console && rm -rf node_modules .next out" || true
if ! as_live ". .venv/bin/activate && python scripts/launch.py --build-only --no-env-check"; then
log "WARN: web console build still failed — service may serve stale assets until rebuilt" log "WARN: web console build still failed — service may serve stale assets until rebuilt"
fi
} }
install_live_console_service() { install_live_console_service() {
@@ -736,6 +753,8 @@ install_live_console_service() {
Description=Live Super Hub Control Plane Description=Live Super Hub Control Plane
After=network-online.target docker.service After=network-online.target docker.service
Wants=network-online.target Wants=network-online.target
# 避免构建/依赖短暂失败时触发 systemd 默认「5 次/10s」后永久停跑表现为 live-edge 502、:8001 无监听)
StartLimitIntervalSec=0
[Service] [Service]
User=live User=live
@@ -747,8 +766,6 @@ LimitNOFILE=$LIVE_NOFILE_SOFT
ExecStart=$ROOT_DIR/start.sh ExecStart=$ROOT_DIR/start.sh
Restart=always Restart=always
RestartSec=5 RestartSec=5
# 避免构建/依赖短暂失败时触发 systemd 默认「5 次/10s」后永久停跑表现为 live-edge 502、:8001 无监听)
StartLimitIntervalSec=0
# 首次 npm build / 冷启动较慢;过小会导致主进程在 listen 前被 SIGTERM # 首次 npm build / 冷启动较慢;过小会导致主进程在 listen 前被 SIGTERM
TimeoutStartSec=300 TimeoutStartSec=300
TimeoutStopSec=30 TimeoutStopSec=30
@@ -1338,14 +1355,27 @@ else:
path.write_text(text, encoding="utf-8") path.write_text(text, encoding="utf-8")
PY PY
chown -R live:live "$ANDROID_PANEL_DIR" chown -R live:live "$ANDROID_PANEL_DIR"
rm -rf "$ANDROID_PANEL_DIR/.venv" local panel_req_hash panel_req_stamp
if ! runuser -u live -- bash -lc "python3 -m venv '$ANDROID_PANEL_DIR/.venv'"; then panel_req_stamp="$ANDROID_PANEL_DIR/.venv/.requirements.sha256"
log "WARN: Android panel venv failed; using a shim over the system Python" panel_req_hash="$(sha256sum "$ANDROID_PANEL_DIR/requirements.txt" 2>/dev/null | awk '{print $1}')"
if ! runuser -u live -- bash -lc "test -x '$ANDROID_PANEL_DIR/.venv/bin/python' && '$ANDROID_PANEL_DIR/.venv/bin/python' -V >/dev/null 2>&1"; then
rm -rf "$ANDROID_PANEL_DIR/.venv" rm -rf "$ANDROID_PANEL_DIR/.venv"
create_python_venv_shim "$ANDROID_PANEL_DIR/.venv" if ! runuser -u live -- bash -lc "python3 -m venv '$ANDROID_PANEL_DIR/.venv'"; then
chown -R live:live "$ANDROID_PANEL_DIR/.venv" log "WARN: Android panel venv failed; using a shim over the system Python"
rm -rf "$ANDROID_PANEL_DIR/.venv"
create_python_venv_shim "$ANDROID_PANEL_DIR/.venv"
chown -R live:live "$ANDROID_PANEL_DIR/.venv"
fi
else
log "Reusing Android panel virtual environment"
fi
if [ ! -f "$panel_req_stamp" ] || [ "$(cat "$panel_req_stamp" 2>/dev/null)" != "$panel_req_hash" ]; then
runuser -u live -- bash -lc ". '$ANDROID_PANEL_DIR/.venv/bin/activate' && python -m pip install --upgrade pip && python -m pip install -r '$ANDROID_PANEL_DIR/requirements.txt'"
printf '%s\n' "$panel_req_hash" >"$panel_req_stamp"
chown live:live "$panel_req_stamp" 2>/dev/null || true
else
log "Android panel requirements unchanged; skipping pip install"
fi fi
runuser -u live -- bash -lc ". '$ANDROID_PANEL_DIR/.venv/bin/activate' && pip install --upgrade pip && pip install -r '$ANDROID_PANEL_DIR/requirements.txt'"
allow_tcp_port_if_firewall_active "$ANDROID_PANEL_PORT" allow_tcp_port_if_firewall_active "$ANDROID_PANEL_PORT"
cat >/etc/systemd/system/live-android-panel.service <<UNIT cat >/etc/systemd/system/live-android-panel.service <<UNIT