Skip to content

Commit 275ac72

Browse files
committed
Add installer build script, docs; tweak UI/theme
Add a PowerShell installer builder (build/build-installer.ps1) and a pre-tag release checklist (docs/plans/PRE_TAG_RELEASE_CHECKLIST.md); update packaging docs to recommend the new script and document Inno Setup theming. Make Inno Setup scripts (Installer/setup.iss and Installer/Installer.iss) more configurable by introducing MyWizardStyle and guarding MyAppBuildDir so WizardStyle can use dynamic Windows theming. Persist system theme preference when the user has no explicit preference (App.xaml.cs and MainWindow.xaml.cs) and switch to IsHitTestVisible for the performance-overlay blur to avoid disabling input globally. Prevent closing the app while the performance intro is visible with a user-facing message. Minor .gitignore cleanup and packaging text updates.
1 parent f6f9822 commit 275ac72

9 files changed

Lines changed: 241 additions & 6 deletions

File tree

.gitignore

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,5 +131,4 @@ CPUSetSetter-main/
131131
*_old.*
132132
*.orig
133133
*.rej
134-
*.xaml.bak
135-
nul
134+
*.xaml.bak

App.xaml.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,12 @@ protected override void OnStartup(StartupEventArgs e)
164164
? settings.UseDarkTheme
165165
: themeService.GetSystemUsesDarkTheme();
166166

167+
if (!settings.HasUserThemePreference && settings.UseDarkTheme != useDarkTheme)
168+
{
169+
settings.UseDarkTheme = useDarkTheme;
170+
Task.Run(async () => await settingsService.UpdateSettingsAsync(settings)).GetAwaiter().GetResult();
171+
}
172+
167173
themeService.ApplyTheme(useDarkTheme);
168174
}
169175
catch (Exception ex)

Installer/Installer.iss

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,15 @@
66
#define MyAppURL "https://github.com/"
77
#define MyAppExeName "ThreadPilot.exe"
88
#define MyAppVersion "0.1.0-beta"
9+
10+
#ifndef MyWizardStyle
11+
#define MyWizardStyle "modern dynamic windows11"
12+
#endif
13+
914
; Point this to the folder containing the published binaries (e.g. dotnet publish -c Release -r win-x64)
10-
#define MyAppBuildDir "..\\publish"
15+
#ifndef MyAppBuildDir
16+
#define MyAppBuildDir "..\\publish"
17+
#endif
1118

