3 NSIS Installer
filtered edited this page 2025-09-19 07:44:41 +10:00

Working with NSIS installer

Work in very small steps

  • NSIS compiler only provides one error - complex changes may completely obscure the root cause of issues
  • Making large changes is confusing and difficult to debug
  • e.g. Ask for each of these individually, and build the app after each step
    1. Ask for a new page to be inserted after the welcome screen
    2. Ask for a title to be added
    3. Ask for a group of checkboxes to be added
    4. ...

Build locally

  • There may be many iterations to do simple tasks
  • Having an agent build after each change allows fully autonomous implementation

Use a test harness

You can test NSIS UI changes inside a test harness, which compiles almost instantly.

builder-debug.config.ts

Build config allowing local install

{
  // ...
  win: {
    target: 'nsis',
  },
  nsis: {
    oneClick: false,
    allowToChangeInstallationDirectory: true,
    include: './scripts/installer.nsh',
  },
}

Install Harness

dev-uninstall-ui.nsi

!define BUILD_UNINSTALLER

!include "MUI2.nsh"
!include "LogicLib.nsh"
!include "nsDialogs.nsh"

; Pull in our electron-builder NSIS extensions and custom pages
!include "installer.nsh"

Name "ComfyUI Uninstall UI (Dev)"
OutFile "..\dist\Dev-Uninstall-UI.exe"
RequestExecutionLevel user
BrandingText " "

; Show our custom uninstall welcome + extra page, then a dummy progress and finish
!insertmacro customUnWelcomePage
!insertmacro MUI_UNPAGE_INSTFILES
!insertmacro MUI_UNPAGE_FINISH

!insertmacro MUI_LANGUAGE "English"

; Ensure basePath is resolved in harness uninstaller
Function un.onInit
  Call un.ResolveBasePath
FunctionEnd

; Minimal installer stub that writes the uninstaller to a temp path and launches it
Section "-launch-uninstaller"
  InitPluginsDir
  WriteUninstaller "$PLUGINSDIR\Dev-Uninstall-UI-Un.exe"
  ExecWait '"$PLUGINSDIR\Dev-Uninstall-UI-Un.exe"'
SectionEnd

Section "un.install"
  ; No-op body for dev harness
  Sleep 10
SectionEnd

Build script

devUninstallUi.cjs

#!/usr/bin/env node
const { spawnSync } = require('node:child_process');
const { join } = require('node:path');
const { existsSync } = require('node:fs');

const localAppData = process.env.LOCALAPPDATA || '';
const cachedMakensis = join(localAppData, 'electron-builder', 'Cache', 'nsis', 'nsis-3.0.4.1', 'Bin', 'makensis.exe');
const fallback = 'makensis';

const exe = existsSync(cachedMakensis) ? cachedMakensis : fallback;
const script = join(process.cwd(), 'scripts', 'dev-uninstall-ui.nsi');

const res = spawnSync(exe, [script], { stdio: 'inherit', shell: false });
if (res.error) {
  console.error('Failed to run makensis:', res.error.message);
  process.exit(1);
}
process.exit(res.status || 0);