electron-builder.config.ts 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. import { execFile } from "node:child_process"
  2. import path from "node:path"
  3. import { fileURLToPath } from "node:url"
  4. import { promisify } from "node:util"
  5. import type { Configuration } from "electron-builder"
  6. const execFileAsync = promisify(execFile)
  7. const packageDir = path.dirname(fileURLToPath(import.meta.url))
  8. const rootDir = path.resolve(packageDir, "../..")
  9. const signScript = path.join(rootDir, "script", "sign-windows.ps1")
  10. // The Electron 42 packaging update briefly installed Linux launchers/icons under
  11. // "opencode-desktop". Keep that hidden desktop entry around so existing GNOME/KDE
  12. // pins still resolve after the canonical app id changes back to ai.opencode.desktop.
  13. const legacyDesktopEntry = path.join(packageDir, "resources", "linux", "opencode-desktop.desktop")
  14. const legacyDesktopEntryFpm = `${legacyDesktopEntry}=/usr/share/applications/opencode-desktop.desktop`
  15. async function signWindows(configuration: { path: string }) {
  16. if (process.platform !== "win32") return
  17. if (process.env.GITHUB_ACTIONS !== "true") return
  18. await execFileAsync(
  19. "pwsh",
  20. ["-NoLogo", "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", signScript, configuration.path],
  21. { cwd: rootDir },
  22. )
  23. }
  24. const channel = (() => {
  25. const raw = process.env.OPENCODE_CHANNEL
  26. if (raw === "dev" || raw === "beta" || raw === "prod") return raw
  27. return "dev"
  28. })()
  29. const APP_IDS = {
  30. dev: "ai.opencode.desktop.dev",
  31. beta: "ai.opencode.desktop.beta",
  32. prod: "ai.opencode.desktop",
  33. } as const
  34. const getBase = (appId: string): Configuration => ({
  35. artifactName: "opencode-desktop-${os}-${arch}.${ext}",
  36. directories: {
  37. output: "dist",
  38. buildResources: "resources",
  39. },
  40. // Linux launchers are .desktop files, so this is the desktop file name,
  41. // not just the app id. For prod, app id "ai.opencode.desktop" becomes
  42. // "ai.opencode.desktop.desktop".
  43. // https://developer.gnome.org/documentation/guidelines/maintainer/integrating.html
  44. // https://www.electron.build/docs/linux/
  45. extraMetadata: {
  46. desktopName: `${appId}.desktop`,
  47. },
  48. files: ["out/**/*", "resources/**/*"],
  49. extraResources: [
  50. {
  51. from: "native/",
  52. to: "native/",
  53. filter: ["index.js", "index.d.ts", "build/Release/mac_window.node", "swift-build/**"],
  54. },
  55. ],
  56. mac: {
  57. category: "public.app-category.developer-tools",
  58. icon: `resources/icons/icon.icns`,
  59. hardenedRuntime: true,
  60. gatekeeperAssess: false,
  61. entitlements: "resources/entitlements.plist",
  62. entitlementsInherit: "resources/entitlements.plist",
  63. notarize: true,
  64. target: ["dmg", "zip"],
  65. },
  66. dmg: {
  67. sign: true,
  68. },
  69. protocols: {
  70. name: "OpenCode",
  71. schemes: ["opencode"],
  72. },
  73. win: {
  74. icon: `resources/icons/icon.ico`,
  75. signtoolOptions: {
  76. sign: signWindows,
  77. },
  78. target: ["nsis"],
  79. verifyUpdateCodeSignature: false,
  80. },
  81. nsis: {
  82. oneClick: true,
  83. perMachine: false,
  84. installerIcon: `resources/icons/icon.ico`,
  85. installerHeaderIcon: `resources/icons/icon.ico`,
  86. },
  87. linux: {
  88. icon: `resources/icons`,
  89. category: "Development",
  90. executableName: appId,
  91. desktop: {
  92. entry: {
  93. // Match the installed .desktop file and hicolor icon basename so
  94. // Linux shells can associate the running Electron window with its launcher.
  95. StartupWMClass: appId,
  96. },
  97. },
  98. target: ["AppImage", "deb", "rpm"],
  99. },
  100. })
  101. function getConfig() {
  102. const appId = APP_IDS[channel]
  103. const base = getBase(appId)
  104. switch (channel) {
  105. case "dev": {
  106. return {
  107. ...base,
  108. appId,
  109. productName: "OpenCode Dev",
  110. rpm: { packageName: "opencode-dev" },
  111. }
  112. }
  113. case "beta": {
  114. return {
  115. ...base,
  116. appId,
  117. productName: "OpenCode Beta",
  118. protocols: { name: "OpenCode Beta", schemes: ["opencode"] },
  119. publish: { provider: "github", owner: "anomalyco", repo: "opencode-beta", channel: "latest" },
  120. rpm: { packageName: "opencode-beta" },
  121. }
  122. }
  123. case "prod": {
  124. return {
  125. ...base,
  126. appId,
  127. productName: "OpenCode",
  128. protocols: { name: "OpenCode", schemes: ["opencode"] },
  129. publish: { provider: "github", owner: "anomalyco", repo: "opencode", channel: "latest" },
  130. deb: { fpm: [legacyDesktopEntryFpm] },
  131. rpm: { packageName: "opencode", fpm: [legacyDesktopEntryFpm] },
  132. }
  133. }
  134. }
  135. }
  136. export default getConfig()