diff --git a/electron/main/backend/update-center/index.ts b/electron/main/backend/update-center/index.ts index c8280c96..659205f0 100644 --- a/electron/main/backend/update-center/index.ts +++ b/electron/main/backend/update-center/index.ts @@ -33,14 +33,20 @@ export interface UpdateCenterLoadItemsResult { warnings: string[]; } -type StoreCategoryMap = Map; +interface RemoteAppMetadata { + category: string; + name?: string; +} + +type StoreAppMetadataMap = Map; interface RemoteCategoryAppEntry { + Name?: string; Pkgname?: string; } const REMOTE_STORE_BASE_URL = "https://erotica.spark-app.store"; -const categoryCache = new Map>(); +const categoryCache = new Map>(); const APTSS_LIST_UPGRADABLE_COMMAND = { command: "bash", @@ -157,16 +163,22 @@ const loadAptssItemMetadata = async ( > => { console.log(`[DEBUG] Loading APTSS metadata for ${item.pkgname}`); const printUrisCommand = getAptssPrintUrisCommand(item.pkgname); - console.log(`[DEBUG] APTSS command: ${printUrisCommand.command} ${printUrisCommand.args.join(' ')}`); - + console.log( + `[DEBUG] APTSS command: ${printUrisCommand.command} ${printUrisCommand.args.join(" ")}`, + ); + const metadataResult = await runCommand( printUrisCommand.command, printUrisCommand.args, ); console.log(`[DEBUG] APTSS metadata result code: ${metadataResult.code}`); - console.log(`[DEBUG] APTSS metadata stdout: ${metadataResult.stdout.substring(0, 500)}`); - console.log(`[DEBUG] APTSS metadata stderr: ${metadataResult.stderr.substring(0, 500)}`); - + console.log( + `[DEBUG] APTSS metadata stdout: ${metadataResult.stdout.substring(0, 500)}`, + ); + console.log( + `[DEBUG] APTSS metadata stderr: ${metadataResult.stderr.substring(0, 500)}`, + ); + const commandError = getCommandError( `aptss metadata query for ${item.pkgname}`, metadataResult, @@ -178,7 +190,7 @@ const loadAptssItemMetadata = async ( const metadata = parsePrintUrisOutput(metadataResult.stdout); console.log(`[DEBUG] APTSS parsed metadata:`, metadata); - + if (!metadata) { return { item: null, @@ -252,7 +264,7 @@ const loadJson = async (url: string): Promise => { const loadStoreCategoryMap = async ( storeArch: string, -): Promise => { +): Promise => { const categories = await loadJson>( `${REMOTE_STORE_BASE_URL}/${storeArch}/categories.json`, ); @@ -266,7 +278,7 @@ const loadStoreCategoryMap = async ( }), ); - const categoryMap: StoreCategoryMap = new Map(); + const categoryMap: StoreAppMetadataMap = new Map(); for (const entry of categoryEntries) { if (entry.status !== "fulfilled") { continue; @@ -274,7 +286,10 @@ const loadStoreCategoryMap = async ( for (const app of entry.value.apps) { if (app.Pkgname && !categoryMap.has(app.Pkgname)) { - categoryMap.set(app.Pkgname, entry.value.category); + categoryMap.set(app.Pkgname, { + category: entry.value.category, + name: app.Name, + }); } } } @@ -282,7 +297,9 @@ const loadStoreCategoryMap = async ( return categoryMap; }; -const getStoreCategoryMap = (storeArch: string): Promise => { +const getStoreCategoryMap = ( + storeArch: string, +): Promise => { const cached = categoryCache.get(storeArch); if (cached) { return cached; @@ -311,8 +328,14 @@ const enrichItemCategories = async ( } const categoryMap = await getStoreCategoryMap(storeArch); - const category = categoryMap.get(item.pkgname); - return category ? { ...item, category } : item; + const metadata = categoryMap.get(item.pkgname); + return metadata + ? { + ...item, + category: metadata.category, + ...(metadata.name ? { name: metadata.name } : {}), + } + : item; }), ); }; diff --git a/electron/main/backend/update-center/service.ts b/electron/main/backend/update-center/service.ts index 1856aadd..ef3abb0f 100644 --- a/electron/main/backend/update-center/service.ts +++ b/electron/main/backend/update-center/service.ts @@ -91,7 +91,7 @@ const toState = ( items: snapshot.items.map((item) => ({ taskKey: getTaskKey(item), packageName: item.pkgname, - displayName: item.pkgname, + displayName: item.name || item.pkgname, currentVersion: item.currentVersion, newVersion: item.nextVersion, source: item.source, @@ -191,9 +191,7 @@ export const createUpdateCenterService = ( async start(taskKeys) { const snapshot = queue.getSnapshot(); const selectedItems = snapshot.items.filter( - (item) => - taskKeys.includes(getTaskKey(item)) && - !item.ignored, + (item) => taskKeys.includes(getTaskKey(item)) && !item.ignored, ); if (selectedItems.length === 0) { @@ -235,7 +233,9 @@ export const createUpdateCenterService = ( webContents.send("queue-install", JSON.stringify(installTaskData)); // 从更新中心的 items 中移除该应用(不再显示在更新列表中) - currentItems = currentItems.filter((i) => getTaskKey(i) !== getTaskKey(item)); + currentItems = currentItems.filter( + (i) => getTaskKey(i) !== getTaskKey(item), + ); } // 更新队列中的 items diff --git a/electron/main/backend/update-center/types.ts b/electron/main/backend/update-center/types.ts index e442a85c..7692befa 100644 --- a/electron/main/backend/update-center/types.ts +++ b/electron/main/backend/update-center/types.ts @@ -7,6 +7,7 @@ export interface InstalledSourceState { export interface UpdateCenterItem { pkgname: string; + name?: string; source: UpdateSource; currentVersion: string; nextVersion: string; diff --git a/src/__tests__/unit/update-center/load-items.test.ts b/src/__tests__/unit/update-center/load-items.test.ts index c2876304..66a1103b 100644 --- a/src/__tests__/unit/update-center/load-items.test.ts +++ b/src/__tests__/unit/update-center/load-items.test.ts @@ -19,6 +19,12 @@ const DPKG_QUERY_INSTALLED_KEY = const APM_PRINT_URIS_KEY = "bash -lc amber-pm-debug /usr/bin/apt -c /opt/durapps/spark-store/bin/apt-fast-conf/aptss-apt.conf download spark-weather --print-uris"; +const APTSS_WEATHER_PRINT_URIS_KEY = + "bash -lc /usr/bin/apt download spark-weather --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 APTSS_NOTES_PRINT_URIS_KEY = + "bash -lc /usr/bin/apt download spark-notes --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 loadUpdateCenterModule = async ( remoteStore: Record, ) => { @@ -141,19 +147,28 @@ describe("update-center load items", () => { stderr: "", }, ], + [ + APTSS_WEATHER_PRINT_URIS_KEY, + { + code: 0, + stdout: + "'https://example.invalid/spark-weather_2.0.0_amd64.deb' spark-weather_2.0.0_amd64.deb 123456 SHA512:deadbeef", + stderr: "", + }, + ], ]); const { loadUpdateCenterItems } = await loadUpdateCenterModule({ "https://erotica.spark-app.store/amd64-store/categories.json": { tools: { zh: "Tools" }, }, "https://erotica.spark-app.store/amd64-store/tools/applist.json": [ - { Pkgname: "spark-weather" }, + { Name: "Spark Weather", Pkgname: "spark-weather" }, ], "https://erotica.spark-app.store/amd64-apm/categories.json": { tools: { zh: "Tools" }, }, "https://erotica.spark-app.store/amd64-apm/tools/applist.json": [ - { Pkgname: "spark-weather" }, + { Name: "Spark Weather", Pkgname: "spark-weather" }, ], }); @@ -175,6 +190,7 @@ describe("update-center load items", () => { nextVersion: "3.0.0", arch: "amd64", category: "tools", + name: "Spark Weather", remoteIcon: "https://erotica.spark-app.store/amd64-apm/tools/spark-weather/icon.png", downloadUrl: "https://example.invalid/spark-weather_3.0.0_amd64.deb", @@ -194,7 +210,7 @@ describe("update-center load items", () => { office: { zh: "Office" }, }, "https://erotica.spark-app.store/amd64-store/office/applist.json": [ - { Pkgname: "spark-notes" }, + { Name: "Spark Notes", Pkgname: "spark-notes" }, ], }); @@ -217,6 +233,15 @@ describe("update-center load items", () => { }; } + if (key === APTSS_NOTES_PRINT_URIS_KEY) { + return { + code: 0, + stdout: + "'https://example.invalid/spark-notes_2.0.0_amd64.deb' spark-notes_2.0.0_amd64.deb 654321 SHA512:beadfeed", + stderr: "", + }; + } + if (key === "apm list --upgradable" || key === "apm list --installed") { return { code: 127, @@ -236,8 +261,13 @@ describe("update-center load items", () => { nextVersion: "2.0.0", arch: "amd64", category: "office", + name: "Spark Notes", remoteIcon: "https://erotica.spark-app.store/amd64-store/office/spark-notes/icon.png", + downloadUrl: "https://example.invalid/spark-notes_2.0.0_amd64.deb", + fileName: "spark-notes_2.0.0_amd64.deb", + size: 654321, + sha512: "beadfeed", }, ]); expect(result.warnings).toEqual([ @@ -289,6 +319,7 @@ describe("update-center load items", () => { currentVersion: "1.0.0", nextVersion: "2.0.0", arch: "amd64", + name: "Spark Notes", }, ]); @@ -298,7 +329,7 @@ describe("update-center load items", () => { }; remoteStore[ "https://erotica.spark-app.store/amd64-store/office/applist.json" - ] = [{ Pkgname: "spark-notes" }]; + ] = [{ Name: "Spark Notes", Pkgname: "spark-notes" }]; const secondResult = await loadUpdateCenterItems(runCommand); @@ -310,6 +341,7 @@ describe("update-center load items", () => { nextVersion: "2.0.0", arch: "amd64", category: "office", + name: "Spark Notes", remoteIcon: "https://erotica.spark-app.store/amd64-store/office/spark-notes/icon.png", }, @@ -323,7 +355,7 @@ describe("update-center load items", () => { tools: { zh: "Tools" }, }, "https://erotica.spark-app.store/amd64-store/office/applist.json": [ - { Pkgname: "spark-notes" }, + { Name: "Spark Notes", Pkgname: "spark-notes" }, ], }); @@ -346,6 +378,15 @@ describe("update-center load items", () => { }; } + if (key === APTSS_NOTES_PRINT_URIS_KEY) { + return { + code: 0, + stdout: + "'https://example.invalid/spark-notes_2.0.0_amd64.deb' spark-notes_2.0.0_amd64.deb 654321 SHA512:beadfeed", + stderr: "", + }; + } + if (key === "apm list --upgradable" || key === "apm list --installed") { return { code: 127, @@ -365,6 +406,7 @@ describe("update-center load items", () => { nextVersion: "2.0.0", arch: "amd64", category: "office", + name: "Spark Notes", remoteIcon: "https://erotica.spark-app.store/amd64-store/office/spark-notes/icon.png", }, diff --git a/src/__tests__/unit/update-center/registerUpdateCenter.test.ts b/src/__tests__/unit/update-center/registerUpdateCenter.test.ts index f932ec8b..ef805cb0 100644 --- a/src/__tests__/unit/update-center/registerUpdateCenter.test.ts +++ b/src/__tests__/unit/update-center/registerUpdateCenter.test.ts @@ -262,6 +262,27 @@ describe("update-center/ipc", () => { await startPromise; }); + it("service item snapshots prefer resolved app names over package names", async () => { + const service = createUpdateCenterService({ + loadItems: async () => [ + { + ...createItem(), + name: "Spark Weather", + }, + ], + }); + + const snapshot = await service.refresh(); + + expect(snapshot.items).toMatchObject([ + { + taskKey: "aptss:spark-weather", + packageName: "spark-weather", + displayName: "Spark Weather", + }, + ]); + }); + it("concurrent start calls still serialize through one processing pipeline", async () => { const startedTaskIds: number[] = []; const releases: Array<() => void> = [];