feat(update-center): 统一使用下载包文件进行安装

- 移除 buildLegacySparkUpgradeCommand,所有更新现在需要先下载 deb 包
- 为 APTSS 添加元数据查询功能
- 优化 aria2c 下载参数,使用 metalink URL
- 版本号更新至 5.0.0beta4
This commit is contained in:
2026-04-12 16:44:55 +08:00
parent fa2689c753
commit 6fcfa438d9
4 changed files with 114 additions and 74 deletions

View File

@@ -33,13 +33,22 @@ export const runAria2Download = async ({
const filePath = join(downloadDir, item.fileName);
// Use .metalink URL for download (same as Qt version)
const metalinkUrl = `${item.downloadUrl}.metalink`;
await new Promise<void>((resolve, reject) => {
const child = spawn("aria2c", [
"--dir",
downloadDir,
"--out",
item.fileName,
item.downloadUrl,
"--enable-rpc=false",
"--console-log-level=warn",
"--summary-interval=1",
"--allow-overwrite=true",
"--connect-timeout=30",
"--max-tries=3",
metalinkUrl,
]);
const abortDownload = () => {

View File

@@ -66,6 +66,14 @@ const getApmPrintUrisCommand = (pkgname: string) => ({
],
});
const getAptssPrintUrisCommand = (pkgname: string) => ({
command: "bash",
args: [
"-lc",
`/usr/bin/apt download ${pkgname} --print-uris -c /opt/durapps/spark-store/bin/apt-fast-conf/aptss-apt.conf -o Dir::Etc::sourcelist=/opt/durapps/spark-store/bin/apt-fast-conf/sources.list.d/aptss.list -o Dir::Etc::sourceparts=/dev/null`,
],
});
const runCommandCapture: UpdateCenterCommandRunner = async (
command,
args,
@@ -140,6 +148,42 @@ const loadApmItemMetadata = async (
};
};
const loadAptssItemMetadata = async (
item: UpdateCenterItem,
runCommand: UpdateCenterCommandRunner,
): Promise<
| { item: UpdateCenterItem; warning?: undefined }
| { item: null; warning: string }
> => {
const printUrisCommand = getAptssPrintUrisCommand(item.pkgname);
const metadataResult = await runCommand(
printUrisCommand.command,
printUrisCommand.args,
);
const commandError = getCommandError(
`aptss metadata query for ${item.pkgname}`,
metadataResult,
);
if (commandError) {
return { item: null, warning: commandError };
}
const metadata = parsePrintUrisOutput(metadataResult.stdout);
if (!metadata) {
return {
item: null,
warning: `aptss metadata query for ${item.pkgname} returned no package metadata`,
};
}
return {
item: {
...item,
...metadata,
},
};
};
const enrichApmItems = async (
items: UpdateCenterItem[],
runCommand: UpdateCenterCommandRunner,
@@ -156,6 +200,22 @@ const enrichApmItems = async (
};
};
const enrichAptssItems = async (
items: UpdateCenterItem[],
runCommand: UpdateCenterCommandRunner,
): Promise<UpdateCenterLoadItemsResult> => {
const results = await Promise.all(
items.map((item) => loadAptssItemMetadata(item, runCommand)),
);
return {
items: results.flatMap((result) => (result.item ? [result.item] : [])),
warnings: results.flatMap((result) =>
result.warning ? [result.warning] : [],
),
};
};
const getStoreArch = (
item: Pick<UpdateCenterItem, "source" | "arch">,
): string => {
@@ -299,18 +359,22 @@ export const loadUpdateCenterItems = async (
enrichItemCategories(aptssItems),
enrichItemCategories(apmItems),
]);
const enrichedApmItems = await enrichApmItems(
categorizedApmItems,
runCommand,
);
const [enrichedAptssItems, enrichedApmItems] = await Promise.all([
enrichAptssItems(categorizedAptssItems, runCommand),
enrichApmItems(categorizedApmItems, runCommand),
]);
return {
items: mergeUpdateSources(
enrichItemIcons(categorizedAptssItems),
enrichItemIcons(enrichedAptssItems.items),
enrichItemIcons(enrichedApmItems.items),
installedSources,
),
warnings: [...warnings, ...enrichedApmItems.warnings],
warnings: [
...warnings,
...enrichedAptssItems.warnings,
...enrichedApmItems.warnings,
],
};
};

View File

@@ -119,29 +119,8 @@ const buildPrivilegedCommand = (
};
};
export const buildLegacySparkUpgradeCommand = (
pkgname: string,
superUserCmd = "",
): UpdateCommand => {
if (superUserCmd) {
return {
execCommand: superUserCmd,
execParams: [
SHELL_CALLER_PATH,
"aptss",
"install",
"-y",
pkgname,
"--only-upgrade",
],
};
}
return {
execCommand: SHELL_CALLER_PATH,
execParams: ["aptss", "install", "-y", pkgname, "--only-upgrade"],
};
};
// Removed buildLegacySparkUpgradeCommand - all updates now require downloading the deb package first
// to avoid aptss install popup. Use ssinstall with downloaded deb file instead.
export const installUpdateItem = async ({
item,
@@ -150,11 +129,13 @@ export const installUpdateItem = async ({
onLog,
signal,
}: InstallUpdateItemOptions): Promise<void> => {
if (item.source === "apm" && !filePath) {
throw new Error("APM update task requires downloaded package metadata");
if (!filePath) {
throw new Error(
`Update task for ${item.pkgname} requires downloaded package file`,
);
}
if (item.source === "apm" && filePath) {
if (item.source === "apm") {
const installCommand = buildPrivilegedCommand(
SHELL_CALLER_PATH,
["apm", "ssinstall", filePath],
@@ -169,26 +150,18 @@ export const installUpdateItem = async ({
return;
}
if (filePath) {
const installCommand = buildPrivilegedCommand(
SSINSTALL_PATH,
[filePath, "--delete-after-install"],
superUserCmd,
);
await runCommand(
installCommand.execCommand,
installCommand.execParams,
onLog,
signal,
);
return;
}
const command = buildLegacySparkUpgradeCommand(
item.pkgname,
superUserCmd ?? "",
// APTSS (Spark Store) packages use ssinstall
const installCommand = buildPrivilegedCommand(
SSINSTALL_PATH,
[filePath, "--delete-after-install", "--no-create-desktop-entry", "--native"],
superUserCmd,
);
await runCommand(
installCommand.execCommand,
installCommand.execParams,
onLog,
signal,
);
await runCommand(command.execCommand, command.execParams, onLog, signal);
};
export const createTaskRunner = (
@@ -246,30 +219,24 @@ export const createTaskRunner = (
};
try {
let filePath: string | undefined;
if (
task.item.source === "apm" &&
(!task.item.downloadUrl || !task.item.fileName)
) {
// All updates require download metadata
if (!task.item.downloadUrl || !task.item.fileName) {
throw new Error(
"APM update task requires downloaded package metadata",
`Update task for ${task.item.pkgname} requires download metadata (URL and filename)`,
);
}
if (task.item.downloadUrl && task.item.fileName) {
queue.markActiveTask(task.id, "downloading");
const result = await runDownload({
item: task.item,
task,
onLog,
signal: activeAbortController.signal,
onProgress: (progress) => {
queue.updateTaskProgress(task.id, progress);
},
});
filePath = result.filePath;
}
queue.markActiveTask(task.id, "downloading");
const result = await runDownload({
item: task.item,
task,
onLog,
signal: activeAbortController.signal,
onProgress: (progress) => {
queue.updateTaskProgress(task.id, progress);
},
});
const filePath = result.filePath;
queue.markActiveTask(task.id, "installing");
await installItem({