Skip to content

Commit

Permalink
feat(nsis): one-click installer automatically removes old version #621
Browse files Browse the repository at this point in the history
on auto-uninstall do not delete app data
  • Loading branch information
develar committed Aug 19, 2016
1 parent 2c3d8c2 commit 682ddde
Show file tree
Hide file tree
Showing 10 changed files with 285 additions and 31 deletions.
2 changes: 2 additions & 0 deletions .idea/dictionaries/develar.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions docker/wine/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ apt-get install -y --no-install-recommends wine1.8 mono-devel ca-certificates-mo
apt-get clean && rm -rf /var/lib/apt/lists/*

ENV WINEDEBUG -all,err+all
ENV WINEDLLOVERRIDES winemenubuilder.exe=d

RUN wineboot --init || true
8 changes: 5 additions & 3 deletions src/asarUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,20 @@ const MAX_FILE_REQUESTS = 32
const concurrency = {concurrency: MAX_FILE_REQUESTS}
const NODE_MODULES_PATTERN = path.sep + "node_modules" + path.sep

function walk(dirPath: string, consumer: (file: string, stat: Stats) => void, filter: (file: string) => boolean, addRootToResult?: boolean): BluebirdPromise<Array<string>> {
export function walk(dirPath: string, consumer?: (file: string, stat: Stats) => void, filter?: (file: string) => boolean, addRootToResult?: boolean): BluebirdPromise<Array<string>> {
return readdir(dirPath)
.then(names => {
return BluebirdPromise.map(names, name => {
const filePath = dirPath + path.sep + name
if (!filter(filePath)) {
if (filter != null && !filter(filePath)) {
return <any>null
}

return lstat(filePath)
.then((stat): any => {
consumer(filePath, stat)
if (consumer != null) {
consumer(filePath, stat)
}
if (stat.isDirectory()) {
return walk(filePath, consumer, filter, true)
}
Expand Down
23 changes: 18 additions & 5 deletions templates/nsis/install.nsh
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
InitPluginsDir

!ifdef HEADER_ICO
File /oname=$PLUGINSDIR\installerHeaderico.ico "${HEADER_ICO}"
!endif

${IfNot} ${Silent}
SetDetailsPrint none

Expand All @@ -12,16 +18,23 @@ ${endif}

!insertmacro CHECK_APP_RUNNING "install"

${if} $installMode == "all"
ReadRegStr $R0 HKEY_LOCAL_MACHINE "${UNINSTALL_REGISTRY_KEY}" UninstallString
${if} $R0 != ""
ExecWait "$R0 /S"
${endif}
ReadRegStr $R0 SHCTX "${UNINSTALL_REGISTRY_KEY}" UninstallString
${if} $R0 != ""
ExecWait "$R0 /S /KEEP_APP_DATA"
${endif}

RMDir /r $INSTDIR
SetOutPath $INSTDIR

SetCompress off
!ifdef APP_32
File /oname=$PLUGINSDIR\app-32.7z "${APP_32}"
!endif
!ifdef APP_64
File /oname=$PLUGINSDIR\app-64.7z "${APP_64}"
!endif
SetCompress "${COMPRESS}"

!ifdef APP_64
${If} ${RunningX64}
Nsis7z::Extract "$PLUGINSDIR\app-64.7z"
Expand Down
15 changes: 0 additions & 15 deletions templates/nsis/installer.nsi
Original file line number Diff line number Diff line change
Expand Up @@ -38,21 +38,6 @@ Function .onInit
${EndIf}
!endif

InitPluginsDir

SetCompress off
!ifdef APP_32
File /oname=$PLUGINSDIR\app-32.7z "${APP_32}"
!endif
!ifdef APP_64
File /oname=$PLUGINSDIR\app-64.7z "${APP_64}"
!endif
SetCompress "${COMPRESS}"

!ifdef HEADER_ICO
File /oname=$PLUGINSDIR\installerHeaderico.ico "${HEADER_ICO}"
!endif

!ifmacrodef customInit
!insertmacro customInit
!endif
Expand Down
5 changes: 3 additions & 2 deletions templates/nsis/uninstaller.nsh
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ Function un.onInit
FunctionEnd

Section "un.install"
SetAutoClose true

!ifndef ONE_CLICK
# for boring installer we check it here to show progress
!insertmacro CHECK_APP_RUNNING "uninstall"
Expand All @@ -44,6 +42,7 @@ Section "un.install"
# delete the installed files
RMDir /r $INSTDIR

ClearErrors
${GetParameters} $R0
${GetOptions} $R0 "/KEEP_APP_DATA" $R1
${If} ${Errors}
Expand All @@ -63,4 +62,6 @@ Section "un.install"
!ifmacrodef customUnInstall
!insertmacro customUnInstall
!endif

Quit
SectionEnd
87 changes: 86 additions & 1 deletion test/src/helpers/expectedContents.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import pathSorter = require("path-sort")

//noinspection SpellCheckingInspection
export const expectedLinuxContents = ["/",
"/opt/",
Expand Down Expand Up @@ -81,4 +83,87 @@ export const expectedWinContents = [
"TestApp.nuspec",
"[Content_Types].xml",
"_rels/.rels"
]
]

export const nsisPerMachineInstall = pathSorter([
"Program Files/TestApp",
"Program Files/TestApp/blink_image_resources_200_percent.pak",
"Program Files/TestApp/content_resources_200_percent.pak",
"Program Files/TestApp/content_shell.pak",
"Program Files/TestApp/d3dcompiler_47.dll",
"Program Files/TestApp/ffmpeg.dll",
"Program Files/TestApp/icudtl.dat",
"Program Files/TestApp/libEGL.dll",
"Program Files/TestApp/libGLESv2.dll",
"Program Files/TestApp/LICENSE",
"Program Files/TestApp/LICENSES.chromium.html",
"Program Files/TestApp/natives_blob.bin",
"Program Files/TestApp/node.dll",
"Program Files/TestApp/snapshot_blob.bin",
"Program Files/TestApp/TestApp.exe",
"Program Files/TestApp/ui_resources_200_percent.pak",
"Program Files/TestApp/Uninstall TestApp.exe",
"Program Files/TestApp/views_resources_200_percent.pak",
"Program Files/TestApp/xinput1_3.dll",
"Program Files/TestApp/resources",
"Program Files/TestApp/resources/app.asar",
"Program Files/TestApp/resources/electron.asar",
"Program Files/TestApp/resources/foo.ico",
"Program Files/TestApp/locales",
"Program Files/TestApp/locales/am.pak",
"Program Files/TestApp/locales/ar.pak",
"Program Files/TestApp/locales/bg.pak",
"Program Files/TestApp/locales/bn.pak",
"Program Files/TestApp/locales/ca.pak",
"Program Files/TestApp/locales/cs.pak",
"Program Files/TestApp/locales/da.pak",
"Program Files/TestApp/locales/de.pak",
"Program Files/TestApp/locales/el.pak",
"Program Files/TestApp/locales/en-GB.pak",
"Program Files/TestApp/locales/en-US.pak",
"Program Files/TestApp/locales/es-419.pak",
"Program Files/TestApp/locales/es.pak",
"Program Files/TestApp/locales/et.pak",
"Program Files/TestApp/locales/fa.pak",
"Program Files/TestApp/locales/fake-bidi.pak",
"Program Files/TestApp/locales/fi.pak",
"Program Files/TestApp/locales/fil.pak",
"Program Files/TestApp/locales/fr.pak",
"Program Files/TestApp/locales/gu.pak",
"Program Files/TestApp/locales/he.pak",
"Program Files/TestApp/locales/hi.pak",
"Program Files/TestApp/locales/hr.pak",
"Program Files/TestApp/locales/hu.pak",
"Program Files/TestApp/locales/id.pak",
"Program Files/TestApp/locales/it.pak",
"Program Files/TestApp/locales/ja.pak",
"Program Files/TestApp/locales/kn.pak",
"Program Files/TestApp/locales/ko.pak",
"Program Files/TestApp/locales/lt.pak",
"Program Files/TestApp/locales/lv.pak",
"Program Files/TestApp/locales/ml.pak",
"Program Files/TestApp/locales/mr.pak",
"Program Files/TestApp/locales/ms.pak",
"Program Files/TestApp/locales/nb.pak",
"Program Files/TestApp/locales/nl.pak",
"Program Files/TestApp/locales/pl.pak",
"Program Files/TestApp/locales/pt-BR.pak",
"Program Files/TestApp/locales/pt-PT.pak",
"Program Files/TestApp/locales/ro.pak",
"Program Files/TestApp/locales/ru.pak",
"Program Files/TestApp/locales/sk.pak",
"Program Files/TestApp/locales/sl.pak",
"Program Files/TestApp/locales/sr.pak",
"Program Files/TestApp/locales/sv.pak",
"Program Files/TestApp/locales/sw.pak",
"Program Files/TestApp/locales/ta.pak",
"Program Files/TestApp/locales/te.pak",
"Program Files/TestApp/locales/th.pak",
"Program Files/TestApp/locales/tr.pak",
"Program Files/TestApp/locales/uk.pak",
"Program Files/TestApp/locales/vi.pak",
"Program Files/TestApp/locales/zh-CN.pak",
"Program Files/TestApp/locales/zh-TW.pak",
"users/Public/Desktop/TestApp.lnk",
"users/Public/Start Menu/Programs/TestApp.lnk"
])
2 changes: 1 addition & 1 deletion test/src/helpers/fileAssert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ class Assertions {
}
}

function prettyDiff(actual: any, expected: any): string {
export function prettyDiff(actual: any, expected: any): string {
const diffJson2 = diffJson(expected, actual)
const diff = diffJson2.map(part => {
if (part.added) {
Expand Down
115 changes: 115 additions & 0 deletions test/src/helpers/wine.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { exec } from "out/util/util"
import { homedir } from "os"
import { emptyDir, readFile, writeFile, ensureDir } from "fs-extra-p"
import * as path from "path"
import { Promise as BluebirdPromise } from "bluebird"
import pathSorter = require("path-sort")
import { unlinkIfExists } from "out/util/util"

//noinspection JSUnusedLocalSymbols
const __awaiter = require("out/util/awaiter")

export class WineManager {
wineDir: string
private winePreparePromise: Promise<any> | null

private env: any

userDir: string

async prepare() {
if (this.env != null) {
return
}

this.wineDir = path.join(homedir(), "wine-test")

const env = process.env
const user = env.SUDO_USER || env.LOGNAME || env.USER || env.LNAME || env.USERNAME || (env.HOME === "/root" ? "root" : null)
if (user == null) {
throw new Error(`Cannot determinate user name: ${JSON.stringify(env, null, 2)}`)
}

this.userDir = path.join(this.wineDir, "drive_c", "users", user)

this.winePreparePromise = this.prepareWine(this.wineDir)
this.env = await this.winePreparePromise
}

async exec(...args: Array<string>) {
return exec("wine", args, {env: this.env})
}

async prepareWine(wineDir: string) {
await emptyDir(wineDir)
//noinspection SpellCheckingInspection
const env = Object.assign({}, process.env, {
WINEDLLOVERRIDES: "winemenubuilder.exe=d",
WINEPREFIX: wineDir
})

await exec("wineboot", ["--init"], {env: env})

// regedit often doesn't modify correctly
let systemReg = await readFile(path.join(wineDir, "system.reg"), "utf8")
systemReg = systemReg.replace('"CSDVersion"="Service Pack 3"', '"CSDVersion"=" "')
systemReg = systemReg.replace('"CurrentBuildNumber"="2600"', '"CurrentBuildNumber"="10240"')
systemReg = systemReg.replace('"CurrentVersion"="5.1"', '"CurrentVersion"="10.0"')
systemReg = systemReg.replace('"ProductName"="Microsoft Windows XP"', '"ProductName"="Microsoft Windows 10"')
systemReg = systemReg.replace('"CSDVersion"=dword:00000300', '"CSDVersion"=dword:00000000')
await writeFile(path.join(wineDir, "system.reg"), systemReg)

// remove links to host OS
const desktopDir = path.join(this.userDir, "Desktop")
await BluebirdPromise.all([
unlinkIfExists(desktopDir),
unlinkIfExists(path.join(this.userDir, "My Documents")),
unlinkIfExists(path.join(this.userDir, "My Music")),
unlinkIfExists(path.join(this.userDir, "My Pictures")),
unlinkIfExists(path.join(this.userDir, "My Videos")),
])

await ensureDir(desktopDir)
return env
}
}

enum ChangeType {
ADDED, REMOVED, NO_CHANGE
}

export function diff(oldList: Array<string>, newList: Array<string>, rootDir: string) {
const delta: any = {
added: [],
deleted: [],
}
const deltaMap = new Map<string, ChangeType>()
// const objHolder = new Set(oldList)
for (let item of oldList) {
deltaMap.set(item, ChangeType.REMOVED)
}

for (let item of newList) {
// objHolder.add(item)
const d = deltaMap.get(item)
if (d === ChangeType.REMOVED) {
deltaMap.set(item, ChangeType.NO_CHANGE)
}
else {
deltaMap.set(item, ChangeType.ADDED)
}
}

for (let [item, changeType] of deltaMap.entries()) {
if (changeType === ChangeType.REMOVED) {
delta.deleted.push(item.substring(rootDir.length + 1))
}
else if (changeType === ChangeType.ADDED) {
delta.added.push(item.substring(rootDir.length + 1))
}
}

delta.added = pathSorter(delta.added)
delta.deleted = pathSorter(delta.deleted)
return delta
}
Loading

0 comments on commit 682ddde

Please sign in to comment.