Skip to content

Commit 836a15c

Browse files
authored
feat: Check for \S Silent Flag when executing NSIS Uninstaller (electron-userland#9282)
1 parent 8e1c7ff commit 836a15c

3 files changed

Lines changed: 95 additions & 58 deletions

File tree

.changeset/hot-ravens-beg.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"app-builder-lib": patch
3+
---
4+
5+
NSIS: Fix non-utilized for Silent Flag in Uninstaller

packages/app-builder-lib/templates/nsis/uninstaller.nsh

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,13 @@ Function un.onInit
88

99
!insertmacro check64BitAndSetRegView
1010

11+
# Parse command line for /S flag and set silent mode
12+
${GetParameters} $R0
13+
${GetOptions} $R0 "/S" $R1
14+
${IfNot} ${Errors}
15+
SetSilent silent
16+
${EndIf}
17+
1118
${If} ${Silent}
1219
call un.checkAppRunning
1320
${else}

test/src/updater/blackboxUpdateTest.ts

Lines changed: 83 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -93,65 +93,8 @@ async function runTest(target: string, arch: Arch = Arch.x64) {
9393
const newAppDir = outDirs[1]
9494

9595
const dirPath = oldAppDir.dir
96-
let appPath: string
97-
9896
// Setup tests by installing the previous version
99-
if (target === "AppImage") {
100-
appPath = path.join(dirPath, `TestApp.AppImage`)
101-
} else if (target === "deb") {
102-
DebUpdater.installWithCommandRunner(
103-
"dpkg",
104-
path.join(dirPath, `TestApp.deb`),
105-
commandWithArgs => {
106-
execSync(commandWithArgs.join(" "), { stdio: "inherit" })
107-
},
108-
console
109-
)
110-
appPath = path.join("/opt", "TestApp", "TestApp")
111-
} else if (target === "rpm") {
112-
RpmUpdater.installWithCommandRunner(
113-
"zypper",
114-
path.join(dirPath, `TestApp.rpm`),
115-
commandWithArgs => {
116-
execSync(commandWithArgs.join(" "), { stdio: "inherit" })
117-
},
118-
console
119-
)
120-
appPath = path.join("/opt", "TestApp", "TestApp")
121-
} else if (target === "pacman") {
122-
PacmanUpdater.installWithCommandRunner(
123-
path.join(dirPath, `TestApp.pacman`),
124-
commandWithArgs => {
125-
execSync(commandWithArgs.join(" "), { stdio: "inherit" })
126-
},
127-
console
128-
)
129-
// execSync(`sudo pacman -Syyu --noconfirm`, { stdio: "inherit" })
130-
// execSync(`sudo pacman -U --noconfirm "${path.join(dirPath, `TestApp.pacman`)}"`, { stdio: "inherit" })
131-
appPath = path.join("/opt", "TestApp", "TestApp")
132-
} else if (process.platform === "win32") {
133-
// access installed app's location
134-
const localProgramsPath = path.join(process.env.LOCALAPPDATA || path.join(homedir(), "AppData", "Local"), "Programs", "TestApp")
135-
// this is to clear dev environment when not running on an ephemeral GH runner.
136-
// Reinstallation will otherwise fail due to "uninstall" message prompt, so we must uninstall first (hence the setTimeout delay)
137-
const uninstaller = path.join(localProgramsPath, "Uninstall TestApp.exe")
138-
if (existsSync(uninstaller)) {
139-
console.log("Uninstalling", uninstaller)
140-
execFileSync(uninstaller, [], { stdio: "inherit" })
141-
await new Promise(resolve => setTimeout(resolve, 5000))
142-
}
143-
144-
const installerPath = path.join(dirPath, "TestApp Setup.exe")
145-
console.log("Installing windows", installerPath)
146-
// Don't use /S for silent install as we lose stdout pipe
147-
execFileSync(installerPath, [], { stdio: "inherit" })
148-
149-
appPath = path.join(localProgramsPath, "TestApp.exe")
150-
} else if (process.platform === "darwin") {
151-
appPath = path.join(dirPath, `mac${getArchSuffix(arch)}`, `TestApp.app`, "Contents", "MacOS", "TestApp")
152-
} else {
153-
throw new Error(`Unsupported Update test target: ${target}`)
154-
}
97+
const appPath = await handleInitialInstallPerOS({ target, dirPath, arch })
15598

15699
if (!existsSync(appPath)) {
157100
throw new Error(`App not found: ${appPath}`)
@@ -176,6 +119,7 @@ async function runTest(target: string, arch: Arch = Arch.x64) {
176119
})
177120
// windows needs to release file locks, so a delay seems to be needed
178121
await new Promise(resolve => setTimeout(resolve, 1000))
122+
await handleCleanupPerOS({ target })
179123
await tmpDir.cleanup()
180124
}
181125

@@ -300,6 +244,87 @@ async function doBuild(
300244
}
301245
}
302246

