Skip to content

Commit 455bbc6

Browse files
committed
updated to the build script to include a digital signature
1 parent ae47b09 commit 455bbc6

3 files changed

Lines changed: 92 additions & 29 deletions

File tree

installer/build-msi.ps1

Lines changed: 77 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,29 @@
11
# ============================================================================
2-
# Weather Widget - MSI Package Builder (Traditional Installer)
2+
# Weather Widget - MSI Package Builder (Microsoft Store / Sideload)
33
# ============================================================================
44
# Usage:
5-
# .\installer\build-msi.ps1 -Version "1.0.0.0"
6-
# .\installer\build-msi.ps1 -Version "1.0.0.0" -CertPath "cert.pfx" -CertPassword "pass"
7-
# .\installer\build-msi.ps1 -Version "1.0.0.0" -SkipSign
5+
# # Sign with certificate from Windows certificate store (by thumbprint):
6+
# .\installer\build-msi.ps1 -Version "0.0.5.0" -CertThumbprint "A9D97675327F7BF344BA308A357E91141A1DDE50"
7+
#
8+
# # Sign with a .pfx file:
9+
# .\installer\build-msi.ps1 -Version "0.0.5.0" -CertPath "cert.pfx" -CertPassword "pass"
10+
#
11+
# # Build without signing (for testing only):
12+
# .\installer\build-msi.ps1 -Version "0.0.5.0" -SkipSign
813
#
914
# Prerequisites:
1015
# - Go 1.25+ with CGO enabled (for Fyne)
1116
# - go-winres: go install github.com/tc-hib/go-winres@latest
12-
# - WiX v4+: dotnet tool install --global wix
13-
# - Code signing certificate (for production builds)
17+
# - WiX v4+: dotnet tool install --global wix (or WiX v7 via winget)
18+
# - signtool.exe (from Windows SDK)
19+
# - Code signing certificate (installed in cert store or as .pfx file)
1420
# ============================================================================
1521

1622
param(
1723
[Parameter(Mandatory=$true)]
1824
[string]$Version,
1925

26+
[string]$CertThumbprint = "A9D97675327F7BF344BA308A357E91141A1DDE50",
2027
[string]$CertPath = "",
2128
[string]$CertPassword = "",
2229
[switch]$SkipSign,
@@ -35,6 +42,41 @@ if (-not (Test-Path $GoWinRes)) {
3542
throw "go-winres not found at $GoWinRes. Install with: go install github.com/tc-hib/go-winres@latest"
3643
}
3744

45+
# Determine signing mode
46+
$SignMode = "none"
47+
if (-not $SkipSign) {
48+
if ($CertThumbprint) {
49+
$SignMode = "thumbprint"
50+
} elseif ($CertPath) {
51+
$SignMode = "pfx"
52+
}
53+
}
54+
55+
# Helper function to sign a file
56+
function Sign-File {
57+
param([string]$FilePath, [string]$Description)
58+
59+
if ($SignMode -eq "none") { return }
60+
61+
$signArgs = @("sign", "/fd", "SHA256", "/tr", "http://timestamp.digicert.com", "/td", "SHA256")
62+
63+
if ($SignMode -eq "thumbprint") {
64+
$signArgs += @("/sha1", $CertThumbprint, "/s", "My")
65+
} else {
66+
$signArgs += @("/f", $CertPath)
67+
if ($CertPassword) { $signArgs += @("/p", $CertPassword) }
68+
}
69+
70+
if ($Description) {
71+
$signArgs += @("/d", $Description)
72+
}
73+
74+
$signArgs += $FilePath
75+
76+
& signtool @signArgs
77+
if ($LASTEXITCODE -ne 0) { throw "Signing failed for $FilePath" }
78+
}
79+
3880
Push-Location $ProjectRoot
3981

4082
try {
@@ -44,6 +86,7 @@ try {
4486
Write-Host ""
4587
Write-Host "============================================" -ForegroundColor Cyan
4688
Write-Host " Weather Widget MSI Builder v$Version" -ForegroundColor Cyan
89+
Write-Host " Sign mode: $SignMode" -ForegroundColor Cyan
4790
Write-Host "============================================" -ForegroundColor Cyan
4891
Write-Host ""
4992

@@ -60,6 +103,15 @@ try {
60103
# -------------------------------------------------------------------------
61104
if (-not $SkipBuild) {
62105
Write-Host "[2/5] Generating Windows resources..." -ForegroundColor Yellow
106+
107+
# Update version in winres.json before generating
108+
$winresJson = Get-Content ".\winres\winres.json" -Raw | ConvertFrom-Json
109+
$winresJson.RT_VERSION.'#1'.'0409'.fixed.file_version = $Version
110+
$winresJson.RT_VERSION.'#1'.'0409'.fixed.product_version = $Version
111+
$winresJson.RT_VERSION.'#1'.'0409'.info.'0409'.FileVersion = $Version
112+
$winresJson.RT_VERSION.'#1'.'0409'.info.'0409'.ProductVersion = $Version
113+
$winresJson | ConvertTo-Json -Depth 10 | Set-Content ".\winres\winres.json"
114+
63115
& $GoWinRes make --in .\winres\winres.json --product-version $Version --file-version $Version
64116
if ($LASTEXITCODE -ne 0) { throw "go-winres failed" }
65117
Write-Host " Done." -ForegroundColor Green
@@ -75,22 +127,21 @@ try {
75127
$ldflags = "-H windowsgui -s -w -X main.version=$Version"
76128
go build -ldflags="$ldflags" -o "$BuildDir\weatherwidget.exe" .\cmd\weatherwidget\
77129
if ($LASTEXITCODE -ne 0) { throw "Go build failed" }
78-
Write-Host " Done." -ForegroundColor Green
130+
Write-Host " Done. Output: $BuildDir\weatherwidget.exe" -ForegroundColor Green
79131
} else {
80132
Write-Host "[2/5] Skipping (SkipBuild)." -ForegroundColor DarkGray
81133
Write-Host "[3/5] Skipping (SkipBuild)." -ForegroundColor DarkGray
134+
if (-not (Test-Path "$BuildDir\weatherwidget.exe")) {
135+
throw "No existing exe found at $BuildDir\weatherwidget.exe. Remove -SkipBuild flag."
136+
}
82137
}
83138

84139
# -------------------------------------------------------------------------
85140
# Step 4: Sign the executable
86141
# -------------------------------------------------------------------------
87-
if (-not $SkipSign -and $CertPath) {
142+
if ($SignMode -ne "none") {
88143
Write-Host "[4/5] Signing executable..." -ForegroundColor Yellow
89-
$signArgs = @("sign", "/fd", "SHA256", "/tr", "http://timestamp.digicert.com", "/td", "SHA256", "/f", $CertPath)
90-
if ($CertPassword) { $signArgs += @("/p", $CertPassword) }
91-
$signArgs += "$BuildDir\weatherwidget.exe"
92-
& signtool @signArgs
93-
if ($LASTEXITCODE -ne 0) { throw "Signing failed" }
144+
Sign-File -FilePath "$BuildDir\weatherwidget.exe" -Description "Weather Widget"
94145
Write-Host " Done." -ForegroundColor Green
95146
} else {
96147
Write-Host "[4/5] Skipping signing." -ForegroundColor DarkGray
@@ -106,29 +157,26 @@ try {
106157
if (-not $wixExe) {
107158
$wixPaths = @(
108159
"${env:ProgramFiles}\WiX Toolset v7.0\bin\wix.exe",
109-
"${env:ProgramFiles}\WiX Toolset v4.0\bin\wix.exe",
110-
"${env:ProgramFiles(x86)}\WiX Toolset v3.14\bin\candle.exe"
160+
"${env:ProgramFiles}\WiX Toolset v4.0\bin\wix.exe"
111161
)
112162
foreach ($p in $wixPaths) {
113163
if (Test-Path $p) { $wixExe = $p; break }
114164
}
115165
}
116166
if (-not $wixExe) {
117-
throw "WiX not found. Install with: winget install WiXToolset.WiXCLI"
167+
throw "WiX not found. Install with: dotnet tool install --global wix"
118168
}
119169
Write-Host " Using WiX: $wixExe" -ForegroundColor DarkGray
120170

121171
& $wixExe build .\installer\Package.wxs -d BuildDir=$BuildDir -d Version=$Version -o $OutputMsi
122172
if ($LASTEXITCODE -ne 0) { throw "WiX build failed" }
173+
Write-Host " MSI created." -ForegroundColor Green
123174

124175
# Sign the MSI
125-
if (-not $SkipSign -and $CertPath) {
176+
if ($SignMode -ne "none") {
126177
Write-Host " Signing MSI..." -ForegroundColor Yellow
127-
$signArgs = @("sign", "/fd", "SHA256", "/tr", "http://timestamp.digicert.com", "/td", "SHA256", "/f", $CertPath)
128-
if ($CertPassword) { $signArgs += @("/p", $CertPassword) }
129-
$signArgs += $OutputMsi
130-
& signtool @signArgs
131-
if ($LASTEXITCODE -ne 0) { throw "MSI signing failed" }
178+
Sign-File -FilePath $OutputMsi -Description "Weather Widget Installer"
179+
Write-Host " MSI signed." -ForegroundColor Green
132180
}
133181

134182
Write-Host ""
@@ -138,6 +186,13 @@ try {
138186
Write-Host "============================================" -ForegroundColor Green
139187
Write-Host ""
140188

189+
if ($SignMode -eq "none") {
190+
Write-Host " WARNING: Package is unsigned. Microsoft Store" -ForegroundColor Yellow
191+
Write-Host " requires SHA256 code signing (Policy 10.2.9)." -ForegroundColor Yellow
192+
Write-Host " Use -CertThumbprint or -CertPath to sign." -ForegroundColor Yellow
193+
Write-Host ""
194+
}
195+
141196
} finally {
142197
Pop-Location
143198
}

installer/build-msix.ps1

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -152,9 +152,12 @@ try {
152152
Copy-Item "$BuildDir\weatherwidget.exe" "$PackageDir\"
153153

154154
# Copy and update AppxManifest with current version
155+
# Use a lookbehind to only replace the Version inside the Identity element,
156+
# not the version="1.0" in the <?xml ?> declaration.
155157
$manifest = Get-Content ".\installer\AppxManifest.xml" -Raw
156-
$manifest = $manifest -replace 'Version="[^"]*"', "Version=`"$Version`""
157-
Set-Content -Path "$PackageDir\AppxManifest.xml" -Value $manifest
158+
$manifest = $manifest -replace '(?<=<Identity\s[^>]*)Version="[^"]*"', "Version=`"$Version`""
159+
# MakeAppx requires UTF-8 without BOM — Set-Content adds a BOM which breaks the XML declaration
160+
[System.IO.File]::WriteAllText("$PackageDir\AppxManifest.xml", $manifest, (New-Object System.Text.UTF8Encoding $false))
158161

159162
# Copy store assets (user must provide these)
160163
$storeAssetsDir = ".\installer\store-assets"
@@ -239,8 +242,13 @@ try {
239242

240243
# .msixupload is a ZIP containing the .msix (and optionally .msixsym)
241244
# Partner Center expects this format for Store submissions.
245+
# Compress-Archive in Windows PowerShell 5.1 only allows .zip extension,
246+
# so create as .zip first, then rename to .msixupload.
242247
if (Test-Path $OutputMsixUpload) { Remove-Item $OutputMsixUpload -Force }
243-
Compress-Archive -Path $OutputMsix -DestinationPath $OutputMsixUpload -Force
248+
$tempZip = "$OutputMsixUpload.zip"
249+
if (Test-Path $tempZip) { Remove-Item $tempZip -Force }
250+
Compress-Archive -Path $OutputMsix -DestinationPath $tempZip -Force
251+
Move-Item -Path $tempZip -Destination $OutputMsixUpload -Force
244252

245253
Write-Host " Done." -ForegroundColor Green
246254
}

winres/winres.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,19 @@
2323
"#1": {
2424
"0409": {
2525
"fixed": {
26-
"file_version": "1.0.0.0",
27-
"product_version": "1.0.0.0"
26+
"file_version": "0.0.5.0",
27+
"product_version": "0.0.5.0"
2828
},
2929
"info": {
3030
"0409": {
3131
"CompanyName": "WeatherWidget",
3232
"FileDescription": "Weather Widget Desktop Application",
33-
"FileVersion": "1.0.0.0",
33+
"FileVersion": "0.0.5.0",
3434
"InternalName": "weatherwidget",
3535
"LegalCopyright": "© 2026 WeatherWidget",
3636
"OriginalFilename": "weatherwidget.exe",
3737
"ProductName": "Weather Widget",
38-
"ProductVersion": "1.0.0.0"
38+
"ProductVersion": "0.0.5.0"
3939
}
4040
}
4141
}

0 commit comments

Comments
 (0)