diff --git a/.idea/dictionaries/develar.xml b/.idea/dictionaries/develar.xml index 2e2a62d6fe5..47b8df2d426 100644 --- a/.idea/dictionaries/develar.xml +++ b/.idea/dictionaries/develar.xml @@ -156,6 +156,7 @@ pkcs pkgbuild pkgutil + pluginsdir postinstall powerrequired powershell diff --git a/README.md b/README.md index 064c90840ae..1ee14c70362 100755 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ A complete solution to package and build a ready for distribution Electron app f * All platforms: `7z`, `zip`, `tar.xz`, `tar.lz`, `tar.gz`, `tar.bz2`, `dir` (unpacked directory). * [macOS](https://github.com/electron-userland/electron-builder/wiki/Options#MacOptions-target): `dmg`, `pkg`, `mas`. * [Linux](https://github.com/electron-userland/electron-builder/wiki/Options#LinuxBuildOptions-target): [AppImage](http://appimage.org), [snap](http://snapcraft.io), debian package (`deb`), `rpm`, `freebsd`, `pacman`, `p5p`, `apk`. - * [Windows](https://github.com/electron-userland/electron-builder/wiki/Options#WinBuildOptions-target): NSIS, Web installer, AppX (Windows Store), Squirrel.Windows. + * [Windows](https://github.com/electron-userland/electron-builder/wiki/Options#WinBuildOptions-target): `nsis` (Installer), `nsis-web` (Web installer), `portable` (portable app without installation), AppX (Windows Store), Squirrel.Windows. * [Two package.json structure](https://github.com/electron-userland/electron-builder/wiki/Two-package.json-Structure) is supported, but you are not forced to use it even if you have native production dependencies. * [Publishing artifacts](https://github.com/electron-userland/electron-builder/wiki/Publishing-Artifacts) to GitHub Releases, Amazon S3 and Bintray. * Pack in a distributable format [already packaged app](#pack-only-in-a-distributable-format). diff --git a/docs/NSIS.md b/docs/NSIS.md index 440e16966ca..50e24cc048c 100644 --- a/docs/NSIS.md +++ b/docs/NSIS.md @@ -12,7 +12,7 @@ Two options are available — [include](https://github.com/electron-userland/ele Keep in mind — if you customize NSIS script, you should always state about it in the issue reports. And don't expect that your issue will be resolved. 1. Add file `build/installer.nsh`. -2. Define wanted macro to customise: `customHeader`, `preInit`, `customInit`, `customUnInit`, `customInstall`, `customUnInstall`. Example: +2. Define wanted macro to customise: `customHeader`, `preInit`, `customInit`, `customUnInit`, `customInstall`, `customUnInstall`, `customRemoveFiles`. Example: ```nsis !macro customHeader !system "echo '' > ${BUILD_RESOURCES_DIR}/customHeader" diff --git a/docs/Options.md b/docs/Options.md index 9de514a344a..525cc103694 100644 --- a/docs/Options.md +++ b/docs/Options.md @@ -240,7 +240,7 @@ To use Squirrel.Windows please install `electron-builder-squirrel-windows` depen ### `win` Windows Specific Options | Name | Description | --- | --- -| target |

Target package type: list of nsis, nsis-web (Web installer), appx, squirrel, 7z, zip, tar.xz, tar.lz, tar.gz, tar.bz2, dir. Defaults to nsis.

AppX package can be built only on Windows 10.

To use Squirrel.Windows please install electron-builder-squirrel-windows dependency.

+| target |

Target package type: list of nsis, nsis-web (Web installer), portable (portable app without installation), appx, squirrel, 7z, zip, tar.xz, tar.lz, tar.gz, tar.bz2, dir. Defaults to nsis.

AppX package can be built only on Windows 10.

To use Squirrel.Windows please install electron-builder-squirrel-windows dependency.

| signingHashAlgorithms |

Array of signing algorithms used. Defaults to ['sha1', 'sha256']

For AppX sha256 is always used.

| icon | The path to application icon. Defaults to `build/icon.ico` (consider using this convention instead of complicating your configuration). | legalTrademarks | The trademarks and registered trademarks. diff --git a/package.json b/package.json index a2e09504c98..0761a788fd6 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "chromium-pickle-js": "^0.2.0", "cuint": "^0.2.2", "debug": "^2.6.1", - "electron-download-tf": "3.2.0", + "electron-download-tf": "4.0.0", "electron-macos-sign": "~1.6.0", "fs-extra-p": "^4.0.2", "hosted-git-info": "^2.2.0", @@ -78,7 +78,7 @@ "decompress-zip": "^0.3.0", "depcheck": "^0.6.7", "docdash": "https://github.com/develar/docdash.git", - "electron-download-tf": "3.2.0", + "electron-download-tf": "4.0.0", "jest-cli": "^19.0.2", "jest-environment-node-debug": "^2.0.0", "jest-junit-reporter": "^1.0.1", diff --git a/packages/electron-builder-http/src/CancellationToken.ts b/packages/electron-builder-http/src/CancellationToken.ts index 12b669ba4a2..f66c215bfeb 100644 --- a/packages/electron-builder-http/src/CancellationToken.ts +++ b/packages/electron-builder-http/src/CancellationToken.ts @@ -2,7 +2,7 @@ import BluebirdPromise from "bluebird-lst" import { EventEmitter } from "events" export class CancellationToken extends EventEmitter { - private parentCancelHandler: any | null = null + private parentCancelHandler: (() => any) | null = null private _cancelled: boolean get cancelled(): boolean { @@ -42,7 +42,7 @@ export class CancellationToken extends EventEmitter { } } - createPromise(callback: (resolve: (thenableOrResult?: R) => void, reject: (error?: any) => void, onCancel: (callback: () => void) => void) => void): Promise { + createPromise(callback: (resolve: (thenableOrResult?: R) => void, reject: (error?: Error) => void, onCancel: (callback: () => void) => void) => void): Promise { if (this.cancelled) { return BluebirdPromise.reject(new CancellationError()) } diff --git a/packages/electron-builder/package.json b/packages/electron-builder/package.json index 87622a7b7e0..61317aa6dd9 100644 --- a/packages/electron-builder/package.json +++ b/packages/electron-builder/package.json @@ -56,7 +56,7 @@ "electron-builder-http": "0.0.0-semantic-release", "electron-builder-util": "0.0.0-semantic-release", "electron-publish": "0.0.0-semantic-release", - "electron-download-tf": "3.2.0", + "electron-download-tf": "4.0.0", "electron-macos-sign": "~1.6.0", "fs-extra-p": "^4.0.2", "hosted-git-info": "^2.2.0", diff --git a/packages/electron-builder/src/cli/create-self-signed-cert.ts b/packages/electron-builder/src/cli/create-self-signed-cert.ts index 098567f06c3..4876e27503e 100644 --- a/packages/electron-builder/src/cli/create-self-signed-cert.ts +++ b/packages/electron-builder/src/cli/create-self-signed-cert.ts @@ -1,12 +1,12 @@ -import yargs from "yargs" -import { printErrorAndExit } from "electron-builder-util/out/promise" import { exec, spawn } from "electron-builder-util" -import { getSignVendorPath } from "../windowsCodeSign" -import * as path from "path" -import sanitizeFileName from "sanitize-filename" +import { unlinkIfExists } from "electron-builder-util/out/fs" import { log } from "electron-builder-util/out/log" +import { printErrorAndExit } from "electron-builder-util/out/promise" import { TmpDir } from "electron-builder-util/out/tmp" -import { unlinkIfExists } from "electron-builder-util/out/fs" +import * as path from "path" +import sanitizeFileName from "sanitize-filename" +import yargs from "yargs" +import { getSignVendorPath } from "../windowsCodeSign" async function main() { const args: any = yargs @@ -34,7 +34,7 @@ async function main() { const certLocation = "Cert:\\LocalMachine\\TrustedPeople" log(`${pfx} will be imported into ${certLocation} Operation will be succeed only if runned from root. Otherwise import file manually.`) await spawn("powershell.exe", ["Import-PfxCertificate", "-FilePath", `"${pfx}"`, "-CertStoreLocation", ""]) - tmpDir.cleanup() + await tmpDir.cleanup() } main() diff --git a/packages/electron-builder/src/options/winOptions.ts b/packages/electron-builder/src/options/winOptions.ts index 8d602ef68ea..6af2c7d6d4d 100644 --- a/packages/electron-builder/src/options/winOptions.ts +++ b/packages/electron-builder/src/options/winOptions.ts @@ -5,7 +5,7 @@ import { PlatformSpecificBuildOptions } from "../metadata" */ export interface WinBuildOptions extends PlatformSpecificBuildOptions { /** - Target package type: list of `nsis`, `nsis-web` (Web installer), `appx`, `squirrel`, `7z`, `zip`, `tar.xz`, `tar.lz`, `tar.gz`, `tar.bz2`, `dir`. Defaults to `nsis`. + Target package type: list of `nsis`, `nsis-web` (Web installer), `portable` (portable app without installation), `appx`, `squirrel`, `7z`, `zip`, `tar.xz`, `tar.lz`, `tar.gz`, `tar.bz2`, `dir`. Defaults to `nsis`. AppX package can be built only on Windows 10. diff --git a/packages/electron-builder/src/targets/WebInstaller.ts b/packages/electron-builder/src/targets/WebInstaller.ts new file mode 100644 index 00000000000..6cdb136fedb --- /dev/null +++ b/packages/electron-builder/src/targets/WebInstaller.ts @@ -0,0 +1,49 @@ +import { Platform } from "electron-builder-core" +import { NsisWebOptions } from "../options/winOptions" +import { computeDownloadUrl, getPublishConfigs, getPublishConfigsForUpdateInfo } from "../publish/PublishManager" +import { WinPackager } from "../winPackager" +import NsisTarget from "./nsis" + +export default class WebInstallerTarget extends NsisTarget { + constructor(packager: WinPackager, outDir: string, targetName: string) { + super(packager, outDir, targetName) + } + + protected get isWebInstaller(): boolean { + return true + } + + protected async configureDefines(oneClick: boolean, defines: any) { + //noinspection ES6MissingAwait + const promise = (NsisTarget.prototype).configureDefines.call(this, oneClick, defines) + await promise + + const packager = this.packager + const options = this.options + + let appPackageUrl = (options).appPackageUrl + if (appPackageUrl == null) { + const publishConfigs = await getPublishConfigsForUpdateInfo(packager, await getPublishConfigs(packager, this.options, false)) + if (publishConfigs == null || publishConfigs.length === 0) { + throw new Error("Cannot compute app package download URL") + } + + appPackageUrl = computeDownloadUrl(publishConfigs[0], null, packager.appInfo.version, { + os: Platform.WINDOWS.buildConfigurationKey, + arch: "" + }) + + defines.APP_PACKAGE_URL_IS_INCOMLETE = null + } + + defines.APP_PACKAGE_URL = appPackageUrl + } + + protected get installerFilenamePattern(): string { + return "${productName} Web Setup ${version}.${ext}" + } + + protected generateGitHubInstallerName(): string { + return `${this.packager.appInfo.name}-WebSetup-${this.packager.appInfo.version}.exe` + } +} \ No newline at end of file diff --git a/packages/electron-builder/src/targets/nsis.ts b/packages/electron-builder/src/targets/nsis.ts index bd7a0f7ad16..5619c373973 100644 --- a/packages/electron-builder/src/targets/nsis.ts +++ b/packages/electron-builder/src/targets/nsis.ts @@ -1,5 +1,5 @@ import BluebirdPromise from "bluebird-lst" -import { Arch, Platform, Target } from "electron-builder-core" +import { Arch, Target } from "electron-builder-core" import { asArray, debug, doSpawn, exec, handleProcess, isEmptyOrSpaces, use } from "electron-builder-util" import { getBinFromBintray } from "electron-builder-util/out/binDownload" import { copyFile } from "electron-builder-util/out/fs" @@ -9,9 +9,8 @@ import * as path from "path" import sanitizeFileName from "sanitize-filename" import { v5 as uuid5 } from "uuid-1345" import { getPlatformIconFileName } from "../metadata" -import { NsisOptions, NsisWebOptions } from "../options/winOptions" +import { NsisOptions } from "../options/winOptions" import { normalizeExt } from "../platformPackager" -import { computeDownloadUrl, getPublishConfigs, getPublishConfigsForUpdateInfo } from "../publish/PublishManager" import { getSignVendorPath } from "../windowsCodeSign" import { WinPackager } from "../winPackager" import { archive } from "./archive" @@ -24,13 +23,13 @@ const nsisResourcePathPromise = getBinFromBintray("nsis-resources", "3.0.0", "cd const USE_NSIS_BUILT_IN_COMPRESSOR = false export default class NsisTarget extends Target { - private readonly options: NsisOptions + protected readonly options: NsisOptions private archs: Map = new Map() private readonly nsisTemplatesDir = path.join(__dirname, "..", "..", "templates", "nsis") - constructor(private packager: WinPackager, readonly outDir: string, targetName: string) { + constructor(protected readonly packager: WinPackager, readonly outDir: string, targetName: string) { super(targetName) let options = this.packager.config.nsis || Object.create(null) @@ -74,13 +73,23 @@ export default class NsisTarget extends Target { } } + protected get installerFilenamePattern(): string { + return "${productName} " + (this.isPortable ? "" : "Setup ") + "${version}.${ext}" + } + + private get isPortable() { + return this.name === "portable" + } + private async buildInstaller(filesToDelete: Array): Promise { + const isPortable = this.isPortable + const packager = this.packager const appInfo = packager.appInfo const version = appInfo.version const options = this.options - const installerFilename = packager.expandArtifactNamePattern(options, "exe", null, "${productName} " + (this.isWebInstaller ? "Web " : "") + "Setup ${version}.${ext}") - const iconPath = await packager.getResource(options.installerIcon, "installerIcon.ico") || await packager.getIconPath() + const installerFilename = packager.expandArtifactNamePattern(options, "exe", null, this.installerFilenamePattern) + const iconPath = (isPortable ? null : await packager.getResource(options.installerIcon, "installerIcon.ico")) || await packager.getIconPath() const oneClick = options.oneClick !== false const installerPath = path.join(this.outDir, installerFilename) @@ -105,9 +114,21 @@ export default class NsisTarget extends Target { defines.APP_PRODUCT_FILENAME = appInfo.productFilename } + const commands: any = { + OutFile: `"${installerPath}"`, + VIProductVersion: appInfo.versionInWeirdWindowsForm, + VIAddVersionKey: this.computeVersionKey(), + Unicode: this.isUnicodeEnabled, + } + if (iconPath != null) { - defines.MUI_ICON = iconPath - defines.MUI_UNICON = iconPath + if (isPortable) { + commands.Icon = iconPath + } + else { + defines.MUI_ICON = iconPath + defines.MUI_UNICON = iconPath + } } if (this.archs.size === 1 && USE_NSIS_BUILT_IN_COMPRESSOR) { @@ -128,32 +149,9 @@ export default class NsisTarget extends Target { }) } - await this.configureDefines(oneClick, defines) - - if (this.isWebInstaller) { - let appPackageUrl = (options).appPackageUrl - if (appPackageUrl == null) { - const publishConfigs = await getPublishConfigsForUpdateInfo(packager, await getPublishConfigs(packager, this.options, false)) - if (publishConfigs == null || publishConfigs.length === 0) { - throw new Error("Cannot compute app package download URL") - } - - appPackageUrl = computeDownloadUrl(publishConfigs[0], null, packager.appInfo.version, { - os: Platform.WINDOWS.buildConfigurationKey, - arch: "" - }) - - defines.APP_PACKAGE_URL_IS_INCOMLETE = null - } - - defines.APP_PACKAGE_URL = appPackageUrl - } - - const commands: any = { - OutFile: `"${installerPath}"`, - VIProductVersion: appInfo.versionInWeirdWindowsForm, - VIAddVersionKey: this.computeVersionKey(), - Unicode: this.isUnicodeEnabled, + this.configureDefinesForAllTypeOfInstaller(defines) + if (!isPortable) { + await this.configureDefines(oneClick, defines) } if (packager.config.compression === "store") { @@ -173,21 +171,26 @@ export default class NsisTarget extends Target { return } - await this.executeMakensis(defines, commands, true, await this.computeScript(defines, commands, installerPath)) + const script = isPortable ? await readFile(path.join(this.nsisTemplatesDir, "portable.nsi"), "utf8") : await this.computeScriptAndSignUninstaller(defines, commands, installerPath) + await this.executeMakensis(defines, commands, await this.computeFinalScript(script, true)) await packager.sign(installerPath) - packager.dispatchArtifactCreated(installerPath, this, `${packager.appInfo.name}-${this.isWebInstaller ? "Web-" : ""}Setup-${version}.exe`) + packager.dispatchArtifactCreated(installerPath, this, this.generateGitHubInstallerName()) + } + + protected generateGitHubInstallerName() { + return `${this.packager.appInfo.name}-${this.isPortable ? "" : "Setup-"}${this.packager.appInfo.version}.exe` } private get isUnicodeEnabled() { return this.options.unicode == null ? true : this.options.unicode } - private get isWebInstaller(): boolean { - return this.name === "nsis-web" + protected get isWebInstaller(): boolean { + return false } - private async computeScript(defines: any, commands: any, installerPath: string) { + private async computeScriptAndSignUninstaller(defines: any, commands: any, installerPath: string) { const packager = this.packager const customScriptPath = await packager.getResource(this.options.script, "installer.nsi") const script = await readFile(customScriptPath || path.join(this.nsisTemplatesDir, "installer.nsi"), "utf8") @@ -202,7 +205,7 @@ export default class NsisTarget extends Target { const isWin = process.platform === "win32" defines.BUILD_UNINSTALLER = null defines.UNINSTALLER_OUT_FILE = isWin ? uninstallerPath : path.win32.join("Z:", uninstallerPath) - await this.executeMakensis(defines, commands, false, script) + await this.executeMakensis(defines, commands, await this.computeFinalScript(script, false)) await exec(isWin ? installerPath : "wine", isWin ? [] : [installerPath]) await packager.sign(uninstallerPath, " Signing NSIS uninstaller") @@ -229,7 +232,7 @@ export default class NsisTarget extends Target { return versionKey } - private async configureDefines(oneClick: boolean, defines: any) { + protected async configureDefines(oneClick: boolean, defines: any) { const packager = this.packager const options = this.options @@ -278,14 +281,6 @@ export default class NsisTarget extends Target { defines.allowToChangeInstallationDirectory = null } - if (!this.isWebInstaller && defines.APP_BUILD_DIR == null) { - if (options.useZip) { - defines.ZIP_COMPRESSION = null - } - - defines.COMPRESSION_METHOD = options.useZip ? "zip" : "7z" - } - if (options.menuCategory != null) { const menu = sanitizeFileName(options.menuCategory === true ? packager.appInfo.companyName : options.menuCategory) if (!isEmptyOrSpaces(menu)) { @@ -304,7 +299,19 @@ export default class NsisTarget extends Target { } } - private async executeMakensis(defines: any, commands: any, isInstaller: boolean, originalScript: string) { + private configureDefinesForAllTypeOfInstaller(defines: any) { + const options = this.options + + if (!this.isWebInstaller && defines.APP_BUILD_DIR == null) { + if (options.useZip) { + defines.ZIP_COMPRESSION = null + } + + defines.COMPRESSION_METHOD = options.useZip ? "zip" : "7z" + } + } + + private async executeMakensis(defines: any, commands: any, script: string) { const args: Array = (this.options.warningsAsErrors === false) ? [] : ["-WX"] for (const name of Object.keys(defines)) { const value = defines[name] @@ -330,20 +337,39 @@ export default class NsisTarget extends Target { args.push("-") - const binDir = process.platform === "darwin" ? "mac" : (process.platform === "win32" ? "Bin" : "linux") + if (debug.enabled) { + process.stdout.write("\n\nNSIS script:\n\n" + script + "\n\n---\nEnd of NSIS script.\n\n") + } + const nsisPath = await nsisPathPromise + await new BluebirdPromise((resolve, reject) => { + const command = path.join(nsisPath, process.platform === "darwin" ? "mac" : (process.platform === "win32" ? "Bin" : "linux"), process.platform === "win32" ? "makensis.exe" : "makensis") + const childProcess = doSpawn(command, args, { + // we use NSIS_CONFIG_CONST_DATA_PATH=no to build makensis on Linux, but in any case it doesn't use stubs as MacOS/Windows version, so, we explicitly set NSISDIR + env: Object.assign({}, process.env, {NSISDIR: nsisPath}), + cwd: this.nsisTemplatesDir, + }, true) + handleProcess("close", childProcess, command, resolve, reject) + + childProcess.stdin.end(script) + }) + } + private async computeFinalScript(originalScript: string, isInstaller: boolean) { let scriptHeader = `!addincludedir "${path.win32.join(__dirname, "..", "..", "templates", "nsis", "include")}"\n` const pluginArch = this.isUnicodeEnabled ? "x86-unicode" : "x86-ansi" scriptHeader += `!addplugindir /${pluginArch} "${path.join(await nsisResourcePathPromise, "plugins", pluginArch)}"\n` - let script = originalScript + if (this.isPortable) { + return scriptHeader + originalScript + } + const packager = this.packager const customInclude = await packager.getResource(this.options.include, "installer.nsh") if (customInclude != null) { scriptHeader += `!addincludedir "${packager.buildResourcesDir}"\n` scriptHeader += `!addplugindir /${pluginArch} "${path.join(packager.buildResourcesDir, pluginArch)}"\n` - script = `\n!include "${customInclude}"\n\n${script}` + scriptHeader += `!include "${customInclude}"\n\n` } const fileAssociations = packager.fileAssociations @@ -353,7 +379,7 @@ export default class NsisTarget extends Target { throw new Error(`Please set perMachine to true — file associations works on Windows only if installed for all users`) } - script = "!include FileAssociation.nsh\n" + script + scriptHeader += "!include FileAssociation.nsh\n" if (isInstaller) { let registerFileAssociationsScript = "" for (const item of fileAssociations) { @@ -373,7 +399,7 @@ export default class NsisTarget extends Target { registerFileAssociationsScript += ` !insertmacro APP_ASSOCIATE "${ext}" "${item.name || ext}" "${item.description || ""}" ${icon} ${commandText} ${command}\n` } } - script = `!macro registerFileAssociations\n${registerFileAssociationsScript}!macroend\n${script}` + scriptHeader += `!macro registerFileAssociations\n${registerFileAssociationsScript}!macroend\n` } else { let unregisterFileAssociationsScript = "" @@ -382,26 +408,10 @@ export default class NsisTarget extends Target { unregisterFileAssociationsScript += ` !insertmacro APP_UNASSOCIATE "${normalizeExt(ext)}" "${item.name || ext}"\n` } } - script = `!macro unregisterFileAssociations\n${unregisterFileAssociationsScript}!macroend\n${script}` + scriptHeader += `!macro unregisterFileAssociations\n${unregisterFileAssociationsScript}!macroend\n` } } - script = scriptHeader + script - - if (debug.enabled) { - process.stdout.write("\n\nNSIS script:\n\n" + script + "\n\n---\nEnd of NSIS script.\n\n") - } - - await new BluebirdPromise((resolve, reject) => { - const command = path.join(nsisPath, binDir, process.platform === "win32" ? "makensis.exe" : "makensis") - const childProcess = doSpawn(command, args, { - // we use NSIS_CONFIG_CONST_DATA_PATH=no to build makensis on Linux, but in any case it doesn't use stubs as MacOS/Windows version, so, we explicitly set NSISDIR - env: Object.assign({}, process.env, {NSISDIR: nsisPath}), - cwd: this.nsisTemplatesDir, - }, true) - handleProcess("close", childProcess, command, resolve, reject) - - childProcess.stdin.end(script) - }) + return scriptHeader + originalScript } } \ No newline at end of file diff --git a/packages/electron-builder/src/winPackager.ts b/packages/electron-builder/src/winPackager.ts index 42bc24e940f..87c1d572dbb 100644 --- a/packages/electron-builder/src/winPackager.ts +++ b/packages/electron-builder/src/winPackager.ts @@ -102,8 +102,10 @@ export class WinPackager extends PlatformPackager { const targetClass: typeof NsisTarget | typeof AppXTarget | null = (() => { switch (name) { case "nsis": - case "nsis-web": + case "portable": return require("./targets/nsis").default + case "nsis-web": + return require("./targets/WebInstaller").default case "squirrel": try { diff --git a/packages/electron-builder/templates/nsis/common.nsh b/packages/electron-builder/templates/nsis/common.nsh index 3d0c2703f9c..1be8009c84b 100644 --- a/packages/electron-builder/templates/nsis/common.nsh +++ b/packages/electron-builder/templates/nsis/common.nsh @@ -36,67 +36,6 @@ Name "${PRODUCT_NAME}" Quit !macroend -!ifndef BUILD_UNINSTALLER -Function GetInQuotes - Exch $R0 - Push $R1 - Push $R2 - Push $R3 - - StrCpy $R2 -1 - IntOp $R2 $R2 + 1 - StrCpy $R3 $R0 1 $R2 - StrCmp $R3 "" 0 +3 - StrCpy $R0 "" - Goto Done - StrCmp $R3 '"' 0 -5 - - IntOp $R2 $R2 + 1 - StrCpy $R0 $R0 "" $R2 - - StrCpy $R2 0 - IntOp $R2 $R2 + 1 - StrCpy $R3 $R0 1 $R2 - StrCmp $R3 "" 0 +3 - StrCpy $R0 "" - Goto Done - StrCmp $R3 '"' 0 -5 - - StrCpy $R0 $R0 $R2 - Done: - - Pop $R3 - Pop $R2 - Pop $R1 - Exch $R0 -FunctionEnd - -Function GetFileParent - Exch $R0 - Push $R1 - Push $R2 - Push $R3 - - StrCpy $R1 0 - StrLen $R2 $R0 - - loop: - IntOp $R1 $R1 + 1 - IntCmp $R1 $R2 get 0 get - StrCpy $R3 $R0 1 -$R1 - StrCmp $R3 "\" get - Goto loop - - get: - StrCpy $R0 $R0 -$R1 - - Pop $R3 - Pop $R2 - Pop $R1 - Exch $R0 -FunctionEnd -!endif - !macro setLinkVars StrCpy $desktopLink "$DESKTOP\${PRODUCT_FILENAME}.lnk" !ifdef MENU_FILENAME @@ -112,4 +51,39 @@ FunctionEnd ${GetOptions} $R9 "--updated" $R8 IfErrors `${_f}` `${_t}` !macroend -!define Updated `"" Updated ""` \ No newline at end of file +!define Updated `"" Updated ""` + +!macro extractEmbeddedAppPackage + !ifdef COMPRESS + SetCompress off + !endif + + !ifdef APP_32 + File /oname=$PLUGINSDIR\app-32.${COMPRESSION_METHOD} "${APP_32}" + !endif + !ifdef APP_64 + File /oname=$PLUGINSDIR\app-64.${COMPRESSION_METHOD} "${APP_64}" + !endif + + !ifdef COMPRESS + SetCompress "${COMPRESS}" + !endif + + !ifdef APP_64 + ${if} ${RunningX64} + !insertmacro doExtractEmbeddedAppPackage "64" + ${else} + !insertmacro doExtractEmbeddedAppPackage "32" + ${endif} + !else + !insertmacro doExtractEmbeddedAppPackage "32" + !endif +!macroend + +!macro doExtractEmbeddedAppPackage ARCH + !ifdef ZIP_COMPRESSION + nsisunz::Unzip "$PLUGINSDIR\app-${ARCH}.zip" "$INSTDIR" + !else + Nsis7z::Extract "$PLUGINSDIR\app-${ARCH}.7z" + !endif +!macroend \ No newline at end of file diff --git a/packages/electron-builder/templates/nsis/installSection.nsh b/packages/electron-builder/templates/nsis/installSection.nsh index 92d018058b6..80420de75b0 100644 --- a/packages/electron-builder/templates/nsis/installSection.nsh +++ b/packages/electron-builder/templates/nsis/installSection.nsh @@ -71,41 +71,6 @@ WriteRegDWORD SHCTX "${UNINSTALL_REGISTRY_KEY}" "EstimatedSize" "$0" !macroend -!macro doExtractEmbeddedAppPackage ARCH - !ifdef ZIP_COMPRESSION - nsisunz::Unzip "$PLUGINSDIR\app-${ARCH}.zip" "$INSTDIR" - !else - Nsis7z::Extract "$PLUGINSDIR\app-${ARCH}.7z" - !endif -!macroend - -!macro extractEmbeddedAppPackage - !ifdef COMPRESS - SetCompress off - !endif - - !ifdef APP_32 - File /oname=$PLUGINSDIR\app-32.${COMPRESSION_METHOD} "${APP_32}" - !endif - !ifdef APP_64 - File /oname=$PLUGINSDIR\app-64.${COMPRESSION_METHOD} "${APP_64}" - !endif - - !ifdef COMPRESS - SetCompress "${COMPRESS}" - !endif - - !ifdef APP_64 - ${if} ${RunningX64} - !insertmacro doExtractEmbeddedAppPackage "64" - ${else} - !insertmacro doExtractEmbeddedAppPackage "32" - ${endif} - !else - !insertmacro doExtractEmbeddedAppPackage "32" - !endif -!macroend - InitPluginsDir !ifdef HEADER_ICO diff --git a/packages/electron-builder/templates/nsis/installUtil.nsh b/packages/electron-builder/templates/nsis/installUtil.nsh new file mode 100644 index 00000000000..59ff5d33175 --- /dev/null +++ b/packages/electron-builder/templates/nsis/installUtil.nsh @@ -0,0 +1,58 @@ +Function GetInQuotes + Exch $R0 + Push $R1 + Push $R2 + Push $R3 + + StrCpy $R2 -1 + IntOp $R2 $R2 + 1 + StrCpy $R3 $R0 1 $R2 + StrCmp $R3 "" 0 +3 + StrCpy $R0 "" + Goto Done + StrCmp $R3 '"' 0 -5 + + IntOp $R2 $R2 + 1 + StrCpy $R0 $R0 "" $R2 + + StrCpy $R2 0 + IntOp $R2 $R2 + 1 + StrCpy $R3 $R0 1 $R2 + StrCmp $R3 "" 0 +3 + StrCpy $R0 "" + Goto Done + StrCmp $R3 '"' 0 -5 + + StrCpy $R0 $R0 $R2 + Done: + + Pop $R3 + Pop $R2 + Pop $R1 + Exch $R0 +FunctionEnd + +Function GetFileParent + Exch $R0 + Push $R1 + Push $R2 + Push $R3 + + StrCpy $R1 0 + StrLen $R2 $R0 + + loop: + IntOp $R1 $R1 + 1 + IntCmp $R1 $R2 get 0 get + StrCpy $R3 $R0 1 -$R1 + StrCmp $R3 "\" get + Goto loop + + get: + StrCpy $R0 $R0 -$R1 + + Pop $R3 + Pop $R2 + Pop $R1 + Exch $R0 +FunctionEnd \ No newline at end of file diff --git a/packages/electron-builder/templates/nsis/installer.nsi b/packages/electron-builder/templates/nsis/installer.nsi index 7edf4690b7d..578e62a7acb 100644 --- a/packages/electron-builder/templates/nsis/installer.nsi +++ b/packages/electron-builder/templates/nsis/installer.nsi @@ -58,6 +58,10 @@ Function .onInit !endif FunctionEnd +!ifndef BUILD_UNINSTALLER + !include "installUtil.nsh" +!endif + Section "install" !ifndef BUILD_UNINSTALLER !include "installSection.nsh" diff --git a/packages/electron-builder/templates/nsis/portable.nsi b/packages/electron-builder/templates/nsis/portable.nsi new file mode 100644 index 00000000000..04ea4bd0838 --- /dev/null +++ b/packages/electron-builder/templates/nsis/portable.nsi @@ -0,0 +1,23 @@ +!include "common.nsh" + +WindowIcon Off +AutoCloseWindow True +RequestExecutionLevel user + +SilentInstall silent + +Function .onInit + !insertmacro check64BitAndSetRegView +FunctionEnd + +Section + StrCpy $INSTDIR $PLUGINSDIR\app + SetOutPath $INSTDIR + + !insertmacro extractEmbeddedAppPackage + + ExecWait "$INSTDIR\${APP_EXECUTABLE_FILENAME}" + + SetOutPath $PLUGINSDIR + RMDir /r $INSTDIR +SectionEnd \ No newline at end of file diff --git a/test/out/windows/__snapshots__/installerTest.js.snap b/test/out/windows/__snapshots__/installerTest.js.snap index 9143f8d492e..fd99e6283c3 100644 --- a/test/out/windows/__snapshots__/installerTest.js.snap +++ b/test/out/windows/__snapshots__/installerTest.js.snap @@ -51,3 +51,15 @@ Array [ "TestApp-Setup-1.1.0.exe", ] `; + +exports[`portable 1`] = ` +Array [ + "Test App ßW 1.1.0.exe", +] +`; + +exports[`portable 2`] = ` +Array [ + "TestApp-1.1.0.exe", +] +`; diff --git a/test/out/windows/__snapshots__/oneClickInstallerTest.js.snap b/test/out/windows/__snapshots__/oneClickInstallerTest.js.snap index 1bb5b9359e4..8cd7191513a 100644 --- a/test/out/windows/__snapshots__/oneClickInstallerTest.js.snap +++ b/test/out/windows/__snapshots__/oneClickInstallerTest.js.snap @@ -97,6 +97,6 @@ Array [ exports[`web installer 2`] = ` Array [ - "TestApp-Web-Setup-1.1.0.exe", + "TestApp-WebSetup-1.1.0.exe", ] `; diff --git a/test/src/BuildTest.ts b/test/src/BuildTest.ts index f9f50303dc6..9c2f67d7bab 100644 --- a/test/src/BuildTest.ts +++ b/test/src/BuildTest.ts @@ -20,7 +20,7 @@ test("cli", async () => { return normalizeOptions(yargs.parse(input.split(" "))) } - function expected(opt: BuildOptions): any { + function expected(opt: BuildOptions): object { return Object.assign({ publish: undefined, draft: undefined, diff --git a/test/src/mac/masTest.ts b/test/src/mac/masTest.ts index 36f4c65537a..7f7c36a39c7 100644 --- a/test/src/mac/masTest.ts +++ b/test/src/mac/masTest.ts @@ -16,7 +16,7 @@ else if (process.env.CSC_KEY_PASSWORD == null) { } test("mas", createMacTargetTest(["mas"])) -test("mas and 7z", createMacTargetTest(["mas", "7z"])) +test.ifAll("mas and 7z", createMacTargetTest(["mas", "7z"])) test("custom mas", () => { let platformPackager: CheckingMacPackager = null diff --git a/test/src/windows/installerTest.ts b/test/src/windows/installerTest.ts index 1cf3e710768..d4cec2e9bf1 100644 --- a/test/src/windows/installerTest.ts +++ b/test/src/windows/installerTest.ts @@ -113,4 +113,12 @@ test.ifAll("allowToChangeInstallationDirectory", app({ expect(updateInfo).toMatchSnapshot() await doTest(context.outDir, false) } +})) + +test.ifNotCiMac("portable", app({ + targets: Platform.WINDOWS.createTarget(["portable"]), + config: { + nsis: { + } + } })) \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 33a74213af5..647599e27c2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1088,16 +1088,16 @@ ecc-jsbn@~0.1.1: dependencies: jsbn "~0.1.0" -electron-download-tf@3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/electron-download-tf/-/electron-download-tf-3.2.0.tgz#690443d2e7d068f000358f86b9d504303711ad10" +electron-download-tf@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/electron-download-tf/-/electron-download-tf-4.0.0.tgz#b2cd6af916ea94cc3723d8931961cb537fb2efff" dependencies: - debug "^2.6.0" + debug "^2.6.1" fs-extra "^2.0.0" minimist "^1.2.0" nugget "^2.0.1" path-exists "^3.0.0" - rc "^1.1.6" + rc "^1.1.7" semver "^5.3.0" sumchecker "^2.0.1" @@ -2799,7 +2799,7 @@ randomatic@^1.1.3: is-number "^2.0.2" kind-of "^3.0.2" -rc@^1.0.1, rc@^1.1.6: +rc@^1.0.1, rc@^1.1.6, rc@^1.1.7: version "1.1.7" resolved "https://registry.yarnpkg.com/rc/-/rc-1.1.7.tgz#c5ea564bb07aff9fd3a5b32e906c1d3a65940fea" dependencies: