Files
bambuddy/installers/windows/bambuddy.iss
maziggy 106ba7938a chore(installer): add RunOnceId to UninstallRun entries
Silences Inno Setup's "missing RunOnceId" warning. Both commands
  (stop service, remove firewall rule) are already idempotent, so the
  behavioural effect is nil — this is purely a cleanliness fix to keep
  the build log warning-free.
2026-06-10 13:25:05 +02:00

173 lines
7.6 KiB
Plaintext

; Bambuddy Windows Installer — Inno Setup script
;
; Builds a self-contained installer that lays down:
; - embedded Python 3.13 + pre-installed venv
; - backend source + pre-built frontend bundle
; - NSSM + ffmpeg under bin/
; - a Windows service running as LocalSystem
;
; Build prerequisites: run installers/windows/build.py first to stage
; the build/staging/ tree, then compile this file with ISCC.exe.
;
; See installers/windows/README.md for the full pipeline.
#define MyAppName "Bambuddy"
#define MyAppPublisher "Martin Ziegler"
#define MyAppURL "https://bambuddy.cool"
#define MyAppExeName "bambuddy.exe"
#define ServiceName "Bambuddy"
#define DefaultPort "8000"
; Version is stamped by build.py into build\staging\version.iss as a
; #define directive. Falls back to a placeholder if you ran ISCC without
; running build.py first (don't ship that build).
#ifexist "build\staging\version.iss"
#include "build\staging\version.iss"
#else
#define MyAppVersion "0.0.0+dev"
#endif
[Setup]
AppId={{8C9C9E1A-7C5A-4F2A-9F1B-BAMBUDDY00001}}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
DefaultDirName={autopf}\Bambuddy
DefaultGroupName={#MyAppName}
DisableProgramGroupPage=yes
LicenseFile=..\..\LICENSE
OutputDir=build\output
OutputBaseFilename=bambuddy-{#MyAppVersion}-windows-x64-setup
Compression=lzma
SolidCompression=yes
WizardStyle=modern
ArchitecturesAllowed=x64compatible
ArchitecturesInstallIn64BitMode=x64compatible
; Admin required: we register a Windows service and write to ProgramData
PrivilegesRequired=admin
PrivilegesRequiredOverridesAllowed=
; Bambuddy branding — bambuddy.ico is a multi-resolution .ico (16/32/48/
; 64/128/256) generated from frontend/public/img/favicon.png; lives next
; to this .iss so the SourcePath-relative reference works during compile
; and the [Files] entry stages it into {app} for Add/Remove Programs.
SetupIconFile=bambuddy.ico
UninstallDisplayIcon={app}\bambuddy.ico
; Don't allow installing to a network drive — service won't start cleanly
DisableDirPage=no
DisableReadyPage=no
ChangesEnvironment=no
CloseApplications=no
[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"
Name: "german"; MessagesFile: "compiler:Languages\German.isl"
[Tasks]
Name: "desktopicon"; Description: "Create a desktop shortcut"; GroupDescription: "Additional shortcuts:"; Flags: unchecked
Name: "firewallrule"; Description: "Add Windows Firewall rule for Bambuddy (port {#DefaultPort})"; GroupDescription: "Network:"
[Files]
; Embedded Python (entire tree)
Source: "build\staging\python\*"; DestDir: "{app}\python"; Flags: recursesubdirs ignoreversion
; Backend + frontend
Source: "build\staging\app\*"; DestDir: "{app}\app"; Flags: recursesubdirs ignoreversion
; NSSM, ffmpeg, ffprobe
Source: "build\staging\bin\*"; DestDir: "{app}\bin"; Flags: recursesubdirs ignoreversion
; Service install/uninstall scripts
Source: "build\staging\service\*"; DestDir: "{app}\service"; Flags: recursesubdirs ignoreversion
; Version stamp
Source: "build\staging\VERSION"; DestDir: "{app}"; Flags: ignoreversion
; App icon — used by UninstallDisplayIcon (Add/Remove Programs) and the
; Start Menu / desktop shortcuts. Lives at the install root so the
; UninstallDisplayIcon path stays stable when the [Files] tree changes.
Source: "bambuddy.ico"; DestDir: "{app}"; Flags: ignoreversion
[Dirs]
; ProgramData layout — created with permissions LocalSystem can write to
Name: "{commonappdata}\Bambuddy"; Permissions: users-modify
Name: "{commonappdata}\Bambuddy\data"; Permissions: users-modify
Name: "{commonappdata}\Bambuddy\logs"; Permissions: users-modify
[Icons]
Name: "{group}\Open Bambuddy Dashboard"; Filename: "http://localhost:{#DefaultPort}"; IconFilename: "{app}\bambuddy.ico"
Name: "{group}\Bambuddy Logs"; Filename: "{commonappdata}\Bambuddy\logs"
Name: "{group}\Uninstall Bambuddy"; Filename: "{uninstallexe}"
Name: "{commondesktop}\Bambuddy"; Filename: "http://localhost:{#DefaultPort}"; IconFilename: "{app}\bambuddy.ico"; Tasks: desktopicon
[Run]
; Register and start the Windows service
Filename: "{app}\service\install-service.bat"; Parameters: """{app}"" ""{commonappdata}\Bambuddy"" {#DefaultPort}"; Flags: runhidden waituntilterminated; StatusMsg: "Registering Bambuddy service..."
; Open Windows Firewall on the dashboard port. We do this only if the
; user opted in via the firewallrule task — some environments manage
; firewall centrally and prefer to handle this themselves.
Filename: "netsh.exe"; Parameters: "advfirewall firewall add rule name=""Bambuddy Dashboard"" dir=in action=allow protocol=TCP localport={#DefaultPort}"; Flags: runhidden waituntilterminated; Tasks: firewallrule; StatusMsg: "Adding firewall rule..."
; Open the dashboard in the user's default browser at the end of install
Filename: "http://localhost:{#DefaultPort}"; Flags: shellexec postinstall nowait skipifsilent; Description: "Open Bambuddy Dashboard"
[UninstallRun]
; Stop + deregister the service before file removal. RunOnceId makes the
; entry run-once per uninstall pass (Inno Setup default is to re-run on
; every pass, which can fire multiple times during upgrade flows).
Filename: "{app}\service\uninstall-service.bat"; Parameters: """{app}"""; Flags: runhidden waituntilterminated; RunOnceId: "StopBambuddyService"
; Remove the firewall rule (silently — if it doesn't exist, netsh just complains)
Filename: "netsh.exe"; Parameters: "advfirewall firewall delete rule name=""Bambuddy Dashboard"""; Flags: runhidden waituntilterminated; RunOnceId: "RemoveFirewallRule"
[UninstallDelete]
; Remove install dir contents; leave ProgramData\Bambuddy alone so the
; user keeps their database + archives. Re-installing on top picks them
; back up automatically.
Type: filesandordirs; Name: "{app}"
[Code]
// Stop the Bambuddy service BEFORE the [Files] section copies anything,
// so file locks on python.exe / .pyd / nssm.exe release in time for the
// overwrite. Without this, upgrading over a running install fails with
// "permission denied" on every file the service has open.
//
// On a fresh install {app}\bin\nssm.exe doesn't exist yet — FileExists
// guards that path so the hook is a no-op for first-time installers.
// The Sleep gives Windows a beat to finalize the python.exe unload
// before the [Files] step starts grabbing exclusive handles.
//
// The install-service.bat in [Run] does `nssm remove ... confirm` plus
// a fresh `nssm install`, so even if we leave the old service entry in
// place here, the post-install step re-registers it cleanly.
function PrepareToInstall(var NeedsRestart: Boolean): String;
var
ResultCode: Integer;
NssmPath: string;
begin
Result := '';
NeedsRestart := False;
NssmPath := ExpandConstant('{app}\bin\nssm.exe');
if FileExists(NssmPath) then
begin
Log('Stopping Bambuddy service before file copy...');
Exec(NssmPath, 'stop Bambuddy', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
// ResultCode 0 == stopped; non-zero is fine too (already stopped /
// service not registered). The lock we care about is python.exe's,
// and it's released the moment the process exits.
Sleep(1500);
end;
end;
// Pre-install check: refuse to install if port 8000 is already in use by
// something other than a previous Bambuddy install. This catches the
// "I have something else on 8000" case early instead of after install.
function InitializeSetup(): Boolean;
begin
Result := True;
// TODO: optional port-conflict check. Inno Setup doesn't have a
// native socket API; would need a tiny helper exe or a netstat parse.
// Defer to v1.1 — for v1, accept that conflicts surface at first
// service start and the user reads the log.
end;