整理发布脚本并修复启动页打包崩溃
This commit is contained in:
@@ -0,0 +1,247 @@
|
||||
param(
|
||||
# 不传时自动从 XplorePlane.csproj 的 <Version> 读取,传入则覆盖工程配置(用于临时测试版本号)
|
||||
[string]$Version,
|
||||
|
||||
# Keep the technical PackId stable so existing Velopack installations can update.
|
||||
[string]$PackId = "Hexagon.XplorePlane",
|
||||
|
||||
[string]$PackTitle = "XplorePlane",
|
||||
|
||||
[string]$PackAuthors = "Hexagon",
|
||||
|
||||
[string]$MainExe = "XplorePlane.exe",
|
||||
|
||||
[string]$DeployDir = "C:\wamp64\www\updates\xploreplane",
|
||||
|
||||
[switch]$SkipBuild,
|
||||
|
||||
# By default also generate an MSI installer so the user can choose the install location.
|
||||
# Use -NoMsi when only the per-user Setup.exe is required.
|
||||
[switch]$NoMsi,
|
||||
|
||||
[ValidateSet("Either", "PerUser", "PerMachine")]
|
||||
[string]$InstallLocation = "Either",
|
||||
|
||||
[string]$Configuration = "Release",
|
||||
|
||||
[string]$RuntimeId = "win-x64"
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$RepoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path
|
||||
$ProjectPath = Join-Path $RepoRoot "XplorePlane\XplorePlane.csproj"
|
||||
$PublishDir = Join-Path $RepoRoot "publish"
|
||||
$ReleasesDir = Join-Path $RepoRoot "Releases"
|
||||
|
||||
# 版本号来源:命令行 -Version 优先,否则从 csproj 的 <Version> 属性读取,
|
||||
# 确保发布包版本号与工程配置保持单一来源,不再需要每次手动传参维护。
|
||||
if (-not $Version) {
|
||||
Write-Host "Reading version from csproj..." -ForegroundColor DarkGray
|
||||
$Version = (dotnet msbuild $ProjectPath -nologo -getProperty:Version).Trim()
|
||||
if ($LASTEXITCODE -ne 0 -or -not $Version) {
|
||||
Write-Host "FAILED: Could not read <Version> from $ProjectPath. Pass -Version explicitly or add <Version> to the csproj." -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "========================================================" -ForegroundColor Cyan
|
||||
Write-Host " XplorePlane Build and Release Script" -ForegroundColor Cyan
|
||||
Write-Host "========================================================" -ForegroundColor Cyan
|
||||
Write-Host " Version: $Version" -ForegroundColor Cyan
|
||||
Write-Host " PackId: $PackId" -ForegroundColor Cyan
|
||||
Write-Host " PackTitle: $PackTitle" -ForegroundColor Cyan
|
||||
Write-Host " Publisher: $PackAuthors" -ForegroundColor Cyan
|
||||
Write-Host " Config: $Configuration" -ForegroundColor Cyan
|
||||
Write-Host " Runtime: $RuntimeId" -ForegroundColor Cyan
|
||||
Write-Host " MSI: $(-not $NoMsi)" -ForegroundColor Cyan
|
||||
if (-not $NoMsi) { Write-Host " Install scope: $InstallLocation" -ForegroundColor Cyan }
|
||||
Write-Host " DeployDir: $DeployDir" -ForegroundColor Cyan
|
||||
Write-Host "========================================================" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
|
||||
# Step 1: Clean
|
||||
Write-Host "[1/5] Cleaning publish directory..." -ForegroundColor Yellow
|
||||
if (Test-Path $PublishDir) { Remove-Item $PublishDir -Recurse -Force }
|
||||
|
||||
# Step 2: Build and Publish
|
||||
if (-not $SkipBuild) {
|
||||
Write-Host "[2/5] Building and publishing ($Configuration, $RuntimeId)..." -ForegroundColor Yellow
|
||||
dotnet publish $ProjectPath -c $Configuration --self-contained -r $RuntimeId -o $PublishDir -p:Version=$Version
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Host "FAILED: dotnet publish failed!" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
Write-Host "OK: Build succeeded" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "[2/5] Skipped build (-SkipBuild)" -ForegroundColor DarkGray
|
||||
if (-not (Test-Path $PublishDir)) {
|
||||
Write-Host "FAILED: publish directory not found, run without -SkipBuild first" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
# Ensure the packaged application starts with the virtual navigation camera.
|
||||
# Navigation camera settings are stored in config.json, not App.config.
|
||||
$cameraConfigPath = Join-Path $PublishDir "config.json"
|
||||
$cameraConfig = [ordered]@{
|
||||
Language = "zh-CN"
|
||||
LogLevel = "Debug"
|
||||
CameraType = "Simulated"
|
||||
CameraSimulatedImagePath = ""
|
||||
}
|
||||
if (Test-Path -LiteralPath $cameraConfigPath) {
|
||||
try {
|
||||
$existingCameraConfig = Get-Content -LiteralPath $cameraConfigPath -Raw | ConvertFrom-Json
|
||||
foreach ($property in $existingCameraConfig.PSObject.Properties) {
|
||||
$cameraConfig[$property.Name] = $property.Value
|
||||
}
|
||||
} catch {
|
||||
Write-Host " WARN: Existing config.json is invalid; recreating camera defaults." -ForegroundColor Yellow
|
||||
}
|
||||
}
|
||||
$cameraConfig["CameraType"] = "Simulated"
|
||||
$cameraConfig["CameraSimulatedImagePath"] = ""
|
||||
$cameraConfig | ConvertTo-Json -Depth 4 | Set-Content -LiteralPath $cameraConfigPath -Encoding UTF8
|
||||
Write-Host "OK: Packaged navigation camera default: Simulated" -ForegroundColor Green
|
||||
|
||||
function Rename-ExistingMsi {
|
||||
param(
|
||||
[string]$Directory,
|
||||
[string]$PackageId
|
||||
)
|
||||
|
||||
$legacyMsi = Join-Path $Directory "$PackageId-win.msi"
|
||||
if (-not (Test-Path -LiteralPath $legacyMsi)) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
$installer = New-Object -ComObject WindowsInstaller.Installer
|
||||
$database = $installer.OpenDatabase($legacyMsi, 0)
|
||||
$view = $database.OpenView("SELECT Value FROM Property WHERE Property='ProductVersion'")
|
||||
$view.Execute()
|
||||
$record = $view.Fetch()
|
||||
$oldVersion = $record.StringData(1)
|
||||
}
|
||||
catch {
|
||||
throw "无法读取旧 MSI 的 ProductVersion:$legacyMsi。$($_.Exception.Message)"
|
||||
}
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($oldVersion)) {
|
||||
throw "旧 MSI 未包含有效的 ProductVersion:$legacyMsi"
|
||||
}
|
||||
|
||||
$versionedMsi = Join-Path $Directory "XplorePlane-$oldVersion-win.msi"
|
||||
if (Test-Path -LiteralPath $versionedMsi) {
|
||||
throw "旧 MSI 的版本化文件已存在,为避免覆盖而停止:$versionedMsi"
|
||||
}
|
||||
|
||||
Move-Item -LiteralPath $legacyMsi -Destination $versionedMsi
|
||||
Write-Host " Archived existing MSI: $versionedMsi" -ForegroundColor DarkGray
|
||||
}
|
||||
|
||||
# Step 3: Velopack Pack
|
||||
Write-Host "[3/5] Packing with vpk..." -ForegroundColor Yellow
|
||||
|
||||
$vpkCmd = Get-Command vpk -ErrorAction SilentlyContinue
|
||||
if (-not $vpkCmd) {
|
||||
Write-Host " vpk not found, installing..." -ForegroundColor Yellow
|
||||
dotnet tool install -g vpk
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Host "FAILED: vpk install failed" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
if (-not $NoMsi) {
|
||||
Rename-ExistingMsi -Directory $ReleasesDir -PackageId $PackId
|
||||
}
|
||||
|
||||
$vpkArgs = @(
|
||||
"pack",
|
||||
"--packId", $PackId,
|
||||
"--packTitle", $PackTitle,
|
||||
"--packAuthors", $PackAuthors,
|
||||
"--packVersion", $Version,
|
||||
"--packDir", $PublishDir,
|
||||
"--mainExe", $MainExe
|
||||
)
|
||||
|
||||
if (-not $NoMsi) {
|
||||
# Setup.exe remains the per-user one-click installer. MSI with Either shows
|
||||
# the Windows Installer location selection page to the end user.
|
||||
$vpkArgs += "--msi"
|
||||
$vpkArgs += "--instLocation"
|
||||
$vpkArgs += $InstallLocation
|
||||
}
|
||||
|
||||
& vpk @vpkArgs
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Host "FAILED: vpk pack failed!" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
if (-not $NoMsi) {
|
||||
$generatedMsi = Join-Path $ReleasesDir "$PackId-win.msi"
|
||||
$versionedMsi = Join-Path $ReleasesDir "XplorePlane-$Version-win.msi"
|
||||
if (Test-Path -LiteralPath $generatedMsi) {
|
||||
Move-Item -LiteralPath $generatedMsi -Destination $versionedMsi -Force
|
||||
Write-Host "OK: MSI archived as $versionedMsi" -ForegroundColor Green
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "OK: Pack succeeded, output in $ReleasesDir" -ForegroundColor Green
|
||||
|
||||
# Step 4: Deploy to WampServer
|
||||
Write-Host "[4/5] Deploying to WampServer..." -ForegroundColor Yellow
|
||||
|
||||
if (-not (Test-Path $DeployDir)) {
|
||||
New-Item -ItemType Directory -Path $DeployDir -Force | Out-Null
|
||||
Write-Host " Created directory: $DeployDir" -ForegroundColor DarkGray
|
||||
}
|
||||
|
||||
Copy-Item -Path (Join-Path $ReleasesDir "*") -Destination $DeployDir -Force -Recurse
|
||||
Write-Host "OK: Deployed to $DeployDir" -ForegroundColor Green
|
||||
|
||||
# Step 5: Verify
|
||||
Write-Host "[5/5] Verifying deployment..." -ForegroundColor Yellow
|
||||
$releasesFile = Join-Path $DeployDir "RELEASES"
|
||||
if (Test-Path $releasesFile) {
|
||||
Write-Host " OK: RELEASES file exists" -ForegroundColor Green
|
||||
$fullPkg = Get-ChildItem $DeployDir -Filter "*-full.nupkg" | Sort-Object LastWriteTime -Descending | Select-Object -First 1
|
||||
if ($fullPkg) {
|
||||
$sizeMB = [math]::Round($fullPkg.Length / 1MB, 1)
|
||||
Write-Host " OK: Full package: $($fullPkg.Name) ($sizeMB MB)" -ForegroundColor Green
|
||||
}
|
||||
$deltaPkg = Get-ChildItem $DeployDir -Filter "*-delta.nupkg" | Sort-Object LastWriteTime -Descending | Select-Object -First 1
|
||||
if ($deltaPkg) {
|
||||
$sizeMB = [math]::Round($deltaPkg.Length / 1MB, 1)
|
||||
Write-Host " OK: Delta package: $($deltaPkg.Name) ($sizeMB MB)" -ForegroundColor Green
|
||||
}
|
||||
$setupExe = Get-ChildItem $DeployDir -Filter "Setup*.exe" | Select-Object -First 1
|
||||
if ($setupExe) {
|
||||
Write-Host " OK: Setup: $($setupExe.Name)" -ForegroundColor Green
|
||||
}
|
||||
$msi = Get-ChildItem $DeployDir -Filter "*.msi" | Sort-Object LastWriteTime -Descending | Select-Object -First 1
|
||||
if ($msi) {
|
||||
Write-Host " OK: MSI: $($msi.Name)" -ForegroundColor Green
|
||||
}
|
||||
} else {
|
||||
Write-Host " WARN: RELEASES file not found, deployment may be incomplete" -ForegroundColor Yellow
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "========================================================" -ForegroundColor Cyan
|
||||
Write-Host " Release complete!" -ForegroundColor Green
|
||||
Write-Host ""
|
||||
Write-Host " Update URL: http://192.168.16.134/updates/xploreplane" -ForegroundColor White
|
||||
Write-Host " Setup: $DeployDir\Setup.exe (per-user, fixed default location)" -ForegroundColor White
|
||||
if (-not $NoMsi) {
|
||||
Write-Host " MSI: $DeployDir\*.msi (user-selectable location)" -ForegroundColor White
|
||||
}
|
||||
Write-Host ""
|
||||
Write-Host " Make sure WampServer is running (Apache online)." -ForegroundColor DarkGray
|
||||
Write-Host " Client App.config Update:ServerUrl should be:" -ForegroundColor DarkGray
|
||||
Write-Host " http://<server-ip>/updates/xploreplane" -ForegroundColor White
|
||||
Write-Host "========================================================" -ForegroundColor Cyan
|
||||
@@ -0,0 +1,81 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory = $true, Position = 0)]
|
||||
[ValidatePattern('^\d+\.\d+\.\d+$')]
|
||||
[string]$Version,
|
||||
|
||||
[switch]$Force
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$releasesDir = Join-Path $PSScriptRoot '..\Releases' | Resolve-Path
|
||||
$packagePattern = "*-{0}-full.nupkg" -f $Version
|
||||
$packages = @(Get-ChildItem -LiteralPath $releasesDir -Filter $packagePattern -File)
|
||||
|
||||
if ($packages.Count -eq 0) {
|
||||
throw "Full package not found for version $Version`: $releasesDir\$packagePattern"
|
||||
}
|
||||
|
||||
if ($packages.Count -gt 1) {
|
||||
throw "Multiple full packages found for version $Version`: $($packages.FullName -join ', ')"
|
||||
}
|
||||
|
||||
$vpk = Get-Command vpk -ErrorAction SilentlyContinue
|
||||
if (-not $vpk) {
|
||||
throw 'vpk was not found. Install it with: dotnet tool install -g vpk'
|
||||
}
|
||||
|
||||
$outputDir = Join-Path $releasesDir "MSI\$Version"
|
||||
$tempDir = Join-Path ([IO.Path]::GetTempPath()) ("xploreplane-msi-" + [Guid]::NewGuid().ToString('N'))
|
||||
$targetMsi = Join-Path $outputDir "XplorePlane-$Version-win.msi"
|
||||
|
||||
try {
|
||||
New-Item -ItemType Directory -Path $tempDir -Force | Out-Null
|
||||
New-Item -ItemType Directory -Path $outputDir -Force | Out-Null
|
||||
|
||||
if ((Test-Path -LiteralPath $targetMsi) -and -not $Force) {
|
||||
throw "Target MSI already exists: $targetMsi. Use -Force to overwrite it."
|
||||
}
|
||||
|
||||
Write-Host "Extracting: $($packages[0].Name)"
|
||||
Add-Type -AssemblyName System.IO.Compression.FileSystem
|
||||
[IO.Compression.ZipFile]::ExtractToDirectory($packages[0].FullName, $tempDir)
|
||||
|
||||
$packDir = Join-Path $tempDir 'lib\app'
|
||||
if (-not (Test-Path -LiteralPath (Join-Path $packDir 'XplorePlane.exe'))) {
|
||||
throw "The package does not contain the application directory or main executable: $packDir"
|
||||
}
|
||||
|
||||
Write-Host "Generating MSI in: $outputDir"
|
||||
& $vpk.Source pack `
|
||||
--packId Hexagon.XplorePlane `
|
||||
--packTitle XplorePlane `
|
||||
--packAuthors Hexagon `
|
||||
--packVersion $Version `
|
||||
--packDir $packDir `
|
||||
--mainExe XplorePlane.exe `
|
||||
--msi `
|
||||
--msiVersion $Version `
|
||||
--instLocation Either `
|
||||
--outputDir $outputDir
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "vpk pack failed with exit code: $LASTEXITCODE"
|
||||
}
|
||||
|
||||
$generatedMsi = @(Get-ChildItem -LiteralPath $outputDir -Filter '*.msi' -File)
|
||||
if ($generatedMsi.Count -ne 1) {
|
||||
throw "Expected one MSI, found $($generatedMsi.Count)."
|
||||
}
|
||||
|
||||
if ($generatedMsi[0].FullName -ne $targetMsi) {
|
||||
Move-Item -LiteralPath $generatedMsi[0].FullName -Destination $targetMsi -Force:$Force
|
||||
}
|
||||
|
||||
Write-Host "Completed: $targetMsi" -ForegroundColor Green
|
||||
}
|
||||
finally {
|
||||
if (Test-Path -LiteralPath $tempDir) {
|
||||
Remove-Item -LiteralPath $tempDir -Recurse -Force
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user