1219
[Setup]
1320
AppId={{A2A4C8B5-4A9A-4B1B-93F4-5F8B1C7E8C2A}
@@ -28,7 +35,7 @@ PrivilegesRequiredOverridesAllowed=dialog
2835
OutputBaseFilename=ThreadPilot_Setup
2936
SetupIconFile=..\assets\icons\ico.ico
3037
SolidCompression=yes
31-
WizardStyle=modern
38+
WizardStyle={#MyWizardStyle}
3239

3340
[Languages]
3441
Name: "english"; MessagesFile: "compiler:Default.isl"

Installer/setup.iss

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
#define MyAppURL "https://github.com/PrimeBuild-pc/ThreadPilot"
77
#define MyAppExeName "ThreadPilot.exe"
88

9+
#ifndef MyWizardStyle
10+
#define MyWizardStyle "modern dynamic windows11"
11+
#endif
12+
913
#ifndef MyAppVersion
1014
#define MyAppVersion "1.1.1"
1115
#endif
@@ -33,7 +37,7 @@ OutputBaseFilename=ThreadPilot_v{#MyAppVersion}_Setup
3337
SetupIconFile=..\assets\icons\ico.ico
3438
Compression=lzma2/ultra64
3539
SolidCompression=yes
36-
WizardStyle=modern
40+
WizardStyle={#MyWizardStyle}
3741
ArchitecturesInstallIn64BitMode=x64compatible
3842
ArchitecturesAllowed=x64compatible
3943
UninstallDisplayIcon={app}\{#MyAppExeName}

MainWindow.xaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
<Style TargetType="Grid">
4949
<Style.Triggers>
5050
<DataTrigger Binding="{Binding ElementName=PerformanceIntroOverlay, Path=Visibility}" Value="Visible">
51-
<Setter Property="IsEnabled" Value="False"/>
51+
<Setter Property="IsHitTestVisible" Value="False"/>
5252
<Setter Property="Effect">
5353
<Setter.Value>
5454
<BlurEffect Radius="15" KernelType="Gaussian"/>

MainWindow.xaml.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,12 @@ private async Task InitializeSettingsAsync()
592592
? settings.UseDarkTheme
593593
: this.themeService.GetSystemUsesDarkTheme();
594594

595+
if (!settings.HasUserThemePreference && settings.UseDarkTheme != useDarkTheme)
596+
{
597+
settings.UseDarkTheme = useDarkTheme;
598+
await this.settingsService.UpdateSettingsAsync(settings);
599+
}
600+
595601
this.themeService.ApplyTheme(useDarkTheme);
596602
this.mainWindowViewModel.IsDarkTheme = useDarkTheme;
597603
this.ApplyWindowCaptionTheme(useDarkTheme);
@@ -1826,6 +1832,17 @@ protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
18261832
return;
18271833
}
18281834

1835+
if (this.isPerformanceIntroVisible)
1836+
{
1837+
e.Cancel = true;
1838+
System.Windows.MessageBox.Show(
1839+
"Please complete the Performance introduction before closing the application.\n\nClick 'Continue to Performance' to proceed.",
1840+
"Performance Introduction Required",
1841+
MessageBoxButton.OK,
1842+
MessageBoxImage.Information);
1843+
return;
1844+
}
1845+
18291846
e.Cancel = true;
18301847
_ = this.HandleWindowCloseAsync();
18311848
}

build/build-installer.ps1

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
param(
2+
[string]$Version = "1.1.1",
3+
[string]$Configuration = "Release",
4+
[switch]$SkipPublish
5+
)
6+
7+
$ErrorActionPreference = "Stop"
8+
9+
$scriptRoot = Split-Path -Parent $PSCommandPath
10+
$projectRoot = Split-Path -Parent $scriptRoot
11+
Set-Location -LiteralPath $projectRoot
12+
13+
$publishDir = Join-Path $projectRoot "artifacts\release\singlefile"
14+
$installerOutputDir = Join-Path $projectRoot "artifacts\release\installer"
15+
$installerExe = Join-Path $installerOutputDir ("ThreadPilot_v{0}_Setup.exe" -f $Version)
16+
$publishedExe = Join-Path $publishDir "ThreadPilot.exe"
17+
18+
if (-not $SkipPublish)
19+
{
20+
Write-Host "Publishing ThreadPilot ($Configuration) to fresh single-file output..."
21+
dotnet publish "ThreadPilot.csproj" --configuration $Configuration -p:PublishProfile=WinX64-SingleFile
22+
}
23+
24+
if (-not (Test-Path -LiteralPath $publishedExe))
25+
{
26+
throw "Published executable not found: $publishedExe"
27+
}
28+
29+
if (Test-Path -LiteralPath $installerExe)
30+
{
31+
Remove-Item -LiteralPath $installerExe -Force
32+
}
33+
34+
$resolvedSourceDir = (Resolve-Path -LiteralPath $publishDir).Path
35+
Write-Host "Building installer from source directory: $resolvedSourceDir"
36+
37+
& iscc.exe "/DMyAppVersion=$Version" "/DMyAppSourceDir=$resolvedSourceDir" "Installer/setup.iss"
38+
if ($LASTEXITCODE -ne 0)
39+
{
40+
throw "Inno Setup compile failed with exit code $LASTEXITCODE"
41+
}
42+
43+
if (-not (Test-Path -LiteralPath $installerExe))
44+
{
45+
throw "Installer not generated at expected path: $installerExe"
46+
}
47+
48+
$publishedInfo = Get-Item -LiteralPath $publishedExe
49+
$installerInfo = Get-Item -LiteralPath $installerExe
50+
51+
if ($installerInfo.LastWriteTimeUtc -lt $publishedInfo.LastWriteTimeUtc)
52+
{
53+
throw "Installer timestamp is older than the published executable. Build may be stale."
54+
}
55+
56+
Write-Host "Installer build complete." -ForegroundColor Green
57+
Write-Host "Published exe: $publishedExe"
58+
Write-Host "Installer exe: $installerExe"
59+
Write-Host "Published exe timestamp (UTC): $($publishedInfo.LastWriteTimeUtc.ToString('u'))"
60+
Write-Host "Installer timestamp (UTC): $($installerInfo.LastWriteTimeUtc.ToString('u'))"
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
# ThreadPilot Pre-Tag Release Checklist
2+
3+
Use this checklist before creating a release tag. It is aligned with local build scripts and the GitHub release workflow.
4+
5+
## Release Meta
6+
7+
- [ ] Target version: 1.1.1
8+
- [ ] Target tag: v1.1.1
9+
- [ ] Branch is correct for release
10+
- [ ] Working tree is clean (except intentionally ignored artifact files)
11+
12+
## 1) Artifact Cleanup (Local)
13+
14+
Goal: keep only 1.1.1 artifacts in artifacts/release.
15+
16+
- [ ] Delete old installer: artifacts/release/installer/ThreadPilot_v1.1.0_Setup.exe
17+
- [ ] Delete old msix: artifacts/release/msix/ThreadPilot_1.1.0.0_win-x64.msix
18+
- [ ] Delete old zips: artifacts/release/ThreadPilot_v1.1.0_Installer.zip, artifacts/release/ThreadPilot_v1.1.0_Portable.zip
19+
- [ ] Delete temporary staging dirs: artifacts/release/package-stage, artifacts/release/_stage_installer, artifacts/release/_stage_portable
20+
21+
Expected state:
22+
23+
- [ ] artifacts/release/installer/ThreadPilot_v1.1.1_Setup.exe
24+
- [ ] artifacts/release/msix/ThreadPilot_1.1.1.0_win-x64.msix
25+
- [ ] artifacts/release/packages/ThreadPilot_v1.1.1_Installer.zip
26+
- [ ] artifacts/release/packages/ThreadPilot_v1.1.1_Portable.zip
27+
28+
## 2) Build Gates (Must Pass)
29+
30+
Run from repository root.
31+
32+
Commands:
33+
34+
dotnet restore "ThreadPilot_1.sln"
35+
dotnet build "ThreadPilot_1.sln" --configuration Release --no-restore
36+
dotnet test "ThreadPilot_1.sln" --configuration Release --no-build
37+
38+
- [ ] Restore passed
39+
- [ ] Build passed
40+
- [ ] Tests passed
41+
42+
## 3) Packaging Gates (Local)
43+
44+
Commands:
45+
46+
./build/build-installer.ps1 -Version "1.1.1"
47+
dotnet publish "ThreadPilot.csproj" --configuration Release -p:PublishProfile=WinX64-MSIX
48+
./build/package-release-zips.ps1 -Version "1.1.1"
49+
50+
- [ ] Installer generated in artifacts/release/installer
51+
- [ ] MSIX generated in artifacts/release/msix
52+
- [ ] Zip packages generated in artifacts/release/packages
53+
- [ ] Inno Setup warnings target is zero (artifacts/release/installer_iscc_setup.log)
54+
55+
## 4) Naming Gate (Local vs CI)
56+
57+
Local script naming (package-release-zips.ps1):
58+
59+
- [ ] ThreadPilot_v1.1.1_Installer.zip
60+
- [ ] ThreadPilot_v1.1.1_Portable.zip
61+
62+
GitHub workflow naming (release.yml package step):
63+
64+
- [ ] ThreadPilot_v1.1.1_singlefile_win-x64.zip
65+
- [ ] ThreadPilot_v1.1.1_readytorun_win-x64.zip
66+
67+
Note: naming differs by channel (local script vs CI workflow). Validate the expected set for the channel you are releasing from.
68+
69+
## 5) Hash + Signature Gate
70+
71+
Generate and verify SHA256 hashes:
72+
73+
Commands:
74+
75+
$hashFile = "artifacts/release/SHA256SUMS.txt"
76+
if (Test-Path $hashFile) { Remove-Item $hashFile -Force }
77+
78+
$releaseFiles = @()
79+
$releaseFiles += Get-ChildItem "artifacts/release/packages" -File -ErrorAction SilentlyContinue
80+
$releaseFiles += Get-ChildItem "artifacts/release/installer/*.exe" -File -ErrorAction SilentlyContinue
81+
$releaseFiles += Get-ChildItem "artifacts/release/msix" -Recurse -File -Include *.msix,*.appx,*.msixbundle,*.appxbundle -ErrorAction SilentlyContinue
82+
83+
$releaseFiles | ForEach-Object {
84+
$hash = Get-FileHash $_.FullName -Algorithm SHA256
85+
"$($hash.Hash) $($_.Name)" | Out-File -FilePath $hashFile -Append -Encoding utf8
86+
}
87+
88+
- [ ] SHA256SUMS.txt generated
89+
- [ ] Every shipped artifact has one hash row
90+
91+
If signing is enabled:
92+
93+
- [ ] Authenticode signature is valid (Get-AuthenticodeSignature)
94+
- [ ] Timestamp present (DigiCert or equivalent)
95+
96+
## 6) Smoke Test Installation
97+
98+
Portable smoke test:
99+
100+
Commands:
101+
102+
$tmp = "$env:TEMP/ThreadPilot_Smoke_$(Get-Random)"
103+
Expand-Archive "artifacts/release/packages/ThreadPilot_v1.1.1_Portable.zip" -DestinationPath $tmp -Force
104+
Test-Path "$tmp/ThreadPilot.exe"
105+
106+
- [ ] Portable archive extracts correctly
107+
- [ ] ThreadPilot.exe exists and starts
108+
109+
Installer smoke test:
110+
111+
- [ ] Run artifacts/release/installer/ThreadPilot_v1.1.1_Setup.exe
112+
- [ ] Install completes without errors
113+
- [ ] Start menu and/or desktop shortcut launches app
114+
- [ ] Uninstall path works
115+
- [ ] Theme is coherent on first launch (system dark -> dark UI + checked setting)
116+
117+
## 7) Pre-Tag Final Gate
118+
119+
- [ ] All gates above are green
120+
- [ ] Release notes prepared
121+
- [ ] Optional signing decision documented (signed/unsigned)
122+
123+
Create tag only after all checks pass:
124+
125+
Commands:
126+
127+
git tag -a v1.1.1 -m "Release ThreadPilot v1.1.1"
128+
git push origin v1.1.1

docs/release/PACKAGING.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,20 @@ Output folder:
4444
iscc /DMyAppVersion=1.1.1 /DMyAppSourceDir="..\\artifacts\\release\\singlefile" Installer/setup.iss
4545
```
4646

47+
Recommended local command (prevents stale publish output by forcing a fresh publish before ISCC):
48+
49+
```powershell
50+
powershell -ExecutionPolicy Bypass -File build/build-installer.ps1 -Version 1.1.1
51+
```
52+
53+
The installer now uses Inno Setup native dynamic theming (`WizardStyle=modern dynamic windows11`) and follows the current Windows light/dark preference at startup.
54+
55+
Optional override for QA/troubleshooting:
56+
57+
```powershell
58+
iscc /DMyWizardStyle="modern dark windows11" /DMyAppVersion=1.1.1 /DMyAppSourceDir="..\\artifacts\\release\\singlefile" Installer/setup.iss
59+
```
60+
4761
Output folder:
4862

4963
- `artifacts/release/installer/`

0 commit comments

Comments
 (0)