fix(desktop): restore linux launcher identity (#31709)
This commit is contained in:
parent
e1073e5d18
commit
2e0f88d0e3
3 changed files with 95 additions and 8 deletions
48
packages/desktop/electron-builder.config.test.ts
Normal file
48
packages/desktop/electron-builder.config.test.ts
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
import { expect, test } from "bun:test"
|
||||||
|
import type { Configuration } from "electron-builder"
|
||||||
|
|
||||||
|
const legacyDesktopEntry = "resources/linux/opencode-desktop.desktop"
|
||||||
|
|
||||||
|
const channels = [
|
||||||
|
{ channel: "dev", appId: "ai.opencode.desktop.dev" },
|
||||||
|
{ channel: "beta", appId: "ai.opencode.desktop.beta" },
|
||||||
|
{ channel: "prod", appId: "ai.opencode.desktop" },
|
||||||
|
] as const
|
||||||
|
|
||||||
|
for (const channel of channels) {
|
||||||
|
test(`uses one Linux desktop identity for ${channel.channel}`, async () => {
|
||||||
|
const previous = process.env.OPENCODE_CHANNEL
|
||||||
|
process.env.OPENCODE_CHANNEL = channel.channel
|
||||||
|
|
||||||
|
const module = await import(`./electron-builder.config.ts?channel=${channel.channel}`)
|
||||||
|
const config = module.default as Configuration
|
||||||
|
|
||||||
|
if (previous === undefined) delete process.env.OPENCODE_CHANNEL
|
||||||
|
else process.env.OPENCODE_CHANNEL = previous
|
||||||
|
|
||||||
|
expect(config.appId).toBe(channel.appId)
|
||||||
|
expect(config.extraMetadata?.desktopName).toBe(`${channel.appId}.desktop`)
|
||||||
|
expect(config.linux?.executableName).toBe(channel.appId)
|
||||||
|
expect(config.linux?.desktop?.entry?.StartupWMClass).toBe(channel.appId)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
test("keeps a hidden prod launcher for old Linux pins", async () => {
|
||||||
|
const previous = process.env.OPENCODE_CHANNEL
|
||||||
|
process.env.OPENCODE_CHANNEL = "prod"
|
||||||
|
|
||||||
|
const module = await import("./electron-builder.config.ts?compat=prod")
|
||||||
|
const config = module.default as Configuration
|
||||||
|
|
||||||
|
if (previous === undefined) delete process.env.OPENCODE_CHANNEL
|
||||||
|
else process.env.OPENCODE_CHANNEL = previous
|
||||||
|
|
||||||
|
expect(config.deb?.fpm?.[0]).toEndWith(`${legacyDesktopEntry}=/usr/share/applications/opencode-desktop.desktop`)
|
||||||
|
expect(config.rpm?.fpm?.[0]).toEndWith(`${legacyDesktopEntry}=/usr/share/applications/opencode-desktop.desktop`)
|
||||||
|
|
||||||
|
const desktop = await Bun.file(legacyDesktopEntry).text()
|
||||||
|
expect(desktop).toContain("Exec=/opt/OpenCode/ai.opencode.desktop %U")
|
||||||
|
expect(desktop).toContain("Icon=ai.opencode.desktop")
|
||||||
|
expect(desktop).toContain("StartupWMClass=ai.opencode.desktop")
|
||||||
|
expect(desktop).toContain("NoDisplay=true")
|
||||||
|
})
|
||||||
|
|
@ -6,8 +6,14 @@ import { promisify } from "node:util"
|
||||||
import type { Configuration } from "electron-builder"
|
import type { Configuration } from "electron-builder"
|
||||||
|
|
||||||
const execFileAsync = promisify(execFile)
|
const execFileAsync = promisify(execFile)
|
||||||
const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..")
|
const packageDir = path.dirname(fileURLToPath(import.meta.url))
|
||||||
|
const rootDir = path.resolve(packageDir, "../..")
|
||||||
const signScript = path.join(rootDir, "script", "sign-windows.ps1")
|
const signScript = path.join(rootDir, "script", "sign-windows.ps1")
|
||||||
|
// The Electron 42 packaging update briefly installed Linux launchers/icons under
|
||||||
|
// "opencode-desktop". Keep that hidden desktop entry around so existing GNOME/KDE
|
||||||
|
// pins still resolve after the canonical app id changes back to ai.opencode.desktop.
|
||||||
|
const legacyDesktopEntry = path.join(packageDir, "resources", "linux", "opencode-desktop.desktop")
|
||||||
|
const legacyDesktopEntryFpm = `${legacyDesktopEntry}=/usr/share/applications/opencode-desktop.desktop`
|
||||||
|
|
||||||
async function signWindows(configuration: { path: string }) {
|
async function signWindows(configuration: { path: string }) {
|
||||||
if (process.platform !== "win32") return
|
if (process.platform !== "win32") return
|
||||||
|
|
@ -26,12 +32,26 @@ const channel = (() => {
|
||||||
return "dev"
|
return "dev"
|
||||||
})()
|
})()
|
||||||
|
|
||||||
const getBase = (): Configuration => ({
|
const APP_IDS = {
|
||||||
|
dev: "ai.opencode.desktop.dev",
|
||||||
|
beta: "ai.opencode.desktop.beta",
|
||||||
|
prod: "ai.opencode.desktop",
|
||||||
|
} as const
|
||||||
|
|
||||||
|
const getBase = (appId: string): Configuration => ({
|
||||||
artifactName: "opencode-desktop-${os}-${arch}.${ext}",
|
artifactName: "opencode-desktop-${os}-${arch}.${ext}",
|
||||||
directories: {
|
directories: {
|
||||||
output: "dist",
|
output: "dist",
|
||||||
buildResources: "resources",
|
buildResources: "resources",
|
||||||
},
|
},
|
||||||
|
// Linux launchers are .desktop files, so this is the desktop file name,
|
||||||
|
// not just the app id. For prod, app id "ai.opencode.desktop" becomes
|
||||||
|
// "ai.opencode.desktop.desktop".
|
||||||
|
// https://developer.gnome.org/documentation/guidelines/maintainer/integrating.html
|
||||||
|
// https://www.electron.build/docs/linux/
|
||||||
|
extraMetadata: {
|
||||||
|
desktopName: `${appId}.desktop`,
|
||||||
|
},
|
||||||
files: ["out/**/*", "resources/**/*"],
|
files: ["out/**/*", "resources/**/*"],
|
||||||
extraResources: [
|
extraResources: [
|
||||||
{
|
{
|
||||||
|
|
@ -74,19 +94,27 @@ const getBase = (): Configuration => ({
|
||||||
linux: {
|
linux: {
|
||||||
icon: `resources/icons`,
|
icon: `resources/icons`,
|
||||||
category: "Development",
|
category: "Development",
|
||||||
executableName: "opencode-desktop",
|
executableName: appId,
|
||||||
|
desktop: {
|
||||||
|
entry: {
|
||||||
|
// Match the installed .desktop file and hicolor icon basename so
|
||||||
|
// Linux shells can associate the running Electron window with its launcher.
|
||||||
|
StartupWMClass: appId,
|
||||||
|
},
|
||||||
|
},
|
||||||
target: ["AppImage", "deb", "rpm"],
|
target: ["AppImage", "deb", "rpm"],
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
function getConfig() {
|
function getConfig() {
|
||||||
const base = getBase()
|
const appId = APP_IDS[channel]
|
||||||
|
const base = getBase(appId)
|
||||||
|
|
||||||
switch (channel) {
|
switch (channel) {
|
||||||
case "dev": {
|
case "dev": {
|
||||||
return {
|
return {
|
||||||
...base,
|
...base,
|
||||||
appId: "ai.opencode.desktop.dev",
|
appId,
|
||||||
productName: "OpenCode Dev",
|
productName: "OpenCode Dev",
|
||||||
rpm: { packageName: "opencode-dev" },
|
rpm: { packageName: "opencode-dev" },
|
||||||
}
|
}
|
||||||
|
|
@ -94,7 +122,7 @@ function getConfig() {
|
||||||
case "beta": {
|
case "beta": {
|
||||||
return {
|
return {
|
||||||
...base,
|
...base,
|
||||||
appId: "ai.opencode.desktop.beta",
|
appId,
|
||||||
productName: "OpenCode Beta",
|
productName: "OpenCode Beta",
|
||||||
protocols: { name: "OpenCode Beta", schemes: ["opencode"] },
|
protocols: { name: "OpenCode Beta", schemes: ["opencode"] },
|
||||||
publish: { provider: "github", owner: "anomalyco", repo: "opencode-beta", channel: "latest" },
|
publish: { provider: "github", owner: "anomalyco", repo: "opencode-beta", channel: "latest" },
|
||||||
|
|
@ -104,11 +132,12 @@ function getConfig() {
|
||||||
case "prod": {
|
case "prod": {
|
||||||
return {
|
return {
|
||||||
...base,
|
...base,
|
||||||
appId: "ai.opencode.desktop",
|
appId,
|
||||||
productName: "OpenCode",
|
productName: "OpenCode",
|
||||||
protocols: { name: "OpenCode", schemes: ["opencode"] },
|
protocols: { name: "OpenCode", schemes: ["opencode"] },
|
||||||
publish: { provider: "github", owner: "anomalyco", repo: "opencode", channel: "latest" },
|
publish: { provider: "github", owner: "anomalyco", repo: "opencode", channel: "latest" },
|
||||||
rpm: { packageName: "opencode" },
|
deb: { fpm: [legacyDesktopEntryFpm] },
|
||||||
|
rpm: { packageName: "opencode", fpm: [legacyDesktopEntryFpm] },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
10
packages/desktop/resources/linux/opencode-desktop.desktop
Normal file
10
packages/desktop/resources/linux/opencode-desktop.desktop
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
[Desktop Entry]
|
||||||
|
Name=OpenCode
|
||||||
|
Exec=/opt/OpenCode/ai.opencode.desktop %U
|
||||||
|
Terminal=false
|
||||||
|
Type=Application
|
||||||
|
Icon=ai.opencode.desktop
|
||||||
|
StartupWMClass=ai.opencode.desktop
|
||||||
|
NoDisplay=true
|
||||||
|
Comment=Open source AI coding agent
|
||||||
|
Categories=Development;
|
||||||
Loading…
Reference in a new issue