247+
async function handleInitialInstallPerOS({ target, dirPath, arch }: { target: string; dirPath: string; arch: Arch }): Promise<string> {
248+
let appPath: string
249+
if (target === "AppImage") {
250+
appPath = path.join(dirPath, `TestApp.AppImage`)
251+
} else if (target === "deb") {
252+
DebUpdater.installWithCommandRunner(
253+
"dpkg",
254+
path.join(dirPath, `TestApp.deb`),
255+
commandWithArgs => {
256+
execSync(commandWithArgs.join(" "), { stdio: "inherit" })
257+
},
258+
console
259+
)
260+
appPath = path.join("/opt", "TestApp", "TestApp")
261+
} else if (target === "rpm") {
262+
RpmUpdater.installWithCommandRunner(
263+
"zypper",
264+
path.join(dirPath, `TestApp.rpm`),
265+
commandWithArgs => {
266+
execSync(commandWithArgs.join(" "), { stdio: "inherit" })
267+
},
268+
console
269+
)
270+
appPath = path.join("/opt", "TestApp", "TestApp")
271+
} else if (target === "pacman") {
272+
PacmanUpdater.installWithCommandRunner(
273+
path.join(dirPath, `TestApp.pacman`),
274+
commandWithArgs => {
275+
execSync(commandWithArgs.join(" "), { stdio: "inherit" })
276+
},
277+
console
278+
)
279+
// execSync(`sudo pacman -Syyu --noconfirm`, { stdio: "inherit" })
280+
// execSync(`sudo pacman -U --noconfirm "${path.join(dirPath, `TestApp.pacman`)}"`, { stdio: "inherit" })
281+
appPath = path.join("/opt", "TestApp", "TestApp")
282+
} else if (process.platform === "win32") {
283+
// access installed app's location
284+
const localProgramsPath = path.join(process.env.LOCALAPPDATA || path.join(homedir(), "AppData", "Local"), "Programs", "TestApp")
285+
// this is to clear dev environment when not running on an ephemeral GH runner.
286+
// Reinstallation will otherwise fail due to "uninstall" message prompt, so we must uninstall first (hence the setTimeout delay)
287+
const uninstaller = path.join(localProgramsPath, "Uninstall TestApp.exe")
288+
if (existsSync(uninstaller)) {
289+
console.log("Uninstalling", uninstaller)
290+
execFileSync(uninstaller, ["/S", "/C", "exit"], { stdio: "inherit" })
291+
await new Promise(resolve => setTimeout(resolve, 5000))
292+
}
293+
294+
const installerPath = path.join(dirPath, "TestApp Setup.exe")
295+
console.log("Installing windows", installerPath)
296+
// Don't use /S for silent install as we lose stdout pipe
297+
execFileSync(installerPath, ["/S"], { stdio: "inherit" })
298+
299+
appPath = path.join(localProgramsPath, "TestApp.exe")
300+
} else if (process.platform === "darwin") {
301+
appPath = path.join(dirPath, `mac${getArchSuffix(arch)}`, `TestApp.app`, "Contents", "MacOS", "TestApp")
302+
} else {
303+
throw new Error(`Unsupported Update test target: ${target}`)
304+
}
305+
return appPath
306+
}
307+
308+
async function handleCleanupPerOS({ target }: { target: string }) {
309+
if (target === "deb") {
310+
// TODO: ignore for now, this doesn't block CI, but proper uninstall logic should be implemented
311+
// execSync("dpkg -r testapp", { stdio: "inherit" });
312+
} else if (target === "rpm") {
313+
execSync(`zypper rm -y testapp`, { stdio: "inherit" })
314+
} else if (target === "pacman") {
315+
execSync(`pacman -R --noconfirm testapp`, { stdio: "inherit" })
316+
} else if (process.platform === "win32") {
317+
// access installed app's location
318+
const localProgramsPath = path.join(process.env.LOCALAPPDATA || path.join(homedir(), "AppData", "Local"), "Programs", "TestApp")
319+
const uninstaller = path.join(localProgramsPath, "Uninstall TestApp.exe")
320+
console.log("Uninstalling", uninstaller)
321+
execFileSync(uninstaller, ["/S", "/C", "exit"], { stdio: "inherit" })
322+
await new Promise(resolve => setTimeout(resolve, 5000))
323+
} else if (process.platform === "darwin") {
324+
// ignore, nothing to uninstall, it's running/updating out of the local `dist` directory
325+
}
326+
}
327+
303328
async function runTestWithinServer(doTest: (rootDirectory: string, updateConfigPath: string) => Promise<void>) {
304329
const tmpDir = new TmpDir("blackbox-update-test")
305330
const root = await tmpDir.getTempDir({ prefix: "server-root" })

0 commit comments

Comments
 (0)