# 2026-07-18 ## ServerStack: 修复 Ubuntu 依赖安装的 libcurl 冲突 **问题**:在 Ubuntu 22.04 上跑 `installDepsUbuntu` 时出现 - 图片 1: `libcurl4-gnutls-dev` 与 `libcurl4-openssl-dev` 互相 Conflicts,导致 `E: Unable to correct problems, you have held broken packages`,整批依赖装不下去。 - 图片 2: 大量老包被 `filter_pkglist` 跳过(`libpng12-0/-dev`、`libpng3`、 `libidn11`、`libcloog-ppl1`、`sysv-rc`、`libjpeg62-turbo-dev`),虽然不报错, 但每次都刷一堆 `Skip unavailable package: ...` 噪音。 **根因**:`lib/check_sw.sh` 的 `installDepsUbuntu`(line 119)的 pkgList 同时列了 `libcurl4-gnutls-dev` 和 `libcurl4-openssl-dev` 这两个互斥的 `-dev` 包。 注释里说"Modern Debian/Ubuntu removed several legacy package names",但实际 pkgList 自身并没有清理这些老包。 **修复**(`E:\workbuddy\ServerStack\lib\check_sw.sh`,仅改 Ubuntu 列表 line 119): - 删掉 `libcurl4-gnutls-dev`(保留 `libcurl3-gnutls` 这个 runtime)。 依据:`modules/php/php-7.4.sh`、`php-8.0.sh`、`php-8.1.sh` 全部用 `libcurl4-openssl-dev`,没有任何地方用 gnutls dev。 - 顺手清理在新版 Ubuntu 上永远被 filter 跳过的死链包: `libjpeg62-turbo-dev`、`libpng12-0/-dev`、`libpng3`、`libidn11`、 `libcloog-ppl1`、`sysv-rc`。`libidn11-dev` 保留(仍可用),Debian 分支的 列表不动(不同发行版软件源不同)。 - 加了一段说明注释,避免后人重新加回 `libcurl4-gnutls-dev`。 **验证**:`bash -n` 通过;图片 1 的冲突不会再出现,图片 2 的 Skip 噪音也基本清零。 ## 补充:用户反馈同样的报错仍在出现 用户第二轮贴出**完全相同**的报错文本(含 `7.81.0-1ubuntu1.25` 版本号和 `No VM guests are running outdated hypervisor (qemu)` 这条云 VM 特征消息)。 排查结论: - 本地 `lib/check_sw.sh` 的 Ubuntu 列表(line 141)已经是干净的,没有 `libcurl4-gnutls-dev`。 - `backup/`、`runtime/` 都是空目录,全项目只有一份 `check_sw.sh`,没有其它副本。 - 报错来自**远程 Ubuntu 22.04 服务器**上跑的旧版脚本,本地修改还没同步过去。 ### 加防御层(`filter_pkglist()` 末尾,line 33-47) 为了即使旧版 pkgList 被部署上去也不炸,在 `filter_pkglist()` 末尾加了一段 互斥冲突消解:检测列表里同时有 `libcurl4-gnutls-dev` 和 `libcurl4-openssl-dev` 时,自动 sed 摘掉 gnutls dev 版,保留 openssl 版(PHP 各模块用的就是它), `libcurl3-gnutls` runtime 不动。 隔离测试通过:`libcurl3-gnutls` 保留 / `libcurl4-openssl-dev` 保留 / `libcurl4-gnutls-dev` 被移除。`bash -n` 语法通过。 ### 给用户的行动项 需要把更新后的 `lib/check_sw.sh` 重新同步到服务器(rsync/scp/git pull 等), 然后重跑安装。同步后两层防护都会生效: 1. Ubuntu 列表源头已无 `libcurl4-gnutls-dev`(也不会再有 Skip 噪音)。 2. 即便将来有人把互斥包加回去,`filter_pkglist` 会自动摘掉。 ## 第三轮:修复"卡很久"的黑屏问题 用户反馈"在运行到此处时会卡很久"。分析 `install_pkgs()` 的卡顿来源: 1. **批量 apt-get 遇冲突时静默求解**:apt 的依赖求解器遇到 `libcurl4-gnutls-dev` vs `libcurl4-openssl-dev` 互斥时,会花 30s~2min 尝试各种组合才放弃,期间 **无任何输出**,看起来像冻住。 2. **逐包回退循环全静默**:批量失败后循环 ~60 个包,每个 `apt-get install` 输出 全 redirect 到 `/dev/null`,用户看到几分钟黑屏。 ### 改动(`lib/check_sw.sh` 的 `install_pkgs()`,line 74-109) - 批量安装前再补一道冲突消解(防御纵深,防 filter_pkglist 被绕过或 pkgList 被中间修改)。 - 回退循环改成可见进度:每个包先 `printf` 包名(cyan,左对齐 40 列),再装, 装完打 `OK`(绿)/`FAILED`(红)。屏幕永不空白。 - 颜色变量复用 `lib/color.sh` 里已定义的 `CMSG/CSUCCESS/CFAILURE/CEND`。 - `cat -v` 验证对齐和转义正确,`bash -n` 语法通过。 ### 三层防护总览(都已在本地 `lib/check_sw.sh`) 1. `installDepsUbuntu` 的 pkgList 源头已删 `libcurl4-gnutls-dev` + 死链包。 2. `filter_pkglist()` 末尾自动消解 libcurl dev 互斥冲突。 3. `install_pkgs()` 开头再消解一次 + 回退循环可见进度。 **用户必须做的**:把 `lib/check_sw.sh` 同步到服务器再重跑。当前服务器上跑的 还是旧版,所以三轮都看到同样的报错。 ## 第四轮:审视整个项目,杜绝静默安装 用户要求"不要静默安装, 要有相关提示"。全项目扫描结论: ### 扫描发现 - 大多数 `make -j` / `./configure` **没有**被 `/dev/null` 吞输出(调用方 `run_install()` 或 `Install_X | tee` 会显示并写日志),只是**缺起始提示**。 - 唯一真正把安装输出吞掉的只有 `lib/check_sw.sh` 逐包回退的 `apt-get ... >/dev/null 2>&1`(第三轮已改成可见进度,本轮再改成完全不吞)。 - 下载环节 `lib/download.sh` 的 `Download_src()` 用 `wget -c`(无 `-q`),带进度,OK。 - 所有模块安装统一经 `lib/loader.sh` 的 `run_install()`,是加统一提示的**单点**。 ### 改动清单(7 个文件,`bash -n` 全通过) 1. `lib/loader.sh` `run_install()`:加 `>> Installing: ` 横幅(带 module 路径)。 改这一处覆盖全部 47 个模块 + 主流程。 2. `lib/check_sw.sh` `install_pkgs()` 回退循环:去掉 `>/dev/null`,每个包打印 "Installing X ..." → "X installed OK / install FAILED",apt 下载进度可见。 3. `lib/openssl.sh` `Install_openSSL()`:开头加 ">> Installing: openSSL ver"。 4. `modules/web/jemalloc.sh` `Install_Jemalloc()`:开头加 ">> Installing: jemalloc ver"。 5. `lib/check_sw.sh` `installDepsBySrc()`(ICU):开头加 ">> Installing: ICU ver"。 6. `core/environment.sh` `prepare_install()`:`${PM} -y install wget gcc curl python` 加提示 + `tee` 写日志(与上方 apt-get update 一致)。 7. `core/os/init_Ubuntu.sh` / `init_Debian.sh`:iptables 安装两处 `apt-get` 加提示。 ### 验证 - `grep 'apt-get.*install.*>/dev/null'` 全项目无匹配 → 再无静默 apt 安装。 - 编译步骤输出仍可经 `| tee` 看到并落日志。 - 收尾仍要提醒:所有改动需 `rsync`/`git pull` 到服务器才会生效。 ## ServerStack: apt-get 依赖安装改为静默(-y) + 写日志 **需求变更**:上一轮把逐包回退改成屏幕逐行可见;这轮用户改口要 「apt-get 静默安装(`-y`) + 输出安装日志」。 **改动**(`lib/check_sw.sh` 的 `install_pkgs()`): - 统一 `export DEBIAN_FRONTEND=noninteractive`,apt 完全非交互(不再卡 debconf 提示)。 - 批量安装:`apt-get --no-install-recommends -y install ${pkgList} >> runtime/install.log 2>&1`, 屏幕只打印 `Installing dependency packages (... -> install.log)` 起始行 + 成功后 `All dependency packages installed successfully.`。 - 逐包回退:每个包同样 `apt-get -y ... >> install.log 2>&1`,屏幕只保留一行 ` -> ... OK/FAILED`(printf 左对齐 40 列,OK 绿/FAILED 红)。 - 日志路径 `local _log="${oneinstack_dir:-.}/runtime/install.log"`,与外层 `installDepsUbuntu 2>&1 | tee` 同一文件;apt 详细输出直接追加、屏幕状态经 tee 落一份,无重复。 - 同步更新函数顶部注释,去掉旧的 /dev/null 描述。`bash -n` 通过。 **设计要点**:屏幕保留简洁状态(可读、不黑屏),完整 apt 输出进 install.log(可 grep 排障)。 ## 第五轮:修复安装摘要 URL 显示 http:/// (IPADDR 为空) **根因**:`IPADDR` 在 `core/environment.sh` 的 `prepare_install()` 由 `./lib/py/get_ipaddr.py` 获取, 该脚本靠 shebang `#!/usr/bin/env python` 启动;Ubuntu 22.04+ 只有 `python3` 无 `python` 命令, 且 base-tools 装的是不存在的 `python` 包、symlink 补救被 `~/.oneinstack` 判断限制, 导致脚本静默失败 → 标准输出空 → `IPADDR` 空 → 摘要 URL 变成 `http:///ocp.php` 等。 **修复**(仅 `environment.sh` + `bin/upgrade.sh`,`bash -n` 均通过): 1. 新增纯 shell 函数 `get_local_ipaddr()`(environment.sh),按 `ip route get src` → `hostname -I` → `ip -o -4 addr show scope global` → `127.0.0.1` 回退,**不依赖 python**。 2. `IPADDR=$(./lib/py/get_ipaddr.py)` 改为 `IPADDR=$(get_local_ipaddr)`。 3. base-tools 安装 `python` → `python3`;`/usr/bin/python` 软链改**无条件**创建, 让 get_public_ipaddr.py / get_ipaddr_state.py 也能跑。 4. `bin/upgrade.sh` 独立入口同样加无条件 `python` 软链兜底。 **验证**:mock 命令测 `get_local_ipaddr` 三种场景均正确(有路由取 src / 无路由回退 hostname / 全无回退 127.0.0.1)。 **注意**:改动仍在本地,需同步到服务器后重装/重跑 install 生效。 ## 第五轮补充:VPC/私网场景让摘要 URL 改用公网 IP 用户指出服务器是 **VPC 网络**。澄清:空 IP bug 与 VPC 无关(是 python 缺失);但 VPC 决定了 "显示哪个 IP"——`ip route get` 在 VPC 取到的是**私网 RFC1918 地址**,从外部经公网 IP/EIP 访问时 私网 URL 打不开。 **补充改动**(`core/environment.sh`): 1. 取 IP 顺序改为先 `PUBLIC_IPADDR=$(get_public_ipaddr.py)` 再 `IPADDR=$(get_local_ipaddr)`, 让本地检测函数能引用已解析的公网 IP。 2. `get_local_ipaddr()` 末尾加 case:`10.* / 172.16-31.* / 192.168.*` 私网段且 `PUBLIC_IPADDR` 非空时,改用公网 IP;公网取不到则安全保留私网 IP。 **验证**:mock 四场景全过(VPC私网+公网→公网 / VPC私网+公网失败→私网 / 经典VM公网→公网 / 纯内网192.168+失败→192.168)。