electron-builder.config.mjs 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. import { join } from 'node:path'
  2. import { fileURLToPath } from 'node:url'
  3. import {
  4. resolveDesktopAppId,
  5. resolveMacOSNotarizationEnvironment,
  6. resolveMacOSSigningEnvironment,
  7. } from './scripts/desktop-release-environment.mjs'
  8. import { notarizeMacOSDiskImageArtifact } from './scripts/notarize-macos-disk-images.mjs'
  9. import { verifyMacOSSignatureAfterSign } from './scripts/verify-macos-signature.mjs'
  10. import {
  11. createWindowsTokenSigner,
  12. installWindowsNsisBootstrapSigner,
  13. } from './scripts/windows-sign.mjs'
  14. import { resolveDesktopAutoUpdateConfig } from './scripts/desktop-auto-update-environment.mjs'
  15. import { desktopTargetBuildPaths, resolveDesktopBuildTarget } from './scripts/desktop-build-paths.mjs'
  16. /**
  17. * Create electron-builder configuration from one release environment.
  18. * @param {NodeJS.ProcessEnv} env - Packaging environment.
  19. * @param {NodeJS.Platform} hostPlatform - Build-host platform used when no explicit target is present.
  20. * @param {string} hostArch - Build-host architecture used when no explicit target is present.
  21. * @returns {object} electron-builder configuration.
  22. */
  23. export function createElectronBuilderConfig(
  24. env = process.env,
  25. hostPlatform = process.platform,
  26. hostArch = process.arch,
  27. ) {
  28. const appId = resolveDesktopAppId(env)
  29. const targetPlatform = env.DSH_DESKTOP_TARGET_PLATFORM
  30. const resolvedPlatform = targetPlatform ?? hostPlatform
  31. const resolvedArch = env.DSH_DESKTOP_TARGET_ARCH ?? hostArch
  32. if (env.DSH_DESKTOP_UNSIGNED !== undefined && !['0', '1'].includes(env.DSH_DESKTOP_UNSIGNED)) {
  33. throw new Error('desktop package: DSH_DESKTOP_UNSIGNED must be 0 or 1')
  34. }
  35. const unsigned = env.DSH_DESKTOP_UNSIGNED === '1'
  36. if (unsigned && resolvedPlatform !== 'win32') throw new Error('desktop package: unsigned builds require Windows')
  37. const packagesMacOS = targetPlatform === 'darwin' || (targetPlatform === undefined && hostPlatform === 'darwin')
  38. const packagesWindows = targetPlatform === 'win32'
  39. const macOSSigning = packagesMacOS ? resolveMacOSSigningEnvironment(env) : undefined
  40. if (packagesMacOS) resolveMacOSNotarizationEnvironment(env)
  41. const windowsSigner = packagesWindows && !unsigned
  42. ? createWindowsTokenSigner({
  43. certificateFile: env.DSH_DESKTOP_WINDOWS_CER_FILE,
  44. signTool: env.DSH_DESKTOP_WINDOWS_SIGNTOOL,
  45. tokenPin: env.DSH_DESKTOP_WINDOWS_TOKEN_PIN,
  46. keyContainer: env.DSH_DESKTOP_WINDOWS_KEY_CONTAINER,
  47. })
  48. : undefined
  49. if (windowsSigner !== undefined) {
  50. installWindowsNsisBootstrapSigner({ sign: windowsSigner })
  51. }
  52. const update = unsigned ? undefined : resolveDesktopAutoUpdateConfig(env, resolvedPlatform, resolvedArch)
  53. const buildPaths = desktopTargetBuildPaths(resolveDesktopBuildTarget(env, hostPlatform, hostArch))
  54. return {
  55. appId,
  56. productName: 'DeepSeek Harness',
  57. artifactName: 'deepseek-harness-${version}-${os}-${arch}.${ext}',
  58. directories: { output: unsigned ? join(buildPaths.root, 'unsigned-artifacts') : buildPaths.artifacts },
  59. asar: true,
  60. files: [
  61. 'lib/*.js',
  62. 'lib/*.cjs',
  63. 'renderer/**/*',
  64. 'package.json',
  65. ],
  66. extraResources: [
  67. { from: buildPaths.runtime, to: 'runtime' },
  68. { from: buildPaths.dsh, to: 'dsh' },
  69. // electron-builder excludes a source directory's root node_modules.
  70. { from: join(buildPaths.dsh, 'node_modules'), to: 'dsh/node_modules' },
  71. ],
  72. mac: {
  73. category: 'public.app-category.developer-tools',
  74. identity: macOSSigning?.signingIdentity,
  75. forceCodeSigning: true,
  76. hardenedRuntime: true,
  77. // Native runtime files are pre-signed; PAK resources are sealed by their enclosing bundle.
  78. signIgnore: ['/Contents/Resources/dsh(?:/|$)', '\\.pak$'],
  79. notarize: true,
  80. target: ['dmg', 'zip'],
  81. },
  82. dmg: {
  83. sign: true,
  84. writeUpdateInfo: false,
  85. },
  86. afterPack: async context => {
  87. const { verifyDesktopRuntime } = await import('./lib/types/runtime-tree.js')
  88. await verifyDesktopRuntime(join(context.packager.getResourcesDir(context.appOutDir), 'dsh'),
  89. context.packager.appInfo.version, { platform: resolvedPlatform, arch: resolvedArch })
  90. },
  91. afterSign: async context => {
  92. if (context.electronPlatformName !== 'darwin') return
  93. const { verifyDesktopRuntime } = await import('./lib/types/runtime-tree.js')
  94. await verifyDesktopRuntime(join(context.appOutDir, `${context.packager.appInfo.productFilename}.app`, 'Contents', 'Resources', 'dsh'),
  95. context.packager.appInfo.version, { platform: 'darwin', arch: resolvedArch })
  96. verifyMacOSSignatureAfterSign(context, macOSSigning ?? resolveMacOSSigningEnvironment(env))
  97. },
  98. artifactBuildCompleted: artifact => {
  99. if (!artifact.file.endsWith('.dmg')) return
  100. return notarizeMacOSDiskImageArtifact(
  101. artifact,
  102. env,
  103. macOSSigning ?? resolveMacOSSigningEnvironment(env),
  104. )
  105. },
  106. win: {
  107. forceCodeSigning: !unsigned,
  108. signtoolOptions: {
  109. sign: windowsSigner,
  110. signingHashAlgorithms: ['sha256'],
  111. },
  112. target: ['nsis'],
  113. },
  114. linux: {
  115. category: 'Development',
  116. target: ['AppImage'],
  117. },
  118. nsis: {
  119. include: fileURLToPath(new URL('./scripts/installer.nsh', import.meta.url)),
  120. oneClick: false,
  121. allowToChangeInstallationDirectory: true,
  122. differentialPackage: true,
  123. },
  124. publish: update === undefined ? null : [{ provider: 'generic', url: update.publicUrl }],
  125. }
  126. }
  127. export default createElectronBuilderConfig()