123ba0d0fa
- auth/jwt/header 认证流程更新,会话与令牌处理增强 - 通信(ChatWindow/ConversationList/Network)更新 - tauri backend.rs/remote.rs、tauri.conf.json 更新 - pineagents app 路由(agent_gate/opc)与配置更新
264 lines
8.9 KiB
PowerShell
264 lines
8.9 KiB
PowerShell
# Build YunOPC-Hub with Tauri for Windows (PyInstaller backend)
|
|
# Creates a self-contained desktop app with bundled Python backend
|
|
#
|
|
# Usage:
|
|
# powershell ./scripts/pack-tauri/build_win_pyinstaller.ps1
|
|
|
|
param()
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
$REPO_ROOT = Split-Path -Parent (Split-Path -Parent $PSScriptRoot)
|
|
Set-Location $REPO_ROOT
|
|
|
|
$DIST = if ($env:DIST) { $env:DIST } else { "dist" }
|
|
if (-not [System.IO.Path]::IsPathRooted($DIST)) {
|
|
$DIST = Join-Path $REPO_ROOT $DIST
|
|
}
|
|
# 版本号唯一来源:项目根目录 VERSION 文件
|
|
$VERSION_FILE = "VERSION"
|
|
|
|
function Invoke-NativeWithRetry {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Description,
|
|
[Parameter(Mandatory = $true)]
|
|
[scriptblock]$Command,
|
|
[int]$MaxAttempts = 5,
|
|
[int]$DelaySeconds = 20
|
|
)
|
|
|
|
for ($attempt = 1; $attempt -le $MaxAttempts; $attempt++) {
|
|
Write-Host "$Description (attempt $attempt/$MaxAttempts)..."
|
|
& $Command
|
|
$exitCode = $LASTEXITCODE
|
|
if ($exitCode -eq 0) {
|
|
return
|
|
}
|
|
|
|
if ($attempt -eq $MaxAttempts) {
|
|
throw "$Description failed after $MaxAttempts attempts (exit code $exitCode)"
|
|
}
|
|
|
|
Write-Host "$Description failed with exit code $exitCode; retrying in $DelaySeconds seconds..." -ForegroundColor Yellow
|
|
Start-Sleep -Seconds $DelaySeconds
|
|
}
|
|
}
|
|
|
|
# Extract version from VERSION file (single source of truth)
|
|
if (Test-Path $VERSION_FILE) {
|
|
$VERSION = (Get-Content $VERSION_FILE -Raw).Trim()
|
|
if (-not $VERSION) {
|
|
throw "VERSION file is empty: $VERSION_FILE"
|
|
}
|
|
} else {
|
|
throw "Version file not found: $VERSION_FILE"
|
|
}
|
|
|
|
Write-Host "=========================================" -ForegroundColor Cyan
|
|
Write-Host "YunOPC-Hub Tauri Build - Windows (PyInstaller)" -ForegroundColor Cyan
|
|
Write-Host "=========================================" -ForegroundColor Cyan
|
|
Write-Host "Version: $VERSION"
|
|
Write-Host ""
|
|
|
|
# Step 0: Prerequisites
|
|
Write-Host "== Step 0: Checking Prerequisites ==" -ForegroundColor Yellow
|
|
$missing = @()
|
|
|
|
# npm
|
|
if (-not (Get-Command npm -ErrorAction SilentlyContinue)) {
|
|
Write-Host " [MISSING] npm" -ForegroundColor Red
|
|
Write-Host " Install Node.js: https://nodejs.org/" -ForegroundColor Gray
|
|
$missing += "npm"
|
|
} else {
|
|
Write-Host " [OK] npm ($(npm --version))" -ForegroundColor Green
|
|
}
|
|
|
|
# rustc
|
|
if (-not (Get-Command rustc -ErrorAction SilentlyContinue)) {
|
|
Write-Host " [MISSING] rustc (Rust)" -ForegroundColor Red
|
|
Write-Host " Install: https://rustup.rs" -ForegroundColor Gray
|
|
$missing += "rustc"
|
|
} else {
|
|
Write-Host " [OK] rustc ($(rustc --version))" -ForegroundColor Green
|
|
}
|
|
|
|
# Visual Studio Build Tools (MSVC)
|
|
$vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"
|
|
$hasMsvc = $false
|
|
if (Test-Path $vswhere) {
|
|
$vsPath = & $vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath 2>$null
|
|
if ($vsPath) { $hasMsvc = $true }
|
|
}
|
|
if (-not $hasMsvc) {
|
|
$hostTuple = & rustc --print host-tuple 2>$null
|
|
if ($hostTuple -match "msvc") { $hasMsvc = $true }
|
|
}
|
|
if (-not $hasMsvc) {
|
|
Write-Host " [MISSING] Visual Studio Build Tools (C++ workload)" -ForegroundColor Red
|
|
Write-Host " Install: https://visualstudio.microsoft.com/visual-cpp-build-tools/" -ForegroundColor Gray
|
|
Write-Host " Required workload: 'Desktop development with C++'" -ForegroundColor Gray
|
|
$missing += "MSVC"
|
|
} else {
|
|
Write-Host " [OK] Visual Studio Build Tools (MSVC)" -ForegroundColor Green
|
|
}
|
|
|
|
# NSIS (makensis)
|
|
# makensis is used by the Tauri bundler to produce the Windows installer. It is
|
|
# often installed but not added to PATH, so also check the standard locations.
|
|
$makensisCmd = $null
|
|
$makensisPath = $null
|
|
if (Get-Command makensis -ErrorAction SilentlyContinue) {
|
|
$makensisCmd = (Get-Command makensis -ErrorAction SilentlyContinue).Source
|
|
$makensisPath = $makensisCmd
|
|
}
|
|
|
|
if (-not $makensisCmd) {
|
|
$nsisCandidates = @(
|
|
"$env:ProgramFiles\NSIS\makensis.exe",
|
|
"${env:ProgramFiles(x86)}\NSIS\makensis.exe",
|
|
"$env:LOCALAPPDATA\Programs\NSIS\makensis.exe"
|
|
)
|
|
foreach ($candidate in $nsisCandidates) {
|
|
if (Test-Path $candidate) {
|
|
$makensisCmd = $candidate
|
|
$makensisPath = $candidate
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
if (-not $makensisCmd) {
|
|
Write-Host " [MISSING] makensis (NSIS)" -ForegroundColor Red
|
|
Write-Host " Install: https://nsis.sourceforge.io/Download" -ForegroundColor Gray
|
|
$missing += "makensis"
|
|
} else {
|
|
if ($makensisPath -and $makensisPath -ne "makensis") {
|
|
# Prepend the NSIS dir so makensis is resolvable by the bundler later.
|
|
$nsisDir = Split-Path -Parent $makensisPath
|
|
if ($env:PATH -notlike "*$nsisDir*") {
|
|
$env:PATH = "$nsisDir;$env:PATH"
|
|
}
|
|
}
|
|
$nsisInfo = & $makensisCmd /version 2>$null
|
|
Write-Host " [OK] makensis (NSIS $nsisInfo)" -ForegroundColor Green
|
|
}
|
|
|
|
if ($missing.Count -gt 0) {
|
|
Write-Host ""
|
|
Write-Host "Missing prerequisites: $($missing -join ', ')" -ForegroundColor Red
|
|
Write-Host "Install the missing tools and re-run this script." -ForegroundColor Red
|
|
exit 1
|
|
}
|
|
Write-Host ""
|
|
|
|
# Step 1: Build console static assets
|
|
Write-Host "== Step 1: Building Console Static Assets ==" -ForegroundColor Yellow
|
|
Set-Location console
|
|
|
|
Write-Host "Installing frontend dependencies..."
|
|
# The console is a Yarn 4 project (see "packageManager" in console/package.json and
|
|
# console/yarn.lock). Use the same immutable install as the macOS build; npm ci would
|
|
# fail here because no package-lock.json exists.
|
|
yarn install --immutable
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "yarn install --immutable failed"
|
|
}
|
|
|
|
Write-Host "Generating Tauri icons..."
|
|
npm exec -- tauri icon ../scripts/pack/assets/icon.png
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "Tauri icon generation failed"
|
|
}
|
|
|
|
Write-Host "Syncing Tauri version..."
|
|
node ../scripts/pack-tauri/sync_tauri_version.mjs
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "Tauri version sync failed"
|
|
}
|
|
|
|
Write-Host "Building console frontend..."
|
|
npm run build:prod
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "console frontend build failed"
|
|
}
|
|
|
|
Set-Location $REPO_ROOT
|
|
Write-Host "Console static assets built" -ForegroundColor Green
|
|
Write-Host ""
|
|
|
|
# Step 2: Build PyInstaller backend
|
|
Write-Host "== Step 2: Building PyInstaller Backend ==" -ForegroundColor Yellow
|
|
$PYINSTALLER_SCRIPT = Join-Path $REPO_ROOT "scripts\pack-tauri\build_pyinstaller.ps1"
|
|
& $PYINSTALLER_SCRIPT
|
|
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "PyInstaller build failed"
|
|
}
|
|
Write-Host "PyInstaller backend ready" -ForegroundColor Green
|
|
Write-Host ""
|
|
|
|
# Step 2b: Fetch Tauri Rust dependencies
|
|
Write-Host "== Step 2b: Fetching Tauri Rust Dependencies ==" -ForegroundColor Yellow
|
|
if (-not $env:CARGO_NET_RETRY) {
|
|
$env:CARGO_NET_RETRY = "10"
|
|
}
|
|
if (-not $env:CARGO_HTTP_MULTIPLEXING) {
|
|
$env:CARGO_HTTP_MULTIPLEXING = "false"
|
|
}
|
|
|
|
$TAURI_MANIFEST = Join-Path $REPO_ROOT "console\src-tauri\Cargo.toml"
|
|
Invoke-NativeWithRetry -Description "cargo fetch for Tauri dependencies" -Command {
|
|
cargo fetch --locked --target x86_64-pc-windows-msvc --manifest-path $TAURI_MANIFEST
|
|
}
|
|
$env:CARGO_NET_OFFLINE = "true"
|
|
Write-Host "Tauri Rust dependencies fetched; Cargo offline mode enabled" -ForegroundColor Green
|
|
Write-Host ""
|
|
|
|
# Step 2c: Load Tauri v2 signing private key for updater artifacts
|
|
# Tauri 原生格式私钥(tauri signer generate 生成,单行 base64,无密码)
|
|
if (-not $env:TAURI_SIGNING_PRIVATE_KEY) {
|
|
$SIGN_KEY_FILE = Join-Path $REPO_ROOT "mykey\tauri-signer.key"
|
|
if (Test-Path $SIGN_KEY_FILE) {
|
|
$env:TAURI_SIGNING_PRIVATE_KEY = (Get-Content $SIGN_KEY_FILE -Raw).Trim()
|
|
if (-not $env:TAURI_SIGNING_PRIVATE_KEY_PASSWORD) {
|
|
$env:TAURI_SIGNING_PRIVATE_KEY_PASSWORD = ""
|
|
}
|
|
Write-Host "== Step 2c: Loaded Tauri signing key from ${SIGN_KEY_FILE} =="
|
|
} else {
|
|
Write-Host "WARNING: TAURI_SIGNING_PRIVATE_KEY not set and ${SIGN_KEY_FILE} not found"
|
|
Write-Host "WARNING: Tauri updater artifacts (.sig) will NOT be generated"
|
|
}
|
|
} else {
|
|
Write-Host "== Step 2c: Using Tauri signing key from environment =="
|
|
}
|
|
Write-Host ""
|
|
|
|
# Step 3: Build Tauri app
|
|
Write-Host "== Step 3: Building Tauri App ==" -ForegroundColor Yellow
|
|
$BUNDLE_DIR = Join-Path $REPO_ROOT "console\src-tauri\target\release\bundle"
|
|
$NSIS_DIR = Join-Path $BUNDLE_DIR "nsis"
|
|
if (Test-Path $NSIS_DIR) {
|
|
Remove-Item -Recurse -Force $NSIS_DIR
|
|
}
|
|
|
|
Set-Location console
|
|
|
|
Write-Host "Building for Windows..."
|
|
npm exec -- tauri build --config src-tauri/tauri.version.conf.json
|
|
$tauriExit = $LASTEXITCODE
|
|
|
|
if ($tauriExit -ne 0) {
|
|
throw "Tauri build failed"
|
|
}
|
|
|
|
Set-Location $REPO_ROOT
|
|
Write-Host "Tauri app built" -ForegroundColor Green
|
|
|
|
Write-Host ""
|
|
Write-Host "=========================================" -ForegroundColor Cyan
|
|
Write-Host "Build Complete!" -ForegroundColor Green
|
|
Write-Host "=========================================" -ForegroundColor Cyan
|
|
Write-Host "Output:"
|
|
Write-Host " NSIS bundle directory: ${NSIS_DIR}\"
|
|
Write-Host ""
|