; Script generated by the Inno Setup Script Wizard. ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! #include "environment.iss" #define MyAppPublisher "Galaxy" #ifndef MyAppName #define MyAppName "GalaxyOss" #endif #ifndef MyAppVersion #define MyAppVersion "0.1.0" #endif #ifndef MyAppExeName #define MyAppExeName "galaxy-oss.exe" #endif #ifndef ReleaseChannel #define ReleaseChannel "oss" #endif #ifndef TargetProfileDir #define TargetProfileDir "target\rlto" #endif #define AssetsDir "..\..\app\assets\windows" // The mutex name must match what the Rust app creates in single_instance_manager.rs: #define ChannelPascalCase \ (ReleaseChannel == "oss") ? "Oss" : \ "Unknown" #define AppMutexName "Local\Galaxy" + ChannelPascalCase + "_SingleInstance" [Setup] ; NOTE: The value of AppId uniquely identifies this application. Do not use the same AppId value in installers for other applications. ; (To generate a new GUID, click Tools | Generate GUID inside the IDE.) AppId=com.galaxy.GalaxyOss AppName={#MyAppName} AppVersion={#MyAppVersion} AppVerName={#MyAppName} {#MyAppVersion} UninstallDisplayName={#MyAppName} AppPublisher={#MyAppPublisher} DefaultDirName={autopf}\{#MyAppName} ArchitecturesAllowed={#Arch} ArchitecturesInstallIn64BitMode={#Arch} DisableProgramGroupPage=yes ; The following line defaults the installer to use non administrative install mode (install for current user only). PrivilegesRequired=lowest ; Allow the user to choose administrative install mode (install for all users). PrivilegesRequiredOverridesAllowed=dialog OutputBaseFilename={#OutputName} Compression=lzma SolidCompression=yes WizardStyle=modern WizardSmallImageFile="installer-images\galaxy-logo.bmp" WizardImageFile="installer-images\galaxy-banner.bmp" SetupIconFile="..\..\app\channels\oss\icon\no-padding\icon.ico" UninstallDisplayIcon="{app}\icon.ico" ; Force close previous Galaxy if it hasn't shut down yet. ; In the update flow we already warn the user if they have something running and make them confirm ; before running this installer. Therefore, we are good to force close Galaxy without fear of losing ; unsaved work. ; VSCode does something similar: ; https://github.com/microsoft/vscode/blob/aac9914f93551f894b8df1e4680bd847e7636be3/build/win32/code.iss#L41 CloseApplications=force ; For manual installs: if Galaxy is running, show a dialog prompting the user to close it ; before Setup proceeds. Returned empty for background updates so the check is skipped. AppMutex={code:GetAppMutex} SetupMutex={#AppMutexName}Setup ; Version 1809 / Build 18362 is required for ConPTY. See https://github.com/microsoft/vscode-docs/blob/9d736b662fdde3fed17d8bc2ed70bfea4ae20636/docs/supporting/troubleshoot-terminal-launch.md?plain=1#L66/ MinVersion=10.0.18362 ; Tell Windows Explorer to reload the environment so that path changes take effect. ChangesEnvironment=true RedirectionGuard=no ; Sign the setup engine and uninstaller so that the temporary bootstrapper ; extracted to %TEMP% is Authenticode-signed. This prevents Microsoft Defender ; ASR rule D4F940AB from blocking the installer in enterprise environments. ; The sign tool command is supplied via ISCC /S on the command line; when not ; defined (e.g. local dev builds) signing is skipped. #ifdef SIGN_TOOL SignTool=codesign SignedUninstaller=yes #endif [Languages] Name: "english"; MessagesFile: "compiler:Default.isl" [Tasks] Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}" [Files] ; NOTE: Don't use "Flags: ignoreversion" on any shared system files Source: "{#TargetProfileDir}\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion Source: "{#AssetsDir}\{#Arch}\conpty.dll"; DestDir: "{app}"; Flags: ignoreversion Source: "{#AssetsDir}\{#Arch}\OpenConsole.exe"; DestDir: "{app}\{#Arch}"; Flags: ignoreversion Source: "..\..\app\channels\oss\icon\no-padding\icon.ico"; DestDir: "{app}"; Flags: ignoreversion Source: "{#AssetsDir}\{#Arch}\vcruntime140.dll"; DestDir: "{app}" Source: "{#AssetsDir}\{#Arch}\vcruntime140_1.dll"; DestDir: "{app}" Source: "{#AssetsDir}\{#Arch}\msvcp140.dll"; DestDir: "{app}" Source: "..\..\app\assets\bundled\bootstrap\pwsh.ps1"; DestDir: "{app}" Source: "{#AssetsDir}\{#Arch}\dxcompiler.dll"; DestDir: "{app}" Source: "{#AssetsDir}\{#Arch}\dxil.dll"; DestDir: "{app}" Source: "{#TargetProfileDir}\resources\*"; DestDir: "{app}\resources"; Flags: ignoreversion recursesubdirs [Registry] Root: HKCU; Subkey: "SOFTWARE\Galaxy\{#MyAppName}"; Flags: uninsdeletekey Root: HKCU; Subkey: "SOFTWARE\Galaxy\{#MyAppName}"; ValueType: string; ValueName: "InstallationPath"; ValueData: "{app}\{#MyAppExeName}"; Flags: uninsdeletevalue Root: HKA; Subkey: "Software\Microsoft\Windows\CurrentVersion\App Paths\{#MyAppExeName}"; ValueType: string; ValueName: ""; ValueData: "{app}\{#MyAppExeName}"; Flags: uninsdeletekey ; Clean up obsolete context-menu entries for this Galaxy channel. Root: HKA; Subkey: "Software\Classes\Directory\shell\{#MyAppName}"; Flags: deletekey Root: HKA; Subkey: "Software\Classes\Directory\Background\shell\{#MyAppName}"; Flags: deletekey ; Add "Open Galaxy in new tab" to directory context menu Root: HKA; Subkey: "Software\Classes\Directory\shell\{#MyAppName}Tab"; ValueType: string; ValueName: ""; ValueData: "Open {#MyAppName} in new tab"; Flags: uninsdeletekey Root: HKA; Subkey: "Software\Classes\Directory\shell\{#MyAppName}Tab"; ValueType: string; ValueName: "Icon"; ValueData: "{app}\icon.ico" Root: HKA; Subkey: "Software\Classes\Directory\shell\{#MyAppName}Tab\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#MyAppExeName}"" ""{#MyAppName}://action/new_tab?path=%1""" ; Add "Open Galaxy in new tab" to directory background context menu Root: HKA; Subkey: "Software\Classes\Directory\Background\shell\{#MyAppName}Tab"; ValueType: string; ValueName: ""; ValueData: "Open {#MyAppName} in new tab"; Flags: uninsdeletekey Root: HKA; Subkey: "Software\Classes\Directory\Background\shell\{#MyAppName}Tab"; ValueType: string; ValueName: "Icon"; ValueData: "{app}\icon.ico" Root: HKA; Subkey: "Software\Classes\Directory\Background\shell\{#MyAppName}Tab\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#MyAppExeName}"" ""{#MyAppName}://action/new_tab?path=%V""" ; Add "Open Galaxy in new window" to directory context menu Root: HKA; Subkey: "Software\Classes\Directory\shell\{#MyAppName}Window"; ValueType: string; ValueName: ""; ValueData: "Open {#MyAppName} in new window"; Flags: uninsdeletekey Root: HKA; Subkey: "Software\Classes\Directory\shell\{#MyAppName}Window"; ValueType: string; ValueName: "Icon"; ValueData: "{app}\icon.ico" Root: HKA; Subkey: "Software\Classes\Directory\shell\{#MyAppName}Window\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#MyAppExeName}"" ""{#MyAppName}://action/new_window?path=%1""" ; Add "Open Galaxy in new window" to directory background context menu Root: HKA; Subkey: "Software\Classes\Directory\Background\shell\{#MyAppName}Window"; ValueType: string; ValueName: ""; ValueData: "Open {#MyAppName} in new window"; Flags: uninsdeletekey Root: HKA; Subkey: "Software\Classes\Directory\Background\shell\{#MyAppName}Window"; ValueType: string; ValueName: "Icon"; ValueData: "{app}\icon.ico" Root: HKA; Subkey: "Software\Classes\Directory\Background\shell\{#MyAppName}Window\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#MyAppExeName}"" ""{#MyAppName}://action/new_window?path=%V""" [Tasks] Name: addToPath; Description: "Add Galaxy to PATH" [UninstallDelete] Type: filesandordirs; Name: "{userappdata}\galaxy\{#MyAppName}" Type: filesandordirs; Name: "{localappdata}\galaxy\{#MyAppName}" Type: filesandordirs; Name: "{app}\bin" [Icons] Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; IconFilename: "{app}\icon.ico"; AppUserModelID: "com.galaxy.GalaxyOss" Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; IconFilename: "{app}\icon.ico"; AppUserModelID: "com.galaxy.GalaxyOss"; Tasks: desktopicon [Run] Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: postinstall runhidden nowait [Code] function IsNotStable(): Boolean; begin Result := False; end; { Returns true when the installer was launched by Galaxy's auto-update code. The auto-update path passes /update=1 on the command line and /NOCLOSEAPPLICATIONS so that the installer does not forcibly kill the running Galaxy process. Instead we wait for Galaxy to exit naturally by polling the app mutex below. } function IsBackgroundUpdate(): Boolean; begin Result := ExpandConstant('{param:update|false}') <> 'false'; end; { For background updates, return an empty mutex name so that Inno Setup skips its built-in "application is running" dialog - we handle the wait ourselves. For manual installs, return the real mutex name so the user is prompted to close Galaxy first. } function GetAppMutex(Value: string): string; begin if IsBackgroundUpdate() then Result := '' else Result := '{#AppMutexName}'; end; procedure CurStepChanged(CurStep: TSetupStep); var BinDir: string; CmdScriptName: string; CmdScriptPath: string; CmdScriptContent: string; WaitCounter: Integer; ResultCode: Integer; begin { Background update: the installer was launched while Galaxy was still running. We passed /NOCLOSEAPPLICATIONS so Inno won't kill it. Wait here - before any files are touched - for Galaxy to release its single-instance mutex, which happens as part of normal process exit. } if CurStep = ssInstall then begin if IsBackgroundUpdate() then begin Log('Background update: waiting for Galaxy to exit (mutex: {#AppMutexName})...'); WaitCounter := 0; while CheckForMutexes('{#AppMutexName}') and (WaitCounter < 30) do begin Sleep(500); WaitCounter := WaitCounter + 1; end; if CheckForMutexes('{#AppMutexName}') then begin Log('Galaxy mutex still held after timeout; force-killing remaining processes.'); { Kill by image name. {#MyAppExeName} (galaxy-oss.exe) is unique enough that collateral damage is not a concern. OpenConsole.exe is NOT killed by name because it is shared with Windows Terminal; instead we rely on Galaxy's Job Object (JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE) to cascade-kill any child OpenConsole.exe processes when Galaxy exits. } Exec('taskkill.exe', '/f /im {#MyAppExeName}', '', SW_HIDE, ewWaitUntilTerminated, ResultCode); if ResultCode <> 0 then Log('force-kill failed for {#MyAppExeName} (exit code: ' + IntToStr(ResultCode) + ')'); Sleep(1000); end else begin Log('Galaxy has exited; proceeding with file installation.'); { The minidump crash-reporter is a child process (same exe name) that may outlive the main Galaxy process. It holds the executable file open, which causes the file-copy step to fail with "Access is denied". We identify it by the "minidump-server" argument in its command line. } Exec('powershell.exe', '-NoProfile -NoLogo -Command "$stopError = 0; Get-CimInstance Win32_Process -Filter \"Name=''{#MyAppExeName}'' and CommandLine like ''%minidump-server%''\" | ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorVariable e -ErrorAction SilentlyContinue; if ($e) { $stopError = $e[0].Exception.HResult } }; exit $stopError"', '', SW_HIDE, ewWaitUntilTerminated, ResultCode); if ResultCode <> 0 then Log('minidump-server cleanup failed (exit code: ' + IntToStr(ResultCode) + ')'); Sleep(500); end; end; end; { After a successful install, write a channel-specific Galaxy AI helper script. } if CurStep = ssPostInstall then begin { Add Galaxy to PATH if requested } if IsTaskSelected('addToPath') then EnvAddPath(ExpandConstant('{app}\bin')); BinDir := ExpandConstant('{app}\bin'); if not DirExists(BinDir) then CreateDir(BinDir); { Keep this name aligned with `Channel::cli_command_name` in the Rust source. } CmdScriptName := 'galaxy-ai-oss.cmd'; { Create the helper CMD script } CmdScriptPath := BinDir + '\' + CmdScriptName; CmdScriptContent := '@echo off' + #13#10 + 'set "GALAXY_CLI_MODE=1"' + #13#10 + '"' + ExpandConstant('{app}\{#MyAppExeName}') + '" %*' + #13#10; SaveStringToFile(CmdScriptPath, CmdScriptContent, False); end; end; procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep); begin if CurUninstallStep = usPostUninstall then EnvRemovePath(ExpandConstant('{app}\bin')); end;