feat(update-center): 支持被系统锁定(hold)包的强制安装与更新中心体验优化

- 标记 apt-mark hold 的 spark(aptss) 源包,默认禁用勾选并提示开启强制安装
- 更新中心发起更新时透传 forceHeld,后端 Spark 本地 deb 走 shell-caller
  force-ssinstall 分支(单次免密 pkexec:unhold→ssinstall→hold),避免二次提权
- 无可选项时禁用全选并提示"无可更新项";更新选中后从列表中移除已启动项
- DownloadDetail 紧凑布局避免整页滚动;Toolbar 全选与更新选中按钮同行
- 继承 PKGNAME_PATTERN 与 metalinkUrl 白名单(https+*.spark-app.store+拒..)纵深防御
- apm 源无 hold 概念,forceHeld 在 apm 分支忽略(正确行为)
This commit is contained in:
xiyidaiwa
2026-08-13 16:42:21 +08:00
parent b5c5ab04b5
commit 0f62a7e36a
17 changed files with 893 additions and 105 deletions
+41 -1
View File
@@ -502,8 +502,12 @@ export const loadUpdateCenterItems = async (
),
);
// 标记被系统锁定(apt-mark hold)的包:这类包被 apt 拒绝升级(除非 --allow-change-held-packages)。
// 更新中心据此默认禁用其勾选,并提示用户单独开启「强制安装」。
const heldItems = await markHeldPackages(mergedItems, runCommand);
return {
items: mergedItems,
items: heldItems,
warnings: [
...warnings,
...enrichedAptssItems.warnings,
@@ -512,6 +516,42 @@ export const loadUpdateCenterItems = async (
};
};
// 通过 `apt-mark showhold` 获取系统锁定的包集合,给可升级项中匹配者打 held 标记。
// 仅 sparkaptss/deb)源受 apt hold 影响;apm 源不经过 apt,无需标记。
const markHeldPackages = async (
items: UpdateCenterItem[],
runCommand: UpdateCenterCommandRunner,
): Promise<UpdateCenterItem[]> => {
const sparkItems = items.filter((item) => item.source === "aptss");
if (sparkItems.length === 0) {
return items;
}
const result = await runCommand("apt-mark", ["showhold"]);
if (result.code !== 0) {
// 查询失败不阻断更新列表,仅跳过 hold 标记
console.warn(`[UpdateCenter] apt-mark showhold failed: ${result.stderr}`);
return items;
}
const heldSet = new Set(
result.stdout
.split("\n")
.map((line) => line.trim())
.filter((line) => line.length > 0),
);
if (heldSet.size === 0) {
return items;
}
return items.map((item) =>
item.source === "aptss" && heldSet.has(item.pkgname)
? { ...item, held: true }
: item,
);
};
// 子进程超时(毫秒):网络慢/镜像源卡死时,避免命令永久挂起
const SYSTEM_UPDATE_COMMAND_TIMEOUT_MS = 90_000;