electron-builder.config.mjs 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  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. { from: buildPaths.dsh, to: 'dsh', filter: ['**/*'] },
  66. // electron-builder excludes a source directory's root node_modules.
  67. { from: join(buildPaths.dsh, 'node_modules'), to: 'dsh/node_modules', filter: ['**/*'] },
  68. ],
  69. asarUnpack: [
  70. '**/*.{node,dylib,dll,so,exe}',
  71. '**/*.so.*',
  72. '**/spawn-helper',
  73. '**/@vscode/ripgrep/bin/rg',
  74. ],
  75. extraResources: [
  76. { from: buildPaths.runtime, to: 'runtime' },
  77. ],
  78. mac: {
  79. category: 'public.app-category.developer-tools',
  80. identity: macOSSigning?.signingIdentity,
  81. forceCodeSigning: true,
  82. hardenedRuntime: true,
  83. // ASAR-unpacked native runtime files are pre-signed; PAK resources are sealed by their enclosing bundle.
  84. signIgnore: ['/Contents/Resources/app\\.asar\\.unpacked/dsh(?:/|$)', '\\.pak$'],
  85. notarize: true,
  86. target: ['dmg', 'zip'],
  87. },
  88. dmg: {
  89. sign: true,
  90. writeUpdateInfo: false,
  91. },
  92. afterSign: async context => {
  93. if (context.electronPlatformName !== 'darwin') return
  94. verifyMacOSSignatureAfterSign(context, macOSSigning ?? resolveMacOSSigningEnvironment(env))
  95. },
  96. artifactBuildCompleted: artifact => {
  97. if (!artifact.file.endsWith('.dmg')) return
  98. return notarizeMacOSDiskImageArtifact(
  99. artifact,
  100. env,
  101. macOSSigning ?? resolveMacOSSigningEnvironment(env),
  102. )
  103. },
  104. win: {
  105. forceCodeSigning: !unsigned,
  106. signtoolOptions: {
  107. sign: windowsSigner,
  108. signingHashAlgorithms: ['sha256'],
  109. },
  110. target: ['nsis'],
  111. },
  112. linux: {
  113. category: 'Development',
  114. target: ['AppImage'],
  115. },
  116. nsis: {
  117. include: fileURLToPath(new URL('./scripts/installer.nsh', import.meta.url)),
  118. oneClick: false,
  119. allowToChangeInstallationDirectory: true,
  120. differentialPackage: true,
  121. },
  122. publish: update === undefined ? null : [{ provider: 'generic', url: update.publicUrl }],
  123. }
  124. }
  125. export default createElectronBuilderConfig()