From 9acbbedcab0ff34bf056bad463f7e1915875b0a3 Mon Sep 17 00:00:00 2001 From: hmtdpetn <3127490962@qq.com> Date: Wed, 10 Jun 2026 12:57:04 +0800 Subject: [PATCH] feat(build): add Windows portable zip packaging --- README.md | 12 ++++ bat/publish.ps1 | 149 ++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 142 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index cd3654e3..f533d38c 100644 --- a/README.md +++ b/README.md @@ -164,6 +164,18 @@ Ant Browser 适合以下场景: - `bat\dev.bat limited`:在 `live` 基础上为 watcher 与其子进程附加 Windows Job Object 内存限制 - 如需为依赖下载配置代理,可在启动前设置 `DEV_PROXY_URL`、`DEV_NO_PROXY`、`DEV_GOPROXY` +### Windows 发布打包(源码) + +Windows 发布脚本默认保持原有 NSIS 安装包行为,也可以生成便携 ZIP,或一次生成两种产物: + +```powershell +bat\publish.bat -Target WINDOWS -WindowsFormat INSTALLER +bat\publish.bat -Target WINDOWS -WindowsFormat PORTABLE +bat\publish.bat -Target WINDOWS -WindowsFormat BOTH +``` + +省略 `-WindowsFormat` 时等同于 `INSTALLER`。安装包和便携 ZIP 输出到 `publish\output\`。 + ### Linux 发布打包(源码) Linux 发布脚本位于 `publish/linux/`。 diff --git a/bat/publish.ps1 b/bat/publish.ps1 index 2a6e2203..3c545582 100644 --- a/bat/publish.ps1 +++ b/bat/publish.ps1 @@ -1,6 +1,8 @@ param( [string]$Target, - [string]$Version + [string]$Version, + [ValidateSet("INSTALLER", "PORTABLE", "BOTH")] + [string]$WindowsFormat = "INSTALLER" ) Set-StrictMode -Version Latest @@ -9,9 +11,10 @@ $ErrorActionPreference = "Stop" $repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path Set-Location $repoRoot -$script:Version = "" +$script:ResolvedVersion = "" $script:LinuxArch = "" -$script:WindowsDone = $false +$script:WindowsInstallerDone = $false +$script:WindowsPortableDone = $false $script:LinuxDone = $false function Write-Section { @@ -88,9 +91,9 @@ function Resolve-Version { $explicit = Get-TrimmedText $ExplicitVersion if ($explicit -ne "") { - $script:Version = Assert-VersionValue -Value $explicit -Source "传入版本号" + $script:ResolvedVersion = Assert-VersionValue -Value $explicit -Source "传入版本号" Write-Host "[1/3] 使用传入版本号..." - Write-Host "✓ 版本号: $script:Version" + Write-Host "✓ 版本号: $script:ResolvedVersion" Write-Host "" return } @@ -107,8 +110,8 @@ function Resolve-Version { throw "无法从 wails.json 读取版本号" } - $script:Version = Assert-VersionValue -Value $resolvedVersion -Source "wails.json productVersion" - Write-Host "✓ 版本号: $script:Version" + $script:ResolvedVersion = Assert-VersionValue -Value $resolvedVersion -Source "wails.json productVersion" + Write-Host "✓ 版本号: $script:ResolvedVersion" Write-Host "" } @@ -126,15 +129,15 @@ function Invoke-WithTemporaryWailsVersion { $currentConfig = Get-Content -LiteralPath $wailsConfigPath -Raw | ConvertFrom-Json $currentVersion = Get-TrimmedText ([string]$currentConfig.info.productVersion) - if ($currentVersion -eq $script:Version) { + if ($currentVersion -eq $script:ResolvedVersion) { & $ScriptBlock return } - Write-Host " 临时覆盖 wails.json productVersion: $currentVersion -> $script:Version" + Write-Host " 临时覆盖 wails.json productVersion: $currentVersion -> $script:ResolvedVersion" $originalBytes = [System.IO.File]::ReadAllBytes($wailsConfigPath) try { - $currentConfig.info.productVersion = $script:Version + $currentConfig.info.productVersion = $script:ResolvedVersion $utf8NoBom = New-Object System.Text.UTF8Encoding($false) $jsonText = ($currentConfig | ConvertTo-Json -Depth 100) [System.IO.File]::WriteAllText($wailsConfigPath, $jsonText + "`n", $utf8NoBom) @@ -190,6 +193,19 @@ function Resolve-PublishTarget { } } +function Resolve-WindowsFormat { + param([string]$InputFormat) + + $normalized = (Get-TrimmedText $InputFormat).ToUpperInvariant() + if ($normalized -eq "") { + return "INSTALLER" + } + if ($normalized -notin @("INSTALLER", "PORTABLE", "BOTH")) { + throw "无效的 Windows 输出格式: $InputFormat`n 支持参数: INSTALLER/PORTABLE/BOTH" + } + return $normalized +} + function Resolve-NsisPath { Write-Host "[Windows] 检测 NSIS 安装..." Write-Host " 支持环境变量:MAKENSIS_PATH / NSIS_PATH / NSIS_HOME" @@ -505,7 +521,7 @@ function Invoke-WindowsPackaging { Write-Host " NSIS 全局配置: disabled (/NOCONFIG)" } $nsisArguments = @( - "/DVERSION=$script:Version", + "/DVERSION=$script:ResolvedVersion", "/DSTAGINGDIR=$stagingAbs", "publish\installer.nsi" ) @@ -521,6 +537,83 @@ function Invoke-WindowsPackaging { Write-Host "" } +function New-WindowsPortableArchive { + param( + [Parameter(Mandatory = $true)] + [string]$StagingDir + ) + + Write-Host "[Windows] 生成便携 ZIP..." + $outputDir = Join-Path $repoRoot "publish/output" + if (-not (Test-Path -LiteralPath $outputDir)) { + New-Item -ItemType Directory -Path $outputDir -Force | Out-Null + } + + $archiveName = "AntBrowser-$script:ResolvedVersion-windows-amd64-portable.zip" + $archivePath = Join-Path $outputDir $archiveName + $rootName = "AntBrowser-$script:ResolvedVersion-windows-amd64-portable" + if (Test-Path -LiteralPath $archivePath) { + Remove-Item -LiteralPath $archivePath -Force + } + + Add-Type -AssemblyName System.IO.Compression + $archiveStream = [System.IO.File]::Open( + $archivePath, + [System.IO.FileMode]::CreateNew, + [System.IO.FileAccess]::Write, + [System.IO.FileShare]::None + ) + try { + $archive = New-Object System.IO.Compression.ZipArchive( + $archiveStream, + [System.IO.Compression.ZipArchiveMode]::Create, + $false + ) + try { + $archive.CreateEntry("$rootName/") | Out-Null + + foreach ($directory in (Get-ChildItem -LiteralPath $StagingDir -Directory -Recurse -Force)) { + $relativePath = $directory.FullName.Substring($StagingDir.Length).TrimStart('\', '/') + $entryName = "$rootName/$($relativePath.Replace('\', '/'))/" + $archive.CreateEntry($entryName) | Out-Null + } + + foreach ($file in (Get-ChildItem -LiteralPath $StagingDir -File -Recurse -Force)) { + $relativePath = $file.FullName.Substring($StagingDir.Length).TrimStart('\', '/') + $entryName = "$rootName/$($relativePath.Replace('\', '/'))" + $entry = $archive.CreateEntry( + $entryName, + [System.IO.Compression.CompressionLevel]::Optimal + ) + $inputStream = [System.IO.File]::OpenRead($file.FullName) + $outputStream = $entry.Open() + try { + $inputStream.CopyTo($outputStream) + } + finally { + $outputStream.Dispose() + $inputStream.Dispose() + } + } + } + finally { + $archive.Dispose() + } + } + finally { + $archiveStream.Dispose() + } + + if (-not (Test-Path -LiteralPath $archivePath -PathType Leaf)) { + throw "便携 ZIP 生成失败: $archivePath" + } + + $script:WindowsPortableDone = $true + Write-Host "✓ Windows 便携包生成成功: publish\output\$archiveName" + Write-Host "" + return $archivePath +} + function Remove-WindowsStaging { param([string]$StagingDir) @@ -533,23 +626,37 @@ function Remove-WindowsStaging { } function Publish-Windows { + param( + [Parameter(Mandatory = $true)] + [string]$Format + ) + Write-Host "[3/3] 开始 Windows 打包..." + Write-Host " 输出格式: $Format" Write-Host "" - $makensisPath = Resolve-NsisPath + $makensisPath = $null + if ($Format -in @("INSTALLER", "BOTH")) { + $makensisPath = Resolve-NsisPath + } Assert-RuntimeHashes -Target "windows-amd64" Build-WindowsBinary $stagingDir = $null try { $stagingDir = New-WindowsStaging - Invoke-WindowsPackaging -MakensisPath $makensisPath -StagingDir $stagingDir + if ($Format -in @("INSTALLER", "BOTH")) { + Invoke-WindowsPackaging -MakensisPath $makensisPath -StagingDir $stagingDir + $script:WindowsInstallerDone = $true + } + if ($Format -in @("PORTABLE", "BOTH")) { + New-WindowsPortableArchive -StagingDir $stagingDir | Out-Null + } } finally { Remove-WindowsStaging -StagingDir $stagingDir } - $script:WindowsDone = $true Write-Host "✓ Windows 打包完成" Write-Host "" } @@ -565,7 +672,7 @@ function Publish-Linux { } try { - & powershell -NoProfile -ExecutionPolicy Bypass -File $linuxScript -RepoRoot $repoRoot -ArchOutFile $archOutFile -Version $script:Version + & powershell -NoProfile -ExecutionPolicy Bypass -File $linuxScript -RepoRoot $repoRoot -ArchOutFile $archOutFile -Version $script:ResolvedVersion if ($LASTEXITCODE -ne 0) { throw "Linux 打包失败" } @@ -591,17 +698,18 @@ try { Resolve-Version -ExplicitVersion $Version $publishTarget = Resolve-PublishTarget -InputTarget $Target + $resolvedWindowsFormat = Resolve-WindowsFormat -InputFormat $WindowsFormat Invoke-WithTemporaryWailsVersion { switch ($publishTarget) { "WINDOWS" { - Publish-Windows + Publish-Windows -Format $resolvedWindowsFormat } "LINUX" { Publish-Linux } "BOTH" { - Publish-Windows + Publish-Windows -Format $resolvedWindowsFormat Publish-Linux } default { @@ -613,8 +721,11 @@ try { Write-Host "" Write-Section "✓ 发布完成!" Write-Host "" - if ($script:WindowsDone) { - Write-Host "Windows 安装包: publish\output\AntBrowser-Setup-$script:Version.exe" + if ($script:WindowsInstallerDone) { + Write-Host "Windows 安装包: publish\output\AntBrowser-Setup-$script:ResolvedVersion.exe" + } + if ($script:WindowsPortableDone) { + Write-Host "Windows 便携包: publish\output\AntBrowser-$script:ResolvedVersion-windows-amd64-portable.zip" } if ($script:LinuxDone) { Write-Host "Linux 产物目录: publish\output\"