Skip to content

Commit 27b2ba8

Browse files
beyondkmpbeyondkmpPayneFuRCvagrant
authored
feat: use electron-winstaller instead of self module (#8344)
1. Add `customSquirrelExePath` option, which allows using custom squirrel windows 2. Use official electron-winstaller 3. Support Windows ARM 4. Fix some bugs(electron-userland/electron-builder-binaries#33, electron-userland/electron-builder-binaries#52) --------- Co-authored-by: beyondkmp <beyondkmkp@gmail.com> Co-authored-by: payne.fu <payne.fu@ringcentral.com> Co-authored-by: vagrant <vagrant@debian-12.6-amd64>
1 parent 6261c9a commit 27b2ba8

12 files changed

Lines changed: 271 additions & 650 deletions

File tree

.changeset/breezy-seas-destroy.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"app-builder-lib": patch
3+
"electron-builder-squirrel-windows": patch
4+
---
5+
6+
use electron-winstaller instead of self module

packages/app-builder-lib/scheme.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6009,6 +6009,10 @@
60096009
"string"
60106010
]
60116011
},
6012+
"customSquirrelVendorDir": {
6013+
"description": "The custom squirrel vender dir. If not specified will use the Squirrel.Windows that is shipped with electron-installer(https://github.com/electron/windows-installer/tree/main/vendor).\nAfter https://github.com/electron-userland/electron-builder-binaries/pull/56 merged, will add `electron-builder-binaries` to get the latest version of squirrel.",
6014+
"type": "string"
6015+
},
60126016
"iconUrl": {
60136017
"description": "A URL to an ICO file to use as the application icon (displayed in Control Panel > Programs and Features). Defaults to the Electron icon.\n\nPlease note — [local icon file url is not accepted](https://github.com/atom/grunt-electron-installer/issues/73), must be https/http.\n\nIf you don't plan to build windows installer, you can omit it.\nIf your project repository is public on GitHub, it will be `https://github.com/${u}/${p}/blob/master/build/icon.ico?raw=true` by default.",
60146018
"type": [

packages/app-builder-lib/src/options/SquirrelWindowsOptions.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,12 @@ export interface SquirrelWindowsOptions extends TargetSpecificOptions {
4040
*/
4141
readonly useAppIdAsId?: boolean
4242

43+
/**
44+
* The custom squirrel vender dir. If not specified will use the Squirrel.Windows that is shipped with electron-installer(https://github.com/electron/windows-installer/tree/main/vendor).
45+
* After https://github.com/electron-userland/electron-builder-binaries/pull/56 merged, will add `electron-builder-binaries` to get the latest version of squirrel.
46+
*/
47+
readonly customSquirrelVendorDir?: string
48+
4349
/**
4450
* https://github.com/electron-userland/electron-builder/issues/1743
4551
* @private

packages/electron-builder-squirrel-windows/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@
1212
"bugs": "https://github.com/electron-userland/electron-builder/issues",
1313
"homepage": "https://github.com/electron-userland/electron-builder",
1414
"files": [
15-
"out"
15+
"out",
16+
"template.nuspectemplate"
1617
],
1718
"dependencies": {
1819
"app-builder-lib": "workspace:*",
19-
"archiver": "^5.3.1",
2020
"builder-util": "workspace:*",
21-
"fs-extra": "^10.1.0"
21+
"electron-winstaller":"5.4.0"
2222
},
2323
"devDependencies": {
2424
"@types/archiver": "5.3.1",

packages/electron-builder-squirrel-windows/src/SquirrelWindowsTarget.ts

Lines changed: 63 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
1+
import { InvalidConfigurationError, log, isEmptyOrSpaces } from "builder-util"
12
import { Arch, getArchSuffix, SquirrelWindowsOptions, Target } from "app-builder-lib"
2-
import { getBinFromUrl } from "app-builder-lib/out/binDownload"
33
import { WinPackager } from "app-builder-lib/out/winPackager"
4-
import { InvalidConfigurationError, isEmptyOrSpaces, log } from "builder-util"
54
import { sanitizeFileName } from "builder-util/out/filename"
65
import * as path from "path"
7-
import { convertVersion, SquirrelBuilder, SquirrelOptions } from "./squirrelPack"
6+
import * as fs from "fs"
7+
import { readFile, writeFile } from "fs/promises"
8+
import { Options as SquirrelOptions, createWindowsInstaller, convertVersion } from "electron-winstaller"
89

910
export default class SquirrelWindowsTarget extends Target {
1011
//tslint:disable-next-line:no-object-literal-type-assertion
1112
readonly options: SquirrelWindowsOptions = { ...this.packager.platformSpecificBuildOptions, ...this.packager.config.squirrelWindows } as SquirrelWindowsOptions
13+
private appDirectory: string = ""
14+
private outputDirectory: string = ""
1215

1316
constructor(
1417
private readonly packager: WinPackager,
@@ -22,10 +25,7 @@ export default class SquirrelWindowsTarget extends Target {
2225
const version = packager.appInfo.version
2326
const sanitizedName = sanitizeFileName(this.appName)
2427

25-
// tslint:disable-next-line:no-invalid-template-strings
2628
const setupFile = packager.expandArtifactNamePattern(this.options, "exe", arch, "${productName} Setup ${version}.${ext}")
27-
const packageFile = `${sanitizedName}-${convertVersion(version)}-full.nupkg`
28-
2929
const installerOutDir = path.join(this.outDir, `squirrel-windows${getArchSuffix(arch)}`)
3030
const artifactPath = path.join(installerOutDir, setupFile)
3131

@@ -39,9 +39,14 @@ export default class SquirrelWindowsTarget extends Target {
3939
log.warn("For windows consider only distributing 64-bit or use nsis target, see https://github.com/electron-userland/electron-builder/issues/359#issuecomment-214851130")
4040
}
4141

42+
this.appDirectory = appOutDir
43+
this.outputDirectory = installerOutDir
4244
const distOptions = await this.computeEffectiveDistOptions()
43-
const squirrelBuilder = new SquirrelBuilder(distOptions, installerOutDir, packager)
44-
await squirrelBuilder.buildInstaller({ setupFile, packageFile }, appOutDir, this.outDir, arch)
45+
if (distOptions.vendorDirectory) {
46+
this.select7zipArch(distOptions.vendorDirectory, arch)
47+
}
48+
49+
await createWindowsInstaller(distOptions)
4550

4651
await packager.info.callArtifactBuildCompleted({
4752
file: artifactPath,
@@ -79,6 +84,13 @@ export default class SquirrelWindowsTarget extends Target {
7984
return this.options.name || this.packager.appInfo.name
8085
}
8186

87+
private select7zipArch(vendorDirectory: string, arch: Arch) {
88+
// Copy the 7-Zip executable for the configured architecture.
89+
const resolvedArch = getArchSuffix(arch) === "" ? process.arch : getArchSuffix(arch)
90+
fs.copyFileSync(path.join(vendorDirectory, `7z-${resolvedArch}.exe`), path.join(vendorDirectory, "7z.exe"))
91+
fs.copyFileSync(path.join(vendorDirectory, `7z-${resolvedArch}.dll`), path.join(vendorDirectory, "7z.dll"))
92+
}
93+
8294
async computeEffectiveDistOptions(): Promise<SquirrelOptions> {
8395
const packager = this.packager
8496
let iconUrl = this.options.iconUrl
@@ -96,39 +108,55 @@ export default class SquirrelWindowsTarget extends Target {
96108
checkConflictingOptions(this.options)
97109

98110
const appInfo = packager.appInfo
99-
const projectUrl = await appInfo.computePackageUrl()
100-
const appName = this.appName
111+
// If not specified will use the Squirrel.Windows that is shipped with electron-installer(https://github.com/electron/windows-installer/tree/main/vendor)
112+
// After https://github.com/electron-userland/electron-builder-binaries/pull/56 merged, will add `electron-builder-binaries` to get the latest version of squirrel.
113+
let vendorDirectory = this.options.customSquirrelVendorDir
114+
if (isEmptyOrSpaces(vendorDirectory) || !fs.existsSync(vendorDirectory)) {
115+
log.warn({ vendorDirectory }, "unable to access Squirrel.Windows vendor directory, falling back to default electron-winstaller")
116+
vendorDirectory = undefined
117+
}
118+
101119
const options: SquirrelOptions = {
102-
name: appName,
103-
productName: this.options.name || appInfo.productName,
104-
appId: this.options.useAppIdAsId ? appInfo.id : appName,
120+
appDirectory: this.appDirectory,
121+
outputDirectory: this.outputDirectory,
122+
name: this.options.useAppIdAsId ? appInfo.id : this.appName,
105123
version: appInfo.version,
106124
description: appInfo.description,
107-
// better to explicitly set to empty string, to avoid any nugget errors
125+
exe: `${this.packager.platformSpecificBuildOptions.executableName || this.options.name || appInfo.productName}.exe`,
108126
authors: appInfo.companyName || "",
109127
iconUrl,
110-
extraMetadataSpecs: projectUrl == null ? null : `\n <projectUrl>${projectUrl}</projectUrl>`,
111128
copyright: appInfo.copyright,
112-
packageCompressionLevel: parseInt((process.env.ELECTRON_BUILDER_COMPRESSION_LEVEL || packager.compression === "store" ? 0 : 9) as any, 10),
113-
vendorPath: await getBinFromUrl("Squirrel.Windows", "1.9.0", "zJHk4CMATM7jHJ2ojRH1n3LkOnaIezDk5FAzJmlSEQSiEdRuB4GGLCegLDtsRCakfHIVfKh3ysJHLjynPkXwhQ=="),
114-
...(this.options as any),
129+
vendorDirectory,
130+
nuspecTemplate: path.join(__dirname, "..", "template.nuspectemplate"),
131+
noMsi: !this.options.msi,
132+
}
133+
134+
const projectUrl = await appInfo.computePackageUrl()
135+
if (projectUrl != null) {
136+
const nuspecTemplate = await this.packager.info.tempDirManager.getTempFile({ prefix: "template", suffix: ".nuspectemplate" })
137+
let templateContent = await readFile(path.resolve(__dirname, "..", "template.nuspectemplate"), "utf8")
138+
const searchString = "<copyright><%- copyright %></copyright>"
139+
templateContent = templateContent.replace(searchString, `${searchString}\n <projectUrl>${projectUrl}</projectUrl>`)
140+
await writeFile(nuspecTemplate, templateContent)
141+
options.nuspecTemplate = nuspecTemplate
142+
}
143+
144+
if (await (await packager.signingManager.value).cscInfo.value) {
145+
options.windowsSign = {
146+
hookFunction: async (file: string) => {
147+
await packager.sign(file)
148+
},
149+
}
115150
}
116151

117152
if (isEmptyOrSpaces(options.description)) {
118-
options.description = options.productName
153+
options.description = this.options.name || appInfo.productName
119154
}
120155

121156
if (options.remoteToken == null) {
122157
options.remoteToken = process.env.GH_TOKEN || process.env.GITHUB_TOKEN
123158
}
124159

125-
if (!("loadingGif" in options)) {
126-
const resourceList = await packager.resourceList
127-
if (resourceList.includes("install-spinner.gif")) {
128-
options.loadingGif = path.join(packager.buildResourcesDir, "install-spinner.gif")
129-
}
130-
}
131-
132160
if (this.options.remoteReleases === true) {
133161
const info = await packager.info.repositoryInfo
134162
if (info == null) {
@@ -137,6 +165,15 @@ export default class SquirrelWindowsTarget extends Target {
137165
options.remoteReleases = `https://github.com/${info.user}/${info.project}`
138166
log.info({ remoteReleases: options.remoteReleases }, `remoteReleases is set`)
139167
}
168+
} else if (typeof this.options.remoteReleases === "string" && !isEmptyOrSpaces(this.options.remoteReleases)) {
169+
options.remoteReleases = this.options.remoteReleases
170+
}
171+
172+
if (!("loadingGif" in options)) {
173+
const resourceList = await packager.resourceList
174+
if (resourceList.includes("install-spinner.gif")) {
175+
options.loadingGif = path.join(packager.buildResourcesDir, "install-spinner.gif")
176+
}
140177
}
141178

142179
return options

0 commit comments

Comments
 (0)