Compare commits

...
1 Commits
Author SHA1 Message Date
David KayaandCopilot 7c1852f79f refactor: replace NSIS with Inno Setup for Windows installer
Switch from NSIS (early 2000s) to Inno Setup 6 — the modern standard
used by VS Code and most Electron apps. Features:

- Modern wizard UI (WizardStyle=modern)
- LZMA2/ultra64 solid compression (138.9 MB, down from 143 MB with NSIS)
- Per-user install to %LOCALAPPDATA%\Programs\Aryx (no admin needed)
- Optional desktop shortcut, Start Menu group
- Auto-closes running instance before install/uninstall
- Proper Add/Remove Programs integration via AppId
- Launch-after-install option

CI updated to install Inno Setup via Chocolatey on windows-latest.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-26 14:47:11 +01:00
4 changed files with 102 additions and 125 deletions
+2 -2
View File
@@ -131,10 +131,10 @@ jobs:
sudo apt-get update
sudo apt-get install -y libsecret-1-dev
- name: Install NSIS
- name: Install Inno Setup
if: runner.os == 'Windows'
shell: pwsh
run: choco install nsis -y --no-progress
run: choco install innosetup -y --no-progress
- name: Install dependencies
run: bun install --frozen-lockfile
+85
View File
@@ -0,0 +1,85 @@
; Inno Setup script for Aryx
; Metadata values are passed via /D preprocessor defines from the build script.
#ifndef PRODUCT_NAME
#define PRODUCT_NAME "Aryx"
#endif
#ifndef PRODUCT_VERSION
#define PRODUCT_VERSION "0.0.0"
#endif
#ifndef PRODUCT_PUBLISHER
#define PRODUCT_PUBLISHER "David Kaya"
#endif
#ifndef SOURCE_DIR
#error "SOURCE_DIR must be defined (path to the packaged app directory)."
#endif
#ifndef OUTPUT_DIR
#error "OUTPUT_DIR must be defined (path to the output directory)."
#endif
#ifndef OUTPUT_FILENAME
#error "OUTPUT_FILENAME must be defined (output installer filename without extension)."
#endif
#ifndef ICON_PATH
#define ICON_PATH SOURCE_DIR + "\" + PRODUCT_NAME + ".exe"
#endif
[Setup]
AppId={{B8A3E7F1-4D2C-4A9B-8E6F-1C3D5A7B9E0F}
AppName={#PRODUCT_NAME}
AppVersion={#PRODUCT_VERSION}
AppPublisher={#PRODUCT_PUBLISHER}
AppSupportURL=https://github.com/davidkaya/aryx
DefaultDirName={localappdata}\Programs\{#PRODUCT_NAME}
DefaultGroupName={#PRODUCT_NAME}
PrivilegesRequired=lowest
OutputDir={#OUTPUT_DIR}
OutputBaseFilename={#OUTPUT_FILENAME}
Compression=lzma2/ultra64
SolidCompression=yes
SetupIconFile={#ICON_PATH}
UninstallDisplayIcon={app}\{#PRODUCT_NAME}.exe
WizardStyle=modern
DisableProgramGroupPage=yes
CloseApplications=force
RestartApplications=no
[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"
[Files]
Source: "{#SOURCE_DIR}\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
[Icons]
Name: "{group}\{#PRODUCT_NAME}"; Filename: "{app}\{#PRODUCT_NAME}.exe"
Name: "{autodesktop}\{#PRODUCT_NAME}"; Filename: "{app}\{#PRODUCT_NAME}.exe"; Tasks: desktopicon
[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
[Run]
Filename: "{app}\{#PRODUCT_NAME}.exe"; Description: "{cm:LaunchProgram,{#PRODUCT_NAME}}"; Flags: nowait postinstall skipifsilent
[UninstallDelete]
Type: filesandordirs; Name: "{app}"
[Code]
procedure CurStepChanged(CurStep: TSetupStep);
var
ResultCode: Integer;
begin
if CurStep = ssInstall then
begin
Exec('taskkill', '/F /IM {#PRODUCT_NAME}.exe', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
end;
end;
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
var
ResultCode: Integer;
begin
if CurUninstallStep = usUninstall then
begin
Exec('taskkill', '/F /IM {#PRODUCT_NAME}.exe', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
end;
end;
-112
View File
@@ -1,112 +0,0 @@
!include "MUI2.nsh"
;--- Product metadata (passed via /D defines from the build script) ---
!ifndef PRODUCT_NAME
!define PRODUCT_NAME "Aryx"
!endif
!ifndef PRODUCT_VERSION
!define PRODUCT_VERSION "0.0.0"
!endif
!ifndef PRODUCT_PUBLISHER
!define PRODUCT_PUBLISHER "David Kaya"
!endif
!ifndef SOURCE_DIR
!error "SOURCE_DIR must be defined (path to the packaged app directory)."
!endif
!ifndef OUTPUT_PATH
!error "OUTPUT_PATH must be defined (path to the output installer .exe)."
!endif
;--- Installer attributes ---
Name "${PRODUCT_NAME}"
OutFile "${OUTPUT_PATH}"
InstallDir "$LOCALAPPDATA\Programs\${PRODUCT_NAME}"
InstallDirRegKey HKCU "Software\${PRODUCT_NAME}" "InstallDir"
RequestExecutionLevel user
SetCompressor /SOLID lzma
;--- Version info embedded in the installer EXE ---
VIProductVersion "${PRODUCT_VERSION}.0"
VIAddVersionKey "ProductName" "${PRODUCT_NAME}"
VIAddVersionKey "ProductVersion" "${PRODUCT_VERSION}"
VIAddVersionKey "FileDescription" "${PRODUCT_NAME} Setup"
VIAddVersionKey "FileVersion" "${PRODUCT_VERSION}"
VIAddVersionKey "CompanyName" "${PRODUCT_PUBLISHER}"
VIAddVersionKey "LegalCopyright" "Copyright ${PRODUCT_PUBLISHER}"
;--- MUI configuration ---
!define MUI_ABORTWARNING
;--- Installer pages ---
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
!define MUI_FINISHPAGE_RUN "$INSTDIR\${PRODUCT_NAME}.exe"
!insertmacro MUI_PAGE_FINISH
;--- Uninstaller pages ---
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
;--- Language ---
!insertmacro MUI_LANGUAGE "English"
;--- Installer section ---
Section "Install"
; Close any running instance
ExecWait 'taskkill /F /IM ${PRODUCT_NAME}.exe' $0
SetOutPath "$INSTDIR"
File /r "${SOURCE_DIR}\*.*"
; Write uninstaller
WriteUninstaller "$INSTDIR\Uninstall.exe"
; Start Menu shortcut
CreateDirectory "$SMPROGRAMS\${PRODUCT_NAME}"
CreateShortcut "$SMPROGRAMS\${PRODUCT_NAME}\${PRODUCT_NAME}.lnk" "$INSTDIR\${PRODUCT_NAME}.exe"
CreateShortcut "$SMPROGRAMS\${PRODUCT_NAME}\Uninstall ${PRODUCT_NAME}.lnk" "$INSTDIR\Uninstall.exe"
; Desktop shortcut
CreateShortcut "$DESKTOP\${PRODUCT_NAME}.lnk" "$INSTDIR\${PRODUCT_NAME}.exe"
; Add/Remove Programs registry entry
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" \
"DisplayName" "${PRODUCT_NAME}"
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" \
"UninstallString" '"$INSTDIR\Uninstall.exe"'
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" \
"QuietUninstallString" '"$INSTDIR\Uninstall.exe" /S'
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" \
"InstallLocation" "$INSTDIR"
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" \
"DisplayIcon" "$INSTDIR\${PRODUCT_NAME}.exe"
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" \
"Publisher" "${PRODUCT_PUBLISHER}"
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" \
"DisplayVersion" "${PRODUCT_VERSION}"
WriteRegDWORD HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" \
"NoModify" 1
WriteRegDWORD HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" \
"NoRepair" 1
; Store install directory for future upgrades
WriteRegStr HKCU "Software\${PRODUCT_NAME}" "InstallDir" "$INSTDIR"
SectionEnd
;--- Uninstaller section ---
Section "Uninstall"
; Close any running instance
ExecWait 'taskkill /F /IM ${PRODUCT_NAME}.exe' $0
; Remove application files
RMDir /r "$INSTDIR"
; Remove shortcuts
RMDir /r "$SMPROGRAMS\${PRODUCT_NAME}"
Delete "$DESKTOP\${PRODUCT_NAME}.lnk"
; Remove registry entries
DeleteRegKey HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}"
DeleteRegKey HKCU "Software\${PRODUCT_NAME}"
SectionEnd
+15 -11
View File
@@ -62,13 +62,13 @@ async function readVersion(): Promise<string> {
return packageJson.version;
}
// --- Windows: NSIS installer ---
// --- Windows: Inno Setup installer ---
async function resolveNsisPath(): Promise<string> {
async function resolveInnoSetupCompilerPath(): Promise<string> {
const candidates = [
'C:\\Program Files (x86)\\NSIS\\makensis.exe',
'C:\\Program Files\\NSIS\\makensis.exe',
'makensis',
'C:\\Program Files (x86)\\Inno Setup 6\\ISCC.exe',
'C:\\Program Files\\Inno Setup 6\\ISCC.exe',
'iscc',
];
for (const candidate of candidates) {
@@ -77,21 +77,25 @@ async function resolveNsisPath(): Promise<string> {
}
}
return 'makensis';
return 'iscc';
}
async function createWindowsInstaller(version: string): Promise<void> {
const nsisScript = join(installerAssetsDirectory, 'windows.nsi');
const makensisPath = await resolveNsisPath();
const issScript = join(installerAssetsDirectory, 'windows.iss');
const isccPath = await resolveInnoSetupCompilerPath();
const outputFilename = releaseTarget.installerAssetName.replace(/\.exe$/, '');
const iconPath = join(repositoryRoot, 'assets', 'icons', 'windows', 'icon.ico');
await runCommand(
makensisPath,
isccPath,
[
`/DPRODUCT_NAME=${productName}`,
`/DPRODUCT_VERSION=${version}`,
`/DSOURCE_DIR=${packagedAppDirectory}`,
`/DOUTPUT_PATH=${installerOutputPath}`,
nsisScript,
`/DOUTPUT_DIR=${releaseRootDirectory}`,
`/DOUTPUT_FILENAME=${outputFilename}`,
`/DICON_PATH=${iconPath}`,
issScript,
],
repositoryRoot,
);