Files
galaxy/script/windows/windows-installer.iss
T

248 lines
12 KiB
Plaintext

; 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;