| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204 |
- import { join } from 'node:path'
- import { fileURLToPath } from 'node:url'
- import { execFile } from 'node:child_process'
- import { promisify } from 'node:util'
- import {
- resolveDesktopAppId,
- resolveMacOSNotarizationEnvironment,
- resolveMacOSSigningEnvironment,
- } from './desktop-release-environment.mjs'
- import { notarizeMacOSDiskImageArtifact } from './notarize-macos-disk-images.mjs'
- import { verifyMacOSSignatureAfterSign } from './verify-macos-signature.mjs'
- import {
- createWindowsTokenSigner,
- installWindowsNsisBootstrapSigner,
- resolveWindowsUpdatePublisher,
- scrubWindowsSigningEnvironment,
- } from './windows-sign.mjs'
- import { resolveDesktopAutoUpdateConfig } from './desktop-auto-update-environment.mjs'
- import { resolveDesktopPolicyEnvironment } from './desktop-policy-environment.mjs'
- import { desktopTargetBuildPaths, resolveDesktopBuildTarget } from './desktop-build-paths.mjs'
- import { installWindowsDirectoryInstaller } from './windows-directory-installer.mjs'
- import { preserveWindowsRuntimeSignature } from './windows-runtime-signature.mjs'
- import {
- resolveMacOSAppUpdateFeed,
- verifyMacOSAppUpdateConfig,
- writeMacOSAppUpdateConfig,
- } from './macos-app-update-config.mjs'
- /**
- * Create electron-builder configuration from one release environment.
- * @param {NodeJS.ProcessEnv} env - Packaging environment.
- * @param {NodeJS.Platform} hostPlatform - Build-host platform used when no explicit target is present.
- * @param {string} hostArch - Build-host architecture used when no explicit target is present.
- * @param {string | undefined} preparedRuntime - Verified private dsh tree for installed-update qualification; ordinary releases use the target tree.
- * @returns {object} electron-builder configuration.
- */
- export function createElectronBuilderConfig(
- env = process.env,
- hostPlatform = process.platform,
- hostArch = process.arch,
- preparedRuntime = undefined,
- ) {
- const appId = resolveDesktopAppId(env)
- const policy = resolveDesktopPolicyEnvironment(env)
- const targetPlatform = env.DSH_DESKTOP_TARGET_PLATFORM
- const resolvedPlatform = targetPlatform ?? hostPlatform
- const resolvedArch = env.DSH_DESKTOP_TARGET_ARCH ?? hostArch
- if (env.DSH_DESKTOP_UNSIGNED !== undefined && !['0', '1'].includes(env.DSH_DESKTOP_UNSIGNED)) {
- throw new Error('desktop package: DSH_DESKTOP_UNSIGNED must be 0 or 1')
- }
- const unsigned = env.DSH_DESKTOP_UNSIGNED === '1'
- if (unsigned && resolvedPlatform !== 'win32') throw new Error('desktop package: unsigned builds require Windows')
- const packagesMacOS = targetPlatform === 'darwin' || (targetPlatform === undefined && hostPlatform === 'darwin')
- const packagesWindows = resolvedPlatform === 'win32'
- if (resolvedPlatform === 'win32') installWindowsDirectoryInstaller()
- const macOSSigning = packagesMacOS ? resolveMacOSSigningEnvironment(env) : undefined
- if (packagesMacOS) resolveMacOSNotarizationEnvironment(env)
- const buildPaths = desktopTargetBuildPaths(resolveDesktopBuildTarget(env, hostPlatform, hostArch))
- let primaryRuntimeDestination
- const windowsSigner = packagesWindows && !unsigned
- ? createWindowsTokenSigner({
- certificateFile: env.DSH_DESKTOP_WINDOWS_CER_FILE,
- signTool: env.DSH_DESKTOP_WINDOWS_SIGNTOOL,
- tokenPin: env.DSH_DESKTOP_WINDOWS_TOKEN_PIN,
- keyContainer: env.DSH_DESKTOP_WINDOWS_KEY_CONTAINER,
- preserveSignature: async path => primaryRuntimeDestination === undefined ? false : preserveWindowsRuntimeSignature(path, {
- sourceRoot: join(buildPaths.runtime, 'primary-runtime'),
- destinationRoot: primaryRuntimeDestination,
- runDirectory: env.DSH_DESKTOP_PACKAGING_RUN_DIR,
- }),
- })
- : undefined
- if (windowsSigner !== undefined) {
- installWindowsNsisBootstrapSigner({ sign: windowsSigner })
- }
- const update = unsigned ? undefined : resolveDesktopAutoUpdateConfig(env, resolvedPlatform, resolvedArch)
- if (preparedRuntime !== undefined) buildPaths.dsh = preparedRuntime
- return {
- appId,
- extraMetadata: { dshDesktopAppId: appId, dshMandatoryUpdatePolicy: policy },
- productName: 'DeepSeek Harness',
- artifactName: 'deepseek-harness-${version}-${os}-${arch}.${ext}',
- directories: { output: unsigned ? join(buildPaths.root, 'unsigned-artifacts') : buildPaths.artifacts },
- asar: true,
- electronDist: buildPaths.electron,
- electronFuses: { runAsNode: true },
- beforeBuild: async () => {
- if (resolvedPlatform !== 'win32') return true
- await promisify(execFile)('powershell.exe', ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-File',
- fileURLToPath(new URL('./prepare-windows-installer.ps1', import.meta.url)),
- '-OutputDirectory', join(buildPaths.root, 'installer-ui')], {
- env: scrubWindowsSigningEnvironment(env), windowsHide: true,
- })
- if (windowsSigner !== undefined) {
- await windowsSigner({ path: join(buildPaths.root, 'installer-ui', 'window-frame.dll'), hash: 'sha256', isNest: false })
- }
- // A falsy result tells electron-builder to omit its production node_modules collection.
- return true
- },
- files: [
- 'lib/main.js',
- 'lib/preload-app.cjs',
- 'lib/preload-mandatory.cjs',
- 'lib/preload-update-dialog.cjs',
- 'renderer/**/*',
- 'package.json',
- { from: buildPaths.dsh, to: 'dsh', filter: ['**/*'] },
- // electron-builder excludes a source directory's root node_modules.
- { from: join(buildPaths.dsh, 'node_modules'), to: 'dsh/node_modules', filter: ['**/*'] },
- ],
- asarUnpack: [
- '**/*.{node,dylib,dll,so,exe}',
- '**/*.so.*',
- '**/spawn-helper',
- '**/@vscode/ripgrep/bin/rg',
- ],
- extraResources: [
- { from: buildPaths.runtime, to: 'runtime' },
- { from: fileURLToPath(new URL('../resources/icon-windows.png', import.meta.url)), to: 'icon.png' },
- ],
- mac: {
- icon: fileURLToPath(new URL('../resources/icon-macos.png', import.meta.url)),
- category: 'public.app-category.developer-tools',
- identity: macOSSigning?.signingIdentity,
- forceCodeSigning: true,
- hardenedRuntime: true,
- // ASAR-unpacked native runtime files are pre-signed; PAK resources are sealed by their enclosing bundle.
- signIgnore: ['/Contents/Resources/app\\.asar\\.unpacked/dsh(?:/|$)', '/Contents/Resources/runtime/primary-runtime(?:/|$)', '\\.pak$'],
- notarize: true,
- target: ['dmg', 'zip'],
- },
- dmg: {
- sign: true,
- writeUpdateInfo: false,
- },
- beforePack: async context => {
- if (windowsSigner !== undefined) primaryRuntimeDestination = join(context.appOutDir, 'resources', 'runtime', 'primary-runtime')
- if (policy === undefined) return
- const { resolveDesktopPolicyConfig } = await import('../lib/types/mandatory-update-policy.js')
- resolveDesktopPolicyConfig(policy)
- },
- afterPack: async context => {
- const { verifyDesktopRuntime, writeDesktopRuntime } = await import('../lib/types/runtime-tree.js')
- const resourcesDir = context.packager.getResourcesDir(context.appOutDir)
- if (resolvedPlatform === 'darwin' && update !== undefined) {
- await writeMacOSAppUpdateConfig(resourcesDir, resolveMacOSAppUpdateFeed(context.packager.config.publish),
- context.packager.appInfo.updaterCacheDirName)
- }
- if (resolvedPlatform === 'win32' && !unsigned) {
- // Windows signs copied executable resources before afterPack runs.
- const prepared = await verifyDesktopRuntime(buildPaths.dsh,
- context.packager.appInfo.version, { platform: resolvedPlatform, arch: resolvedArch })
- writeDesktopRuntime(buildPaths.dsh, prepared.release, prepared.sharedPackages.map(entry => entry.name),
- { platform: resolvedPlatform, arch: resolvedArch })
- }
- await verifyDesktopRuntime(buildPaths.dsh,
- context.packager.appInfo.version, { platform: resolvedPlatform, arch: resolvedArch })
- },
- afterSign: async context => {
- if (context.electronPlatformName !== 'darwin') return
- const appPath = join(context.appOutDir, `${context.packager.appInfo.productFilename}.app`)
- if (update !== undefined) {
- await verifyMacOSAppUpdateConfig(appPath, resolveMacOSAppUpdateFeed(context.packager.config.publish),
- context.packager.appInfo.updaterCacheDirName)
- }
- verifyMacOSSignatureAfterSign(context, macOSSigning ?? resolveMacOSSigningEnvironment(env))
- },
- artifactBuildCompleted: artifact => {
- if (!artifact.file.endsWith('.dmg')) return
- return notarizeMacOSDiskImageArtifact(
- artifact,
- env,
- macOSSigning ?? resolveMacOSSigningEnvironment(env),
- )
- },
- win: {
- icon: fileURLToPath(new URL('../resources/icon-windows.png', import.meta.url)),
- forceCodeSigning: !unsigned,
- signtoolOptions: {
- sign: windowsSigner,
- publisherName: windowsSigner === undefined ? undefined : resolveWindowsUpdatePublisher(env.DSH_DESKTOP_WINDOWS_CER_FILE),
- signingHashAlgorithms: ['sha256'],
- },
- target: ['nsis'],
- },
- linux: {
- category: 'Development',
- target: ['AppImage'],
- },
- nsis: {
- installerSidebar: join(buildPaths.root, 'installer-ui', 'uninstaller-sidebar.bmp'),
- uninstallerSidebar: join(buildPaths.root, 'installer-ui', 'uninstaller-sidebar.bmp'),
- include: fileURLToPath(new URL('./installer.nsh', import.meta.url)),
- oneClick: false,
- perMachine: false,
- allowElevation: false,
- allowToChangeInstallationDirectory: false,
- installerLanguages: ['en_US', 'zh_CN'],
- differentialPackage: true,
- },
- detectUpdateChannel: false,
- publish: update === undefined ? null : [{ provider: 'generic', url: update.publicUrl, channel: 'nightly' }],
- }
- }
|