feat: add versioned release packaging
This commit is contained in:
+127
-16
@@ -1,20 +1,99 @@
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||
$repoRoot = Resolve-Path (Join-Path $scriptDir "..")
|
||||
$repoRoot = (Resolve-Path (Join-Path $scriptDir "..")).Path
|
||||
Set-Location $repoRoot
|
||||
|
||||
$pythonVersion = python -c "import sys; print('%d.%d.%d' % sys.version_info[:3])"
|
||||
Write-Host "Using Python: $pythonVersion"
|
||||
if ([version]$pythonVersion -lt [version]"3.10.0") {
|
||||
Write-Warning "Project target is Python 3.10+. Switch to a 3.10+ environment before official release builds."
|
||||
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 ', ')"
|
||||
}
|
||||
}
|
||||
|
||||
$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"
|
||||
}
|
||||
|
||||
python -m PyInstaller --noconfirm --clean $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"
|
||||
}
|
||||
|
||||
$distDir = Join-Path $repoRoot "dist\cmshopee"
|
||||
$exePath = Join-Path $distDir "cmshopee.exe"
|
||||
@@ -37,19 +116,51 @@ $excluded = @(
|
||||
"data"
|
||||
)
|
||||
|
||||
$found = @()
|
||||
foreach ($relative in $excluded) {
|
||||
$candidate = Join-Path $distDir $relative
|
||||
if (Test-Path -LiteralPath $candidate) {
|
||||
$found += $relative
|
||||
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 = "$appCodeName-$appVersion"
|
||||
$releaseDir = Join-Path $releaseRoot $releaseName
|
||||
$portableZip = Join-Path $releaseRoot "$releaseName-portable.zip"
|
||||
|
||||
foreach ($path in @($releaseDir, $portableZip)) {
|
||||
Assert-InProject -Path $path
|
||||
if (Test-Path -LiteralPath $path) {
|
||||
Remove-Item -LiteralPath $path -Recurse -Force
|
||||
}
|
||||
}
|
||||
|
||||
if ($found.Count -gt 0) {
|
||||
throw "Build output contains local user data: $($found -join ', ')"
|
||||
}
|
||||
New-Item -ItemType Directory -Force -Path $releaseDir | Out-Null
|
||||
Get-ChildItem -LiteralPath $distDir -Force | Copy-Item -Destination $releaseDir -Recurse -Force
|
||||
Set-Content -LiteralPath (Join-Path $releaseDir "version.txt") -Value $appVersion -Encoding ascii -NoNewline
|
||||
|
||||
Write-Host "Build complete: $exePath"
|
||||
Write-Host "Do not copy local data into the release package."
|
||||
$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
|
||||
Compress-Archive -Path (Join-Path $releaseDir "*") -DestinationPath $portableZip -Force
|
||||
|
||||
Write-Host "$(Decode-Utf8Base64 "5p6E5bu65a6M5oiQOg==") $exePath"
|
||||
Write-Host "$(Decode-Utf8Base64 "5Y+R5biD55uu5b2VOg==") $releaseDir"
|
||||
Write-Host "$(Decode-Utf8Base64 "5L6/5pC65Y6L57yp5YyFOg==") $portableZip"
|
||||
Write-Host (Decode-Utf8Base64 "5LiN6KaB5oqK5pys5ZywIGRhdGHjgIHphY3nva7jgIHmlbDmja7lupPjgIHlm77niYfjgIHml6Xlv5fmiJbnmbvlvZXmgIHlpI3liLbov5vlj5HluIPljIXjgII=")
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user