Skip to content

Commit 9272cf3

Browse files
authored
fix: allow custom electron zip name to be provided when unpacking a provided electronDist (electron-userland#9126)
1 parent 8ba9be4 commit 9272cf3

7 files changed

Lines changed: 97 additions & 55 deletions

File tree

.changeset/nice-onions-yawn.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"app-builder-lib": patch
3+
---
4+
5+
fix: allow custom electron zip name to be provided when unpacking a provided electronDist

packages/app-builder-lib/src/electron/ElectronFramework.ts

Lines changed: 71 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { asArray, copyDir, DO_NOT_USE_HARD_LINKS, executeAppBuilder, log, MAX_FILE_REQUESTS, statOrNull, unlinkIfExists } from "builder-util"
2-
import { emptyDir, readdir, rename } from "fs-extra"
3-
import * as fs from "fs/promises"
2+
import { emptyDir, readdir, rename, rm } from "fs-extra"
43
import * as path from "path"
54
import asyncPool from "tiny-async-pool"
65
import { Configuration } from "../configuration"
@@ -107,7 +106,7 @@ async function removeUnusedLanguagesIfNeeded(options: BeforeCopyExtraFilesOption
107106

108107
const language = path.basename(file, langFileExt)
109108
if (!wantedLanguages.includes(language)) {
110-
return fs.rm(path.join(dir, file), { recursive: true, force: true })
109+
return rm(path.join(dir, file), { recursive: true, force: true })
111110
}
112111
return
113112
})
@@ -149,7 +148,9 @@ class ElectronFramework implements Framework {
149148
}
150149

