250 lines
9.3 KiB
PowerShell
250 lines
9.3 KiB
PowerShell
param(
|
|
[switch]$SkipTests,
|
|
[switch]$SkipFrontendBuild,
|
|
[string]$WailsPath,
|
|
[string]$MakensisPath
|
|
)
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
|
|
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
$repoRoot = (Resolve-Path (Join-Path $scriptDir "..")).Path
|
|
$binDir = Join-Path $repoRoot "build\bin"
|
|
$installerDir = Join-Path $repoRoot "build\windows\installer"
|
|
$runtimeDir = Join-Path $installerDir "runtime"
|
|
|
|
function Resolve-RequiredTool {
|
|
param(
|
|
[Parameter(Mandatory = $true)][string]$Name,
|
|
[string]$FallbackPath,
|
|
[string]$InstallHint
|
|
)
|
|
|
|
$command = Get-Command $Name -ErrorAction SilentlyContinue
|
|
if ($command) {
|
|
return $command.Source
|
|
}
|
|
if ($FallbackPath -and (Test-Path -LiteralPath $FallbackPath)) {
|
|
return (Resolve-Path -LiteralPath $FallbackPath).Path
|
|
}
|
|
throw "Required tool not found: $Name. $InstallHint"
|
|
}
|
|
|
|
function Assert-UnderDirectory {
|
|
param(
|
|
[Parameter(Mandatory = $true)][string]$Path,
|
|
[Parameter(Mandatory = $true)][string]$Parent
|
|
)
|
|
|
|
$parentPath = (Resolve-Path -LiteralPath $Parent).Path.TrimEnd('\')
|
|
if (Test-Path -LiteralPath $Path) {
|
|
$targetPath = (Resolve-Path -LiteralPath $Path).Path
|
|
} else {
|
|
$targetPath = [System.IO.Path]::GetFullPath($Path)
|
|
}
|
|
if (-not $targetPath.StartsWith($parentPath, [System.StringComparison]::OrdinalIgnoreCase)) {
|
|
throw "Refusing to operate on $targetPath because it is not under $parentPath"
|
|
}
|
|
}
|
|
|
|
function Reset-Directory {
|
|
param(
|
|
[Parameter(Mandatory = $true)][string]$Path,
|
|
[Parameter(Mandatory = $true)][string]$Parent
|
|
)
|
|
|
|
Assert-UnderDirectory -Path $Path -Parent $Parent
|
|
if (Test-Path -LiteralPath $Path) {
|
|
Remove-Item -LiteralPath $Path -Recurse -Force
|
|
}
|
|
New-Item -ItemType Directory -Force -Path $Path | Out-Null
|
|
}
|
|
|
|
function Write-Utf8NoBom {
|
|
param(
|
|
[Parameter(Mandatory = $true)][string]$Path,
|
|
[Parameter(Mandatory = $true)][string]$Content
|
|
)
|
|
|
|
$encoding = New-Object System.Text.UTF8Encoding($false)
|
|
[System.IO.File]::WriteAllText($Path, $Content, $encoding)
|
|
}
|
|
|
|
function Copy-RequiredFile {
|
|
param(
|
|
[Parameter(Mandatory = $true)][string]$Source,
|
|
[Parameter(Mandatory = $true)][string]$Destination
|
|
)
|
|
|
|
if (-not (Test-Path -LiteralPath $Source)) {
|
|
throw "Missing release resource: $Source"
|
|
}
|
|
$destinationDir = Split-Path -Parent $Destination
|
|
if ($destinationDir -and -not (Test-Path -LiteralPath $destinationDir)) {
|
|
New-Item -ItemType Directory -Force -Path $destinationDir | Out-Null
|
|
}
|
|
try {
|
|
Copy-Item -LiteralPath $Source -Destination $Destination -Force -ErrorAction Stop
|
|
} catch {
|
|
if (Test-Path -LiteralPath $Destination) {
|
|
$sourceInfo = Get-Item -LiteralPath $Source
|
|
$destinationInfo = Get-Item -LiteralPath $Destination
|
|
if ($sourceInfo.Length -eq $destinationInfo.Length) {
|
|
Write-Warning "Could not overwrite ${Destination}; existing file has the same size and will be kept. $($_.Exception.Message)"
|
|
return
|
|
}
|
|
}
|
|
throw
|
|
}
|
|
}
|
|
|
|
function Stop-RunningReleaseProcesses {
|
|
$processes = Get-Process -Name "qiweimanager", "helper", "helper_auto_reply" -ErrorAction SilentlyContinue
|
|
foreach ($process in $processes) {
|
|
Write-Host "==> Stopping running release process: $($process.ProcessName) ($($process.Id))"
|
|
Stop-Process -Id $process.Id -Force -ErrorAction Stop
|
|
}
|
|
}
|
|
|
|
Set-Location $repoRoot
|
|
New-Item -ItemType Directory -Force -Path (Join-Path $repoRoot ".gocache") | Out-Null
|
|
$env:GOCACHE = (Resolve-Path (Join-Path $repoRoot ".gocache")).Path
|
|
|
|
$wailsFallback = if ($WailsPath) { $WailsPath } else { Join-Path $env:USERPROFILE "go\bin\wails.exe" }
|
|
$wails = Resolve-RequiredTool -Name "wails.exe" -FallbackPath $wailsFallback -InstallHint "Install Wails CLI on the build machine first."
|
|
$makensis = Resolve-RequiredTool -Name "makensis.exe" -FallbackPath $MakensisPath -InstallHint "Install NSIS on the build machine first, e.g. run as Administrator: choco install nsis -y; or pass -MakensisPath."
|
|
$npm = Resolve-RequiredTool -Name "npm.cmd" -InstallHint "Install Node.js on the build machine first."
|
|
$go = Resolve-RequiredTool -Name "go.exe" -InstallHint "Install Go on the build machine first."
|
|
$pdftoppm = Get-Command "pdftoppm.exe" -ErrorAction SilentlyContinue
|
|
$env:PATH = "$(Split-Path -Parent $makensis);$env:PATH"
|
|
|
|
Write-Host "==> Release root: $repoRoot"
|
|
Write-Host "==> Wails: $wails"
|
|
Write-Host "==> NSIS: $makensis"
|
|
|
|
if (-not $SkipTests) {
|
|
Write-Host "==> Running Go tests"
|
|
& $go test ./...
|
|
}
|
|
|
|
New-Item -ItemType Directory -Force -Path $binDir | Out-Null
|
|
|
|
Stop-RunningReleaseProcesses
|
|
|
|
Write-Host "==> Building 32-bit helper.exe"
|
|
$helperOut = Join-Path $binDir "helper.exe"
|
|
$oldGoArch = $env:GOARCH
|
|
try {
|
|
$env:GOARCH = "386"
|
|
Push-Location (Join-Path $repoRoot "helper")
|
|
& $go build -trimpath -ldflags "-H windowsgui -s -w" -o $helperOut .
|
|
} finally {
|
|
Pop-Location
|
|
if ($null -eq $oldGoArch) {
|
|
Remove-Item Env:GOARCH -ErrorAction SilentlyContinue
|
|
} else {
|
|
$env:GOARCH = $oldGoArch
|
|
}
|
|
}
|
|
|
|
Write-Host "==> Building bundled silk decoder"
|
|
$silkDecoderOut = Join-Path $binDir "tools\audio\silkdecode.exe"
|
|
New-Item -ItemType Directory -Force -Path (Split-Path $silkDecoderOut) | Out-Null
|
|
Push-Location (Join-Path $repoRoot "tools\audio\silkdecode")
|
|
try {
|
|
& $go build -trimpath -ldflags "-s -w" -o $silkDecoderOut .
|
|
} finally {
|
|
Pop-Location
|
|
}
|
|
|
|
if ($pdftoppm) {
|
|
Write-Host "==> Copying PDF renderer"
|
|
Copy-RequiredFile -Source $pdftoppm.Source -Destination (Join-Path $binDir "tools\pdf\pdftoppm.exe")
|
|
} else {
|
|
Write-Warning "pdftoppm.exe not found; scanned PDF OCR fallback will be unavailable in this build."
|
|
}
|
|
|
|
Write-Host "==> Copying DLLs to build\bin"
|
|
$helperDll = Join-Path $repoRoot "Helper_4.1.33.6009.dll"
|
|
$loaderDll = Join-Path $repoRoot "Loader_4.1.33.6009.dll"
|
|
Copy-RequiredFile -Source $helperDll -Destination (Join-Path $binDir "Helper_4.1.33.6009.dll")
|
|
Copy-RequiredFile -Source $loaderDll -Destination (Join-Path $binDir "Loader_4.1.33.6009.dll")
|
|
|
|
foreach ($staleFile in @("helper_auto_reply.exe", "qiweimanager.exe~")) {
|
|
$path = Join-Path $binDir $staleFile
|
|
if (Test-Path -LiteralPath $path) {
|
|
try {
|
|
Remove-Item -LiteralPath $path -Force -ErrorAction Stop
|
|
} catch {
|
|
Write-Warning "Could not remove stale file ${path}: $($_.Exception.Message)"
|
|
}
|
|
}
|
|
}
|
|
|
|
if (-not $SkipFrontendBuild) {
|
|
Write-Host "==> Building frontend"
|
|
Push-Location (Join-Path $repoRoot "frontend")
|
|
try {
|
|
& $npm run build
|
|
} finally {
|
|
Pop-Location
|
|
}
|
|
}
|
|
|
|
Write-Host "==> Staging NSIS runtime resources"
|
|
Reset-Directory -Path $runtimeDir -Parent $installerDir
|
|
New-Item -ItemType Directory -Force -Path (Join-Path $runtimeDir "config\knowledge") | Out-Null
|
|
New-Item -ItemType Directory -Force -Path (Join-Path $runtimeDir "config\materials") | Out-Null
|
|
|
|
Copy-RequiredFile -Source $helperOut -Destination (Join-Path $runtimeDir "helper.exe")
|
|
Copy-RequiredFile -Source $silkDecoderOut -Destination (Join-Path $runtimeDir "tools\audio\silkdecode.exe")
|
|
if (Test-Path -LiteralPath (Join-Path $binDir "tools\pdf\pdftoppm.exe")) {
|
|
Copy-RequiredFile -Source (Join-Path $binDir "tools\pdf\pdftoppm.exe") -Destination (Join-Path $runtimeDir "tools\pdf\pdftoppm.exe")
|
|
}
|
|
Copy-RequiredFile -Source (Join-Path $binDir "Helper_4.1.33.6009.dll") -Destination (Join-Path $runtimeDir "Helper_4.1.33.6009.dll")
|
|
Copy-RequiredFile -Source (Join-Path $binDir "Loader_4.1.33.6009.dll") -Destination (Join-Path $runtimeDir "Loader_4.1.33.6009.dll")
|
|
|
|
Copy-Item -LiteralPath (Join-Path $repoRoot "requestdata") -Destination (Join-Path $runtimeDir "requestdata") -Recurse -Force
|
|
Copy-Item -LiteralPath (Join-Path $repoRoot "eventdata") -Destination (Join-Path $runtimeDir "eventdata") -Recurse -Force
|
|
|
|
$defaultConfig = @'
|
|
{
|
|
"callbackConfig": {
|
|
"callbackUrl": "",
|
|
"callbackToken": "",
|
|
"httpPort": "10001",
|
|
"enableCallback": false,
|
|
"enableCloudAuth": false,
|
|
"fileUploadUrl": "",
|
|
"deviceCode": ""
|
|
},
|
|
"lastUpdated": 0
|
|
}
|
|
'@
|
|
Write-Utf8NoBom -Path (Join-Path $runtimeDir "config\config.json") -Content $defaultConfig
|
|
Write-Utf8NoBom -Path (Join-Path $runtimeDir "config\client_status.json") -Content "{}"
|
|
|
|
$knowledgeKeep = Join-Path $repoRoot "config\knowledge\.keep"
|
|
if (Test-Path -LiteralPath $knowledgeKeep) {
|
|
Copy-Item -LiteralPath $knowledgeKeep -Destination (Join-Path $runtimeDir "config\knowledge\.keep") -Force
|
|
} else {
|
|
Write-Utf8NoBom -Path (Join-Path $runtimeDir "config\knowledge\.keep") -Content "placeholder for installer-created knowledge directory"
|
|
}
|
|
|
|
$materialsIndex = Join-Path $repoRoot "config\materials\materials.json"
|
|
if (Test-Path -LiteralPath $materialsIndex) {
|
|
Copy-RequiredFile -Source $materialsIndex -Destination (Join-Path $runtimeDir "config\materials\materials.json")
|
|
} else {
|
|
Write-Utf8NoBom -Path (Join-Path $runtimeDir "config\materials\materials.json") -Content "{`"materials`":[]}"
|
|
}
|
|
|
|
Write-Host "==> Building Wails NSIS installer"
|
|
& $wails build --nsis -webview2 embed -trimpath
|
|
|
|
$installer = Join-Path $binDir "qiweimanager-amd64-installer.exe"
|
|
if (-not (Test-Path -LiteralPath $installer)) {
|
|
throw "Installer was not generated: $installer"
|
|
}
|
|
|
|
Write-Host "==> Release complete: $installer"
|