diff --git a/.idea/dictionaries/develar.xml b/.idea/dictionaries/develar.xml
index 1b86cb090f6..c044210381a 100644
--- a/.idea/dictionaries/develar.xml
+++ b/.idea/dictionaries/develar.xml
@@ -53,6 +53,7 @@
isbinaryfile
joliet
keyserver
+ lcid
libappindicator
libexec
libgcrypt
diff --git a/docker/nsis-linux.sh b/docker/nsis-linux.sh
deleted file mode 100644
index cbdf0f7b67b..00000000000
--- a/docker/nsis-linux.sh
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/usr/bin/env bash
-
-# docker run -ti --rm -v $PWD:/tmp/nsis buildpack-deps:trusty
-
-mkdir -p /tmp/scons && curl -L http://prdownloads.sourceforge.net/scons/scons-local-2.5.0.tar.gz | tar -xz -C /tmp/scons
-mkdir -p /tmp/nsis && curl -L https://sourceforge.net/projects/nsis/files/NSIS%203/3.0/nsis-3.0-src.tar.bz2/download | tar -xj -C /tmp/nsis --strip-components 1 && cd /tmp/nsis
-
-python /tmp/scons/scons.py STRIP=0 SKIPSTUBS=all SKIPPLUGINS=all SKIPUTILS=all SKIPMISC=all NSIS_CONFIG_CONST_DATA_PATH=no makensis
\ No newline at end of file
diff --git a/docker/nsis.sh b/docker/nsis.sh
deleted file mode 100755
index a192f48203f..00000000000
--- a/docker/nsis.sh
+++ /dev/null
@@ -1,50 +0,0 @@
-#!/usr/bin/env bash
-set -e
-
-rm -rf Docs
-rm -rf NSIS.chm
-rm -rf Examples
-rm -rf Plugins/x86-ansi
-rm -f makensisw.exe
-
-# nsProcess plugin
-curl -L http://nsis.sourceforge.net/mediawiki/images/1/18/NsProcess.zip > a.zip
-7za x a.zip -oa
-mv a/Plugin/nsProcessW.dll Plugins/x86-unicode/nsProcess.dll
-mv a/Include/nsProcess.nsh Include/nsProcess.nsh
-unlink a.zip
-rm -rf a
-
-# UAC plugin
-curl -L http://nsis.sourceforge.net/mediawiki/images/8/8f/UAC.zip > a.zip
-7za x a.zip -oa
-mv a/Plugins/x86-unicode/UAC.dll Plugins/x86-unicode/UAC.dll
-mv a/UAC.nsh Include/UAC.nsh
-unlink a.zip
-rm -rf a
-
-# WinShell
-curl -L http://nsis.sourceforge.net/mediawiki/images/5/54/WinShell.zip > a.zip
-7za x a.zip -oa
-mv a/Plugins/x86-unicode/WinShell.dll Plugins/x86-unicode/WinShell.dll
-unlink a.zip
-rm -rf a
-
-# 7z
-curl -L http://nsis.sourceforge.net/mediawiki/images/9/93/Nsis7z.zip > a.zip
-7za x a.zip -oa
-mv a/Plugins/x86-unicode/nsis7z.dll Plugins/x86-unicode/nsis7z.dll
-unlink a.zip
-rm -rf a
-
-# http://nsis.sourceforge.net/SpiderBanner_plug-in
-curl -L http://nsis.sourceforge.net/mediawiki/images/4/4c/SpiderBanner_plugin.zip > a.zip
-7za x a.zip -oa
-mv a/Plugins/x86-unicode/SpiderBanner.dll Plugins/x86-unicode/SpiderBanner.dll
-unlink a.zip
-rm -rf a
-
-
-dir=${PWD##*/}
-rm -rf ../${dir}.7z
-7za a -m0=lzma2 -mx=9 -mfb=64 -md=64m -ms=on ../${dir}.7z .
diff --git a/docs/Options.md b/docs/Options.md
index 4166ccccea6..c78d02ed73d 100644
--- a/docs/Options.md
+++ b/docs/Options.md
@@ -142,6 +142,7 @@ See [NSIS target notes](https://github.com/electron-userland/electron-builder/wi
| installerHeaderIcon | *one-click installer only.* The path to header icon (above the progress bar), relative to the project directory. Defaults to `build/installerHeaderIcon.ico` or application icon.
| include | The path to NSIS include script to customize installer. Defaults to `build/installer.nsh`. See [Custom NSIS script](https://github.com/electron-userland/electron-builder/wiki/NSIS#custom-nsis-script).
| script | The path to NSIS script to customize installer. Defaults to `build/installer.nsi`. See [Custom NSIS script](https://github.com/electron-userland/electron-builder/wiki/NSIS#custom-nsis-script).
+| language | * Hex LCID, defaults to `1033`(`English - United States`, see https://msdn.microsoft.com/en-au/goglobal/bb964664.aspx?f=255&MSPPError=-2147217396).
### `.build.linux`
diff --git a/src/metadata.ts b/src/metadata.ts
index 58f54449e4b..0424f6f6b98 100755
--- a/src/metadata.ts
+++ b/src/metadata.ts
@@ -416,6 +416,11 @@ export interface NsisOptions {
The path to NSIS script to customize installer. Defaults to `build/installer.nsi`. See [Custom NSIS script](https://github.com/electron-userland/electron-builder/wiki/NSIS#custom-nsis-script).
*/
readonly script?: string | null
+
+ /*
+ * Hex LCID, defaults to `1033`(`English - United States`, see https://msdn.microsoft.com/en-au/goglobal/bb964664.aspx?f=255&MSPPError=-2147217396).
+ */
+ readonly language?: string | null
}
/*
diff --git a/src/targets/nsis.ts b/src/targets/nsis.ts
index a64b500442d..7d74bb0bac8 100644
--- a/src/targets/nsis.ts
+++ b/src/targets/nsis.ts
@@ -14,9 +14,9 @@ import semver = require("semver")
//noinspection JSUnusedLocalSymbols
const __awaiter = require("../util/awaiter")
-const NSIS_VERSION = "3.0.0"
+const NSIS_VERSION = "3.0.1"
//noinspection SpellCheckingInspection
-const NSIS_SHA2 = "7741089f3ca13de879f87836156ef785eab49844cacbeeabaeaefd1ade325ee7"
+const NSIS_SHA2 = "23280f66c07c923da6f29a3c318377720c8ecd7af4de3755256d1ecf60d07f74"
//noinspection SpellCheckingInspection
const ELECTRON_BUILDER_NS_UUID = "50e065bc-3134-11e6-9bab-38c9862bdaf3"
@@ -119,13 +119,14 @@ export default class NsisTarget extends Target {
// Error: invalid VIProductVersion format, should be X.X.X.X
// so, we must strip beta
const parsedVersion = new semver.SemVer(appInfo.version)
+ const localeId = this.options.language || "1033"
const versionKey = [
- `ProductName "${appInfo.productName}"`,
- `ProductVersion "${appInfo.version}"`,
- `CompanyName "${appInfo.companyName}"`,
- `LegalCopyright "${appInfo.copyright}"`,
- `FileDescription "${appInfo.description}"`,
- `FileVersion "${appInfo.buildVersion}"`,
+ `/LANG=${localeId} ProductName "${appInfo.productName}"`,
+ `/LANG=${localeId} ProductVersion "${appInfo.version}"`,
+ `/LANG=${localeId} CompanyName "${appInfo.companyName}"`,
+ `/LANG=${localeId} LegalCopyright "${appInfo.copyright}"`,
+ `/LANG=${localeId} FileDescription "${appInfo.description}"`,
+ `/LANG=${localeId} FileVersion "${appInfo.buildVersion}"`,
]
use(this.packager.platformSpecificBuildOptions.legalTrademarks, it => versionKey.push(`LegalTrademarks "${it}"`))
diff --git a/templates/nsis/install.nsh b/templates/nsis/install.nsh
index d985a7d619b..572254bca58 100644
--- a/templates/nsis/install.nsh
+++ b/templates/nsis/install.nsh
@@ -23,6 +23,14 @@ ${if} $R0 != ""
ExecWait "$R0 /S /KEEP_APP_DATA"
${endif}
+${if} $installMode == "all"
+ # remove per-user installation
+ ReadRegStr $R0 HKEY_CURRENT_USER "${UNINSTALL_REGISTRY_KEY}" UninstallString
+ ${if} $R0 != ""
+ ExecWait "$R0 /S /KEEP_APP_DATA"
+ ${endif}
+${endif}
+
RMDir /r $INSTDIR
SetOutPath $INSTDIR
@@ -68,12 +76,13 @@ WinShell::SetLnkAUMI "$desktopLink" "${APP_ID}"
!insertmacro customInstall
!endif
-${IfNot} ${Silent}
- !ifdef ONE_CLICK
- # otherwise app window will be in backround
- HideWindow
- !ifdef RUN_AFTER_FINISH
+!ifdef ONE_CLICK
+ !ifdef RUN_AFTER_FINISH
+ ${IfNot} ${Silent}
+ # otherwise app window will be in backround
+ HideWindow
Call StartApp
- !endif
+ ${EndIf}
!endif
-${EndIf}
\ No newline at end of file
+ Quit
+!endif
diff --git a/templates/nsis/oneClick.nsh b/templates/nsis/oneClick.nsh
index fed866a7a92..7c4178029ce 100644
--- a/templates/nsis/oneClick.nsh
+++ b/templates/nsis/oneClick.nsh
@@ -11,7 +11,6 @@
!endif
!endif
-AutoCloseWindow true
!insertmacro MUI_PAGE_INSTFILES
!ifdef BUILD_UNINSTALLER
!insertmacro MUI_UNPAGE_INSTFILES
diff --git a/test/src/helpers/wine.ts b/test/src/helpers/wine.ts
index 5802419e369..de2dc99e715 100644
--- a/test/src/helpers/wine.ts
+++ b/test/src/helpers/wine.ts
@@ -36,7 +36,7 @@ export class WineManager {
this.env = await this.winePreparePromise
}
- async exec(...args: Array) {
+ exec(...args: Array) {
return exec("wine", args, {env: this.env})
}
diff --git a/test/src/nsisTest.ts b/test/src/nsisTest.ts
index 0564891e7c3..f0b8eed88fe 100644
--- a/test/src/nsisTest.ts
+++ b/test/src/nsisTest.ts
@@ -14,7 +14,22 @@ import { WineManager, diff } from "./helpers/wine"
const __awaiter = require("out/util/awaiter")
const nsisTarget = Platform.WINDOWS.createTarget(["nsis"])
-test("one-click", app({targets: nsisTarget}, {useTempDir: true, signed: true}))
+
+test("one-click", app({
+ targets: Platform.WINDOWS.createTarget(["nsis"], Arch.ia32),
+ devMetadata: {
+ build: {
+ // wine creates incorrect filenames and registry entries for unicode, so, we use ASCII
+ productName: "TestApp",
+ }
+ }
+}, {
+ useTempDir: true,
+ signed: true,
+ packed: (projectDir, outDir) => {
+ return doTest(outDir, true)
+ }
+}))
test.ifDevOrLinuxCi("perMachine, no run after finish", app({
targets: Platform.WINDOWS.createTarget(["nsis"], Arch.ia32),
@@ -39,51 +54,56 @@ test.ifDevOrLinuxCi("perMachine, no run after finish", app({
let headerIconPath = path.join(projectDir, "build", "foo.ico")
return copy(getTestAsset("headerIcon.ico"), headerIconPath)
},
- packed: async (projectDir, outDir) => {
- if (process.env.CI != null) {
- return
- }
+ packed: (projectDir, outDir) => {
+ return doTest(outDir, false)
+ },
+}))
- const wine = new WineManager()
- await wine.prepare()
- const driveC = path.join(wine.wineDir, "drive_c")
- const driveCWindows = path.join(wine.wineDir, "drive_c", "windows")
- const perUserTempDir = path.join(wine.userDir, "Temp")
- const walkFilter = (it: string) => {
- return it !== driveCWindows && it !== perUserTempDir
- }
+async function doTest(outDir: string, perUser: boolean) {
+ if (process.env.DO_WINE !== "true") {
+ return BluebirdPromise.resolve()
+ }
- function listFiles() {
- return walk(driveC, null, walkFilter)
- }
+ const wine = new WineManager()
+ await wine.prepare()
+ const driveC = path.join(wine.wineDir, "drive_c")
+ const driveCWindows = path.join(wine.wineDir, "drive_c", "windows")
+ const perUserTempDir = path.join(wine.userDir, "Temp")
+ const walkFilter = (it: string) => {
+ return it !== driveCWindows && it !== perUserTempDir
+ }
- let fsBefore = await listFiles()
+ function listFiles() {
+ return walk(driveC, null, walkFilter)
+ }
- await wine.exec(path.join(outDir, "TestApp Setup 1.1.0.exe"), "/S")
+ let fsBefore = await listFiles()
- const appAsar = path.join(driveC, "Program Files", "TestApp", "resources", "app.asar")
- assertThat(JSON.parse(extractFile(appAsar, "package.json").toString())).hasProperties({
- name: "TestApp"
- })
+ await wine.exec(path.join(outDir, "TestApp Setup 1.1.0.exe"), "/S")
- let fsAfter = await listFiles()
+ const instDir = perUser ? path.join(wine.userDir, "Local Settings", "Application Data", "Programs") : path.join(driveC, "Program Files")
+ const appAsar = path.join(instDir, "TestApp", "resources", "app.asar")
+ assertThat(JSON.parse(extractFile(appAsar, "package.json").toString())).hasProperties({
+ name: "TestApp"
+ })
- let fsChanges = diff(fsBefore, fsAfter, driveC)
- assertThat(fsChanges.added).isEqualTo(nsisPerMachineInstall)
- assertThat(fsChanges.deleted).isEqualTo([])
+ let fsAfter = await listFiles()
- // run installer again to test uninstall
- const appDataFile = path.join(wine.userDir, "Application Data", "TestApp", "doNotDeleteMe")
- await outputFile(appDataFile, "app data must be not removed")
- fsBefore = await listFiles()
- await wine.exec(path.join(outDir, "TestApp Setup 1.1.0.exe"))
- fsAfter = await listFiles()
+ let fsChanges = diff(fsBefore, fsAfter, driveC)
+ assertThat(fsChanges.added).isEqualTo(nsisPerMachineInstall)
+ assertThat(fsChanges.deleted).isEqualTo([])
- fsChanges = diff(fsBefore, fsAfter, driveC)
- assertThat(fsChanges.added).isEqualTo([])
- assertThat(fsChanges.deleted).isEqualTo([])
- },
-}))
+ // run installer again to test uninstall
+ const appDataFile = path.join(wine.userDir, "Application Data", "TestApp", "doNotDeleteMe")
+ await outputFile(appDataFile, "app data must be not removed")
+ fsBefore = await listFiles()
+ await wine.exec(path.join(outDir, "TestApp Setup 1.1.0.exe", "/S"))
+ fsAfter = await listFiles()
+
+ fsChanges = diff(fsBefore, fsAfter, driveC)
+ assertThat(fsChanges.added).isEqualTo([])
+ assertThat(fsChanges.deleted).isEqualTo([])
+}
test.ifNotCiOsx("boring", app({
targets: nsisTarget,
@@ -91,6 +111,7 @@ test.ifNotCiOsx("boring", app({
build: {
nsis: {
oneClick: false,
+ language: "1031",
}
}
}