151150
async prepareApplicationStageDirectory(options: PrepareApplicationStageDirectoryOptions) {
152-
await unpack(options, createDownloadOpts(options.packager.config, options.platformName, options.arch, this.version), this.distMacOsAppName)
151+
const downloadOptions = createDownloadOpts(options.packager.config, options.platformName, options.arch, this.version)
152+
const shouldCleanup = await unpack(options, downloadOptions, this.distMacOsAppName)
153+
await cleanupAfterUnpack(options, this.distMacOsAppName, shouldCleanup)
153154
if (options.packager.config.downloadAlternateFFmpeg) {
154155
await injectFFMPEG(options, this.version)
155156
}
@@ -179,49 +180,79 @@ export async function createElectronFrameworkSupport(configuration: Configuratio
179180
return new ElectronFramework(branding.projectName, version, `${branding.productName}.app`)
180181
}
181182

182-
async function unpack(prepareOptions: PrepareApplicationStageDirectoryOptions, options: ElectronDownloadOptions, distMacOsAppName: string) {
183+
/**
184+
* Unpacks a custom or default Electron distribution into the app output directory.
185+
*/
186+
async function unpack(prepareOptions: PrepareApplicationStageDirectoryOptions, downloadOptions: ElectronDownloadOptions, distMacOsAppName: string) {
187+
const downloadUsingAdjustedConfig = (options: ElectronDownloadOptions) => {
188+
return executeAppBuilder(["unpack-electron", "--configuration", JSON.stringify([options]), "--output", appOutDir, "--distMacOsAppName", distMacOsAppName])
189+
}
190+
183191
const { packager, appOutDir, platformName } = prepareOptions
192+
const { version, arch } = downloadOptions
193+
const defaultZipName = `electron-v${version}-${platformName}-${arch}.zip`
184194

185-
const electronDist = packager.config.electronDist || null
186-
let dist: string | null = null
187-
// check if supplied a custom electron distributable/fork/predownloaded directory
188-
if (typeof electronDist === "string") {
189-
let resolvedDist: string
190-
// check if custom electron hook file for import resolving
191-
if ((await statOrNull(electronDist))?.isFile()) {
192-
const customElectronDist: any = await resolveFunction(packager.appInfo.type, electronDist, "electronDist")
193-
resolvedDist = await Promise.resolve(typeof customElectronDist === "function" ? customElectronDist(prepareOptions) : customElectronDist)
194-
} else {
195-
resolvedDist = electronDist
196-
}
197-
dist = path.isAbsolute(resolvedDist) ? resolvedDist : path.resolve(packager.projectDir, resolvedDist)
198-
}
199-
if (dist != null) {
200-
const zipFile = `electron-v${options.version}-${platformName}-${options.arch}.zip`
201-
if ((await statOrNull(path.join(dist, zipFile))) != null) {
202-
log.info({ dist, zipFile }, "resolved electronDist")
203-
options.cache = dist
204-
dist = null
205-
} else {
206-
log.info({ electronDist: log.filePath(dist), expectedFile: zipFile }, "custom electronDist provided but no zip found; assuming unpacked electron directory.")
207-
}
195+
let resolvedDist: string | null = null
196+
try {
197+
const electronDistHook: any = await resolveFunction(packager.appInfo.type, packager.config.electronDist, "electronDist")
198+
resolvedDist = typeof electronDistHook === "function" ? await Promise.resolve(electronDistHook(prepareOptions)) : electronDistHook
199+
} catch (error: any) {
200+
throw new Error("Failed to resolve electronDist: " + error.message)
208201
}
209202

210-
let isFullCleanup = false
211-
if (dist == null) {
212-
await executeAppBuilder(["unpack-electron", "--configuration", JSON.stringify([options]), "--output", appOutDir, "--distMacOsAppName", distMacOsAppName])
213-
} else {
214-
isFullCleanup = true
215-
const source = packager.getElectronSrcDir(dist)
216-
const destination = packager.getElectronDestinationDir(appOutDir)
217-
log.info({ source, destination }, "copying Electron")
218-
await emptyDir(appOutDir)
219-
await copyDir(source, destination, {
220-
isUseHardLink: DO_NOT_USE_HARD_LINKS,
203+
if (resolvedDist == null) {
204+
// if no custom electronDist is provided, use the default unpack logic
205+
log.debug(null, "no custom electronDist provided, unpacking default Electron distribution")
206+
await downloadUsingAdjustedConfig(downloadOptions)
207+
return true // indicates that we should clean up after unpacking
208+
}
209+
210+
if (!path.isAbsolute(resolvedDist)) {
211+
// if it's a relative path, resolve it against the project directory
212+
resolvedDist = path.resolve(packager.projectDir, resolvedDist)
213+
}
214+
215+
const electronDistStats = await statOrNull(resolvedDist)
216+
if (!electronDistStats) {
217+
throw new Error(`The specified electronDist does not exist: ${resolvedDist}. Please provide a valid path to the Electron zip file or cache directory.`)
218+
}
219+
220+
if (resolvedDist.endsWith(".zip")) {
221+
log.info({ zipFile: resolvedDist }, "using custom electronDist zip file")
222+
await downloadUsingAdjustedConfig({
223+
...downloadOptions,
224+
cache: path.dirname(resolvedDist), // set custom directory to the zip file's directory
225+
customFilename: path.basename(resolvedDist), // set custom filename to the zip file's name
221226
})
227+
return false // do not clean up after unpacking, it's a custom bundle and we should respect its configuration/contents as required
222228
}
223229

224-
await cleanupAfterUnpack(prepareOptions, distMacOsAppName, isFullCleanup)
230+
if (electronDistStats.isDirectory()) {
231+
// backward compatibility: if electronDist is a directory, check for the default zip file inside it
232+
const files = await readdir(resolvedDist)
233+
if (files.includes(defaultZipName)) {
234+
log.info({ electronDist: log.filePath(resolvedDist) }, "using custom electronDist directory")
235+
await downloadUsingAdjustedConfig({
236+
...downloadOptions,
237+
cache: resolvedDist,
238+
customFilename: defaultZipName,
239+
})
240+
return false
241+
}
242+
}
243+
244+
// if we reach here, it means the provided electronDist is neither a zip file nor a directory with the default zip file
245+
// e.g. we treat it as a custom already-unpacked Electron distribution
246+
log.info({ electronDist: log.filePath(resolvedDist) }, "using custom unpacked Electron distribution")
247+
const source = packager.getElectronSrcDir(resolvedDist)
248+
const destination = packager.getElectronDestinationDir(appOutDir)
249+
log.info({ source, destination }, "copying unpacked Electron")
250+
await emptyDir(appOutDir)
251+
await copyDir(source, destination, {
252+
isUseHardLink: DO_NOT_USE_HARD_LINKS,
253+
})
254+
255+
return false
225256
}
226257

227258
function cleanupAfterUnpack(prepareOptions: PrepareApplicationStageDirectoryOptions, distMacOsAppName: string, isFullCleanup: boolean) {

test/snapshots/mac/macPackagerTest.js.snap

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
22

3-
exports[`electronDist 1`] = `"corrupted Electron dist"`;
4-
53
exports[`multiple asar resources 1`] = `
64
{
75
"mac": [

test/snapshots/windows/winCodeSignTest.js.snap

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
22

3-
exports[`electronDist 1`] = `"ENOENT"`;
4-
53
exports[`forceCodeSigning 1`] = `"ERR_ELECTRON_BUILDER_INVALID_CONFIGURATION"`;
64

75
exports[`parseDn 1`] = `

test/src/mac/macPackagerTest.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -142,12 +142,17 @@ test.ifMac("yarn two package.json w/ native module", ({ expect }) =>
142142
)
143143

144144
test.ifMac("electronDist", ({ expect }) =>
145-
appThrows(expect, {
146-
targets: Platform.MAC.createTarget(DIR_TARGET, Arch.x64),
147-
config: {
148-
electronDist: "foo",
145+
appThrows(
146+
expect,
147+
{
148+
targets: Platform.MAC.createTarget(DIR_TARGET, Arch.x64),
149+
config: {
150+
electronDist: "foo",
151+
},
149152
},
150-
})
153+
{},
154+
error => expect(error.message).toContain("Failed to resolve electronDist")
155+
)
151156
)
152157

153158
test.ifWinCi("Build macOS on Windows is not supported", ({ expect }) => appThrows(expect, platform(Platform.MAC)))

test/src/updater/linuxUpdaterTest.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ const packageManagerMap: {
5555
pms: ["pacman"],
5656
updater: PacmanUpdater,
5757
extension: "pacman",
58-
}
58+
},
5959
}
6060

6161
for (const distro in packageManagerMap) {

test/src/windows/winCodeSignTest.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -103,12 +103,17 @@ test("forceCodeSigning", ({ expect }) =>
103103
}))
104104

105105
test("electronDist", ({ expect }) =>
106-
appThrows(expect, {
107-
targets: windowsDirTarget,
108-
config: {
109-
electronDist: "foo",
106+
appThrows(
107+
expect,
108+
{
109+
targets: windowsDirTarget,
110+
config: {
111+
electronDist: "foo",
112+
},
110113
},
111-
}))
114+
{},
115+
error => expect(error.message).toContain("Failed to resolve electronDist")
116+
))
112117

113118
test("azure signing without credentials", ({ expect }) =>
114119
appThrows(

0 commit comments

Comments
 (0)