Files
cmshoppe/scripts/build_exe.ps1
T

214 lines
8.6 KiB
PowerShell
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
$ErrorActionPreference = "Stop"
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$repoRoot = (Resolve-Path (Join-Path $scriptDir "..")).Path
Set-Location $repoRoot
function Read-VersionValue {
param(
[string]$Path,
[string]$Name
)
$pattern = '^\s*{0}\s*=\s*"([^"]+)"' -f [regex]::Escape($Name)
foreach ($line in Get-Content -LiteralPath $Path -Encoding UTF8) {
if ($line -match $pattern) {
return $Matches[1]
}
}
throw "Cannot find $Name in $Path"
}
function Decode-Utf8Base64 {
param([string]$Text)
return [System.Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($Text))
}
function Assert-InProject {
param([string]$Path)
$resolvedRoot = (Resolve-Path -LiteralPath $repoRoot).Path
$rootPrefix = $resolvedRoot.TrimEnd("\") + "\"
if (Test-Path -LiteralPath $Path) {
$resolvedPath = (Resolve-Path -LiteralPath $Path).Path
if ($resolvedPath -ne $resolvedRoot -and -not $resolvedPath.StartsWith($rootPrefix, [System.StringComparison]::OrdinalIgnoreCase)) {
throw "Refusing to modify path outside project: $resolvedPath"
}
} else {
$fullPath = [System.IO.Path]::GetFullPath($Path)
if ($fullPath -ne $resolvedRoot -and -not $fullPath.StartsWith($rootPrefix, [System.StringComparison]::OrdinalIgnoreCase)) {
throw "Refusing to modify path outside project: $fullPath"
}
}
}
function Assert-NoExcludedData {
param(
[string]$Root,
[string[]]$Excluded
)
$found = @()
foreach ($relative in $Excluded) {
$candidate = Join-Path $Root $relative
if (Test-Path -LiteralPath $candidate) {
$found += $relative
}
}
if ($found.Count -gt 0) {
throw "Build output contains local user data: $($found -join ', ')"
}
}
function Compress-ArchiveWithRetry {
param(
[string]$SourcePath,
[string]$DestinationPath,
[int]$Attempts = 5,
[int]$DelaySeconds = 2
)
for ($attempt = 1; $attempt -le $Attempts; $attempt++) {
try {
if (Test-Path -LiteralPath $DestinationPath) {
Remove-Item -LiteralPath $DestinationPath -Force
}
Compress-Archive -Path $SourcePath -DestinationPath $DestinationPath -Force
return
} catch {
if ($attempt -ge $Attempts) {
throw
}
Write-Host "压缩发布包被临时文件占用阻塞,等待 $DelaySeconds 秒后重试 ($attempt/$Attempts):$($_.Exception.Message)"
Start-Sleep -Seconds $DelaySeconds
}
}
}
$versionFile = Join-Path $repoRoot "app\version.py"
if (-not (Test-Path -LiteralPath $versionFile)) {
throw "Missing app\version.py"
}
$appName = Read-VersionValue -Path $versionFile -Name "APP_NAME"
$appCodeName = Read-VersionValue -Path $versionFile -Name "APP_CODE_NAME"
$appVersion = Read-VersionValue -Path $versionFile -Name "APP_VERSION"
if ($appCodeName -ne "cmshopee") {
throw "APP_CODE_NAME must be cmshopee, current value: $appCodeName"
}
$pythonExe = "py"
$pythonArgs = @("-3.10")
$pythonVersionOutput = & $pythonExe @pythonArgs -c "import sys; print('%d.%d.%d' % sys.version_info[:3])"
if ($LASTEXITCODE -ne 0 -or -not $pythonVersionOutput) {
throw (Decode-Utf8Base64 "5pyq5om+5YiwIFB5dGhvbiAzLjEw44CC6K+35YWI5a6J6KOFIFB5dGhvbiAzLjEw77yM5bm256Gu6K6kIFdpbmRvd3MgUHl0aG9uIExhdW5jaGVyIOWPr+mAmui/hyBweSAtMy4xMCDosIPnlKjjgII=")
}
$pythonVersion = ($pythonVersionOutput | Select-Object -First 1).Trim()
Write-Host "$(Decode-Utf8Base64 "5L2/55SoIFB5dGhvbiAzLjEwOg==") $pythonVersion"
if (-not $pythonVersion.StartsWith("3.10.")) {
throw "$(Decode-Utf8Base64 "5b2T5YmN5omT5YyF6ISa5pys5Zu65a6a5L2/55SoIFB5dGhvbiAzLjEw77yM5b2T5YmN54mI5pys"): $pythonVersion"
}
$spec = Join-Path $repoRoot "cmshopee.spec"
if (-not (Test-Path -LiteralPath $spec)) {
throw "Missing cmshopee.spec"
}
Write-Host "$(Decode-Utf8Base64 "5q2j5Zyo5p6E5bu6") $appName $appVersion"
Write-Host (Decode-Utf8Base64 "5omT5YyF5ZG95Luk5Zu65a6a5L2/55SoIHB5IC0zLjEwIC1tIFB5SW5zdGFsbGVy44CC")
& $pythonExe @pythonArgs -m PyInstaller --noconfirm --clean $spec
if ($LASTEXITCODE -ne 0) {
throw "$(Decode-Utf8Base64 "UHlJbnN0YWxsZXIg5omT5YyF5aSx6LSl77yM6YCA5Ye656CB"): $LASTEXITCODE"
}
$updaterSpec = Join-Path $repoRoot "cmshopee-updater.spec"
$updaterDist = Join-Path $repoRoot "dist\updater"
$updaterWork = Join-Path $repoRoot "build\updater"
& $pythonExe @pythonArgs -m PyInstaller --noconfirm --clean --distpath $updaterDist --workpath $updaterWork $updaterSpec
if ($LASTEXITCODE -ne 0) {
throw "独立更新器打包失败,退出码: $LASTEXITCODE"
}
$updaterExe = Join-Path $updaterDist "cmshopee-updater.exe"
if (-not (Test-Path -LiteralPath $updaterExe)) {
throw "独立更新器产物不存在: $updaterExe"
}
$distDir = Join-Path $repoRoot "dist\cmshopee"
$exePath = Join-Path $distDir "cmshopee.exe"
if (-not (Test-Path -LiteralPath $exePath)) {
throw "Build failed: $exePath not found"
}
$excluded = @(
"config.json",
"config\ai_models.json",
"cmshopee.db",
"cmshopee.db-wal",
"cmshopee.db-shm",
"db.sqlite",
"chrome_user_data_dir",
"images",
"logs",
"prompts",
"title_prompt.txt",
"data"
)
Assert-NoExcludedData -Root $distDir -Excluded $excluded
$internalDir = Join-Path $distDir "_internal"
if (-not (Test-Path -LiteralPath $internalDir)) {
throw "Build output missing _internal directory. Expected PyInstaller 6.x onedir layout (cmshopee.exe + _internal). Ensure PyInstaller >= 6.0 is installed: py -3.10 -m pip install -r requirements-build.txt"
}
Write-Host "Verified onedir layout: _internal present"
$releaseRoot = Join-Path $repoRoot "release"
$releaseName = "$appName$appVersion"
$releaseDir = Join-Path $releaseRoot $releaseName
$portableZip = Join-Path $releaseRoot "$releaseName.zip"
$releaseMetadata = Join-Path $releaseRoot "release-metadata${appVersion}.json"
$legacyReleaseMetadata = Join-Path $releaseRoot "release-metadata.json"
foreach ($path in @($releaseDir, $portableZip, $releaseMetadata, $legacyReleaseMetadata)) {
Assert-InProject -Path $path
if (Test-Path -LiteralPath $path) {
Remove-Item -LiteralPath $path -Recurse -Force
}
}
New-Item -ItemType Directory -Force -Path $releaseDir | Out-Null
Get-ChildItem -LiteralPath $distDir -Force | Copy-Item -Destination $releaseDir -Recurse -Force
Copy-Item -LiteralPath $updaterExe -Destination (Join-Path $releaseDir "cmshopee-updater.exe") -Force
Set-Content -LiteralPath (Join-Path $releaseDir "version.txt") -Value $appVersion -Encoding ascii -NoNewline
$readme = @(
"$appName v$appVersion",
"",
(Decode-Utf8Base64 "5ZCv5Yqo5pa55byP77ya"),
(Decode-Utf8Base64 "5Y+M5Ye7IGNtc2hvcGVlLmV4ZSDlkK/liqjjgII="),
"",
(Decode-Utf8Base64 "5L2/55So6K+05piO77ya"),
(Decode-Utf8Base64 "LSDor7fmiormlbTkuKrmlofku7blpLnop6PljovliLDlj6/lhpnnm67lvZXvvIzkvovlpoIgRDpcY21zaG9wZWUg5oiW5qGM6Z2i44CC"),
(Decode-Utf8Base64 "LSDkuI3opoHmlL7liLAgQzpcUHJvZ3JhbSBGaWxlcyDmiJblhbbku5bpnIDopoHnrqHnkIblkZjmnYPpmZDlhpnlhaXnmoTnm67lvZXjgII="),
(Decode-Utf8Base64 "LSDpppbmrKHov5DooYzkvJrlnKjnqIvluo/lkIznuqfliJvlu7ogZGF0YVzvvIznlKjkuo7kv53lrZjotKblj7fjgIHku7vliqHjgIHlm77niYfjgIHml6Xlv5fjgIHphY3nva7lkoznmbvlvZXmgIHjgII="),
(Decode-Utf8Base64 "LSDljYfnuqfml7bopobnm5bmlrDniYjnqIvluo/mlofku7blkozkvp3otZbmlofku7bvvIzkvYblv4Xpobvkv53nlZnml6fnm67lvZXph4znmoQgZGF0YVzjgII="),
(Decode-Utf8Base64 "LSDkuI3opoHmioogZGF0YVwg5Y+R57uZ5YW25LuW5Lq677yM6YeM6Z2i5YyF5ZCr5pys5Zyw6YWN572u44CB5Lia5Yqh5pWw5o2u44CB5Zu+54mH5ZKM55m75b2V5oCB44CC")
)
Set-Content -LiteralPath (Join-Path $releaseDir "README.txt") -Value $readme -Encoding UTF8
Assert-NoExcludedData -Root $releaseDir -Excluded $excluded
& $pythonExe @pythonArgs -m app.release_manifest manifest --release-dir $releaseDir --version $appVersion
if ($LASTEXITCODE -ne 0) {
throw "生成 package-manifest.json 失败,退出码: $LASTEXITCODE"
}
Compress-ArchiveWithRetry -SourcePath (Join-Path $releaseDir "*") -DestinationPath $portableZip
& $pythonExe @pythonArgs -m app.release_manifest metadata --zip $portableZip --version $appVersion --output $releaseMetadata
if ($LASTEXITCODE -ne 0) {
throw "生成 release-metadata${appVersion}.json 失败,退出码: $LASTEXITCODE"
}
Write-Host "$(Decode-Utf8Base64 "5p6E5bu65a6M5oiQOg==") $exePath"
Write-Host "$(Decode-Utf8Base64 "5Y+R5biD55uu5b2VOg==") $releaseDir"
Write-Host "$(Decode-Utf8Base64 "5L6/5pC65Y6L57yp5YyFOg==") $portableZip"
Write-Host "$(Decode-Utf8Base64 "5Y+R5biD5YWD5pWw5o2uOg==") $releaseMetadata"
Write-Host (Decode-Utf8Base64 "5LiN6KaB5oqK5pys5ZywIGRhdGHjgIHphY3nva7jgIHmlbDmja7lupPjgIHlm77niYfjgIHml6Xlv5fmiJbnmbvlvZXmgIHlpI3liLbov5vlj5HluIPljIXjgII=")