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: