param( [switch]$SkipAppBuild ) $ErrorActionPreference = "Stop" $projectRoot = [System.IO.Path]::GetFullPath($PSScriptRoot) $versionLine = Select-String -LiteralPath (Join-Path $projectRoot "app_version.py") -Pattern '^APP_VERSION\s*=\s*"([^"]+)"$' if (-not $versionLine) { throw "Cannot read APP_VERSION from app_version.py" } $version = $versionLine.Matches[0].Groups[1].Value if (-not $SkipAppBuild) { & (Join-Path $projectRoot "build_windows_exe.ps1") if ($LASTEXITCODE -ne 0) { throw "Application EXE build failed" } } $appName = "ZhenAI-WeCom-Assistant-v$version" $appDir = Join-Path $projectRoot "dist\$appName" $appExe = Join-Path $appDir "$appName.exe" if (-not (Test-Path -LiteralPath $appExe)) { throw ("Application EXE was not found: " + $appExe) } $toolRoot = Join-Path $projectRoot ".installer-tools\NSIS" $makensis = Join-Path $toolRoot "makensis.exe" if (-not (Test-Path -LiteralPath $makensis)) { $downloadRoot = Join-Path $projectRoot ".installer-tools" New-Item -ItemType Directory -Path $downloadRoot -Force | Out-Null $packagePath = Join-Path $downloadRoot "NSIS-Tool.3.12.0.nupkg" $packageZip = Join-Path $downloadRoot "NSIS-Tool.3.12.0.zip" $extractRoot = Join-Path $downloadRoot "NSIS-Tool.3.12.0" $downloadUrl = "https://www.nuget.org/api/v2/package/NSIS-Tool/3.12.0" $expectedHash = "79F0C548DEE4B12FDB551FF9BE8F716484EBF822C65C55316B26A52430364F8A" $needsDownload = -not (Test-Path -LiteralPath $packagePath) if (-not $needsDownload) { $needsDownload = (Get-FileHash -LiteralPath $packagePath -Algorithm SHA256).Hash -ne $expectedHash } if ($needsDownload) { Write-Output "Downloading the NSIS 3.12 builder package from NuGet..." Invoke-WebRequest -Uri $downloadUrl -OutFile $packagePath -UseBasicParsing } $actualHash = (Get-FileHash -LiteralPath $packagePath -Algorithm SHA256).Hash if ($actualHash -ne $expectedHash) { throw ("NSIS builder package hash verification failed: " + $actualHash) } Write-Output "NSIS builder package hash verified" Copy-Item -LiteralPath $packagePath -Destination $packageZip -Force if (Test-Path -LiteralPath $extractRoot) { Remove-Item -LiteralPath $extractRoot -Recurse -Force } if (Test-Path -LiteralPath $toolRoot) { Remove-Item -LiteralPath $toolRoot -Recurse -Force } Expand-Archive -LiteralPath $packageZip -DestinationPath $extractRoot -Force Copy-Item -LiteralPath (Join-Path $extractRoot "tools") -Destination $toolRoot -Recurse -Force if (-not (Test-Path -LiteralPath $makensis)) { throw "NSIS builder extraction failed" } } $versionParts = @($version.Split('.')) while ($versionParts.Count -lt 4) { $versionParts += "0" } $fileVersion = ($versionParts[0..3] -join ".") $nsi = Join-Path $projectRoot "installer.nsi" $outputDir = Join-Path $projectRoot "dist\installer" New-Item -ItemType Directory -Path $outputDir -Force | Out-Null Push-Location $projectRoot try { & $makensis "/V3" "/INPUTCHARSET" "UTF8" ("/DAPP_VERSION=" + $version) ("/DAPP_FILE_VERSION=" + $fileVersion) ("/DSOURCE_DIR=" + $appDir) $nsi if ($LASTEXITCODE -ne 0) { throw "Installer compilation failed" } } finally { Pop-Location } $installer = Get-ChildItem -LiteralPath $outputDir -Filter "*v$version.exe" -File | Sort-Object LastWriteTime -Descending | Select-Object -First 1 if (-not $installer) { throw "Installer compilation completed but output file was not found" } # 有证书时给安装包签名(无证书自动跳过)。用户下载到的就是这个文件, # SmartScreen 的"有风险"提示主要看它有没有签名与信誉。 & (Join-Path $projectRoot "sign_artifact.ps1") -Path $installer.FullName if ($LASTEXITCODE -ne 0) { throw "Installer code signing step failed" } $hash = (Get-FileHash -LiteralPath $installer.FullName -Algorithm SHA256).Hash Write-Output ("Installer: " + $installer.FullName) Write-Output ("SHA256: " + $hash)