build: production Windows release pipeline (PyInstaller, SBOM, CI, signing hook)
This commit is contained in:
221
scripts/_build_common.ps1
Normal file
221
scripts/_build_common.ps1
Normal file
@@ -0,0 +1,221 @@
|
||||
#Requires -Version 5.1
|
||||
<#
|
||||
Shared build helpers for Proxy God / ProxyChainManager.
|
||||
Dot-sourced by setup_and_build.ps1 and release_build.ps1 — do not run directly.
|
||||
#>
|
||||
|
||||
Set-StrictMode -Version Latest
|
||||
|
||||
function Get-RepoRoot {
|
||||
# This file lives in scripts/ — parent is the repository root.
|
||||
return (Split-Path -Parent $PSScriptRoot)
|
||||
}
|
||||
|
||||
function Resolve-PythonExe {
|
||||
if (Get-Command py -ErrorAction SilentlyContinue) {
|
||||
try {
|
||||
$out = (& py -3 -c "import sys; print(sys.executable)" 2>$null).Trim()
|
||||
if ($out -and (Test-Path -LiteralPath $out)) { return $out }
|
||||
} catch { }
|
||||
}
|
||||
$p = Get-Command python.exe -ErrorAction SilentlyContinue
|
||||
if ($p) { return $p.Source }
|
||||
throw @(
|
||||
"Python 3.10+ not found (tried 'py -3' and 'python'). Install, then re-run:",
|
||||
" winget install Python.Python.3.12 --accept-package-agreements --accept-source-agreements",
|
||||
"Or https://www.python.org/downloads/ (enable 'Add python.exe to PATH')."
|
||||
) -join "`n"
|
||||
}
|
||||
|
||||
function Get-GitCommit {
|
||||
try {
|
||||
$c = (git -C (Get-RepoRoot) rev-parse --short HEAD 2>$null).Trim()
|
||||
if ($c) { return $c }
|
||||
} catch { }
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
function Get-ReleaseVersion {
|
||||
param([string]$Override = "")
|
||||
if ($Override) { return $Override.Trim() }
|
||||
$root = Get-RepoRoot
|
||||
try {
|
||||
$tag = (git -C $root describe --tags --exact-match 2>$null).Trim()
|
||||
if ($tag) { return $tag.TrimStart("v") }
|
||||
} catch { }
|
||||
try {
|
||||
$desc = (git -C $root describe --tags --always --dirty 2>$null).Trim()
|
||||
if ($desc) { return ($desc -replace '^v', '') }
|
||||
} catch { }
|
||||
$py = Resolve-PythonExe
|
||||
$v = (& $py -c "from proxy_chain_manager import __version__; print(__version__)" 2>$null).Trim()
|
||||
if ($v) { return $v }
|
||||
return "0.0.0-dev"
|
||||
}
|
||||
|
||||
function Install-BuildDependencies {
|
||||
param([string]$PythonExe, [string]$Root)
|
||||
Write-Host "`n== pip (upgrade) =="
|
||||
& $PythonExe -m pip install --upgrade pip
|
||||
Write-Host "`n== runtime dependencies =="
|
||||
& $PythonExe -m pip install -r (Join-Path $Root "requirements.txt")
|
||||
Write-Host "`n== build dependencies =="
|
||||
& $PythonExe -m pip install -r (Join-Path $Root "dev-requirements.txt")
|
||||
}
|
||||
|
||||
function Invoke-UnitTests {
|
||||
param([string]$PythonExe, [string]$Root)
|
||||
Write-Host "`n== compileall =="
|
||||
& $PythonExe -m compileall -q (Join-Path $Root "proxy_chain_manager")
|
||||
if ($LASTEXITCODE -ne 0) { throw "compileall failed (exit $LASTEXITCODE)" }
|
||||
Write-Host "`n== unittest discover =="
|
||||
Push-Location $Root
|
||||
try {
|
||||
& $PythonExe -m unittest discover -s tests -v
|
||||
if ($LASTEXITCODE -ne 0) { throw "unittest failed (exit $LASTEXITCODE)" }
|
||||
} finally {
|
||||
Pop-Location
|
||||
}
|
||||
}
|
||||
|
||||
function Invoke-StageBundledGost {
|
||||
param([string]$Root)
|
||||
Write-Host "`n== Stage bundled GOST =="
|
||||
$prep = Join-Path $Root "scripts\prepare_bundled_gost.ps1"
|
||||
& powershell -NoProfile -ExecutionPolicy Bypass -File $prep
|
||||
if ($LASTEXITCODE -ne 0) { throw "prepare_bundled_gost.ps1 failed (exit $LASTEXITCODE)" }
|
||||
}
|
||||
|
||||
function Invoke-GenerateVersionInfo {
|
||||
param(
|
||||
[string]$PythonExe,
|
||||
[string]$Root,
|
||||
[string]$Version,
|
||||
[string]$Commit = ""
|
||||
)
|
||||
Write-Host "`n== Version metadata ($Version) =="
|
||||
$gen = Join-Path $Root "scripts\generate_version_info.py"
|
||||
$out = Join-Path $Root "build\version_info.txt"
|
||||
$args = @($gen, "--version", $Version, "--out", $out)
|
||||
if ($Commit) { $args += @("--commit", $Commit) }
|
||||
& $PythonExe @args
|
||||
if ($LASTEXITCODE -ne 0) { throw "generate_version_info.py failed (exit $LASTEXITCODE)" }
|
||||
}
|
||||
|
||||
function Invoke-PyInstallerBuild {
|
||||
param([string]$PythonExe, [string]$Root)
|
||||
Write-Host "`n== PyInstaller (ProxyChainManager.spec) =="
|
||||
$spec = Join-Path $Root "ProxyChainManager.spec"
|
||||
if (-not (Test-Path $spec)) { throw "Spec not found: $spec" }
|
||||
& $PythonExe -m PyInstaller --noconfirm --clean $spec
|
||||
if ($LASTEXITCODE -ne 0) { throw "PyInstaller failed (exit $LASTEXITCODE)" }
|
||||
$exe = Join-Path $Root "dist\ProxyChainManager.exe"
|
||||
if (-not (Test-Path $exe)) { throw "Build failed: missing $exe" }
|
||||
return $exe
|
||||
}
|
||||
|
||||
function Invoke-CodeSign {
|
||||
param([string]$ExePath)
|
||||
$cert = $env:SIGN_CERT_PATH
|
||||
if (-not $cert) {
|
||||
Write-Host " (signing skipped — set SIGN_CERT_PATH to a .pfx to Authenticode-sign)"
|
||||
return
|
||||
}
|
||||
if (-not (Test-Path $cert)) { throw "SIGN_CERT_PATH not found: $cert" }
|
||||
$signtool = "${env:ProgramFiles(x86)}\Windows Kits\10\bin\10.0.22621.0\x64\signtool.exe"
|
||||
if (-not (Test-Path $signtool)) {
|
||||
$signtool = (Get-Command signtool.exe -ErrorAction SilentlyContinue).Source
|
||||
}
|
||||
if (-not $signtool) { throw "signtool.exe not found — install Windows SDK" }
|
||||
Write-Host "`n== Authenticode sign =="
|
||||
$args = @("sign", "/fd", "SHA256", "/f", $cert, "/tr", "http://timestamp.digicert.com", "/td", "SHA256")
|
||||
if ($env:SIGN_CERT_PASSWORD) {
|
||||
$args += @("/p", $env:SIGN_CERT_PASSWORD)
|
||||
}
|
||||
$args += $ExePath
|
||||
& $signtool @args
|
||||
if ($LASTEXITCODE -ne 0) { throw "signtool sign failed (exit $LASTEXITCODE)" }
|
||||
}
|
||||
|
||||
function Write-ExeHashSidecar {
|
||||
param([string]$ExePath)
|
||||
$hash = (Get-FileHash -Algorithm SHA256 -LiteralPath $ExePath).Hash.ToLower()
|
||||
$sidecar = "$ExePath.sha256"
|
||||
"$hash *$(Split-Path -Leaf $ExePath)" | Out-File -FilePath $sidecar -Encoding ascii -Force
|
||||
Write-Host "SHA256: $hash -> $sidecar"
|
||||
return @{ Hash = $hash; Sidecar = $sidecar }
|
||||
}
|
||||
|
||||
function Write-Sbom {
|
||||
param(
|
||||
[string]$PythonExe,
|
||||
[string]$OutDir,
|
||||
[string]$Version
|
||||
)
|
||||
Write-Host "`n== SBOM =="
|
||||
$freeze = Join-Path $OutDir "requirements-frozen.txt"
|
||||
& $PythonExe -m pip freeze | Out-File -FilePath $freeze -Encoding utf8
|
||||
$sbom = Join-Path $OutDir "SBOM.json"
|
||||
$pkgs = @()
|
||||
Get-Content $freeze | ForEach-Object {
|
||||
if ($_ -match '^([^=]+)==(.+)$') {
|
||||
$pkgs += @{ name = $Matches[1]; version = $Matches[2] }
|
||||
}
|
||||
}
|
||||
$manifest = @{
|
||||
product = "Proxy God"
|
||||
version = $Version
|
||||
generated = (Get-Date).ToUniversalTime().ToString("o")
|
||||
python = (& $PythonExe -c "import sys; print(sys.version.split()[0])").Trim()
|
||||
packages = $pkgs
|
||||
}
|
||||
($manifest | ConvertTo-Json -Depth 4) | Out-File -FilePath $sbom -Encoding utf8
|
||||
Write-Host "Wrote $sbom and $freeze"
|
||||
return @{ Sbom = $sbom; Freeze = $freeze }
|
||||
}
|
||||
|
||||
function Write-ReleaseManifest {
|
||||
param(
|
||||
[string]$OutDir,
|
||||
[string]$Version,
|
||||
[string]$Commit,
|
||||
[string]$ExePath,
|
||||
[string]$Sha256,
|
||||
[hashtable]$Extra = @{}
|
||||
)
|
||||
$manifest = @{
|
||||
product = "Proxy God"
|
||||
executable = "ProxyChainManager.exe"
|
||||
version = $Version
|
||||
git_commit = $Commit
|
||||
built_at_utc = (Get-Date).ToUniversalTime().ToString("o")
|
||||
platform = "windows-amd64"
|
||||
sha256 = $Sha256
|
||||
uac_admin = $true
|
||||
bundled_gost = $true
|
||||
pyinstaller = "6.10.0"
|
||||
}
|
||||
foreach ($k in $Extra.Keys) { $manifest[$k] = $Extra[$k] }
|
||||
$path = Join-Path $OutDir "RELEASE_MANIFEST.json"
|
||||
($manifest | ConvertTo-Json -Depth 4) | Out-File -FilePath $path -Encoding utf8
|
||||
Write-Host "Wrote $path"
|
||||
return $path
|
||||
}
|
||||
|
||||
function New-ReleaseZip {
|
||||
param(
|
||||
[string]$ReleaseDir,
|
||||
[string]$Version,
|
||||
[string]$Root
|
||||
)
|
||||
$zipName = "ProxyGod-v$Version-windows-amd64.zip"
|
||||
$zipPath = Join-Path $Root "releases\$zipName"
|
||||
$releasesRoot = Split-Path $zipPath -Parent
|
||||
if (-not (Test-Path $releasesRoot)) {
|
||||
New-Item -ItemType Directory -Path $releasesRoot | Out-Null
|
||||
}
|
||||
if (Test-Path $zipPath) { Remove-Item -LiteralPath $zipPath -Force }
|
||||
Compress-Archive -Path (Join-Path $ReleaseDir "*") -DestinationPath $zipPath -Force
|
||||
Write-Host "Release zip: $zipPath"
|
||||
return $zipPath
|
||||
}
|
||||
74
scripts/generate_version_info.py
Normal file
74
scripts/generate_version_info.py
Normal file
@@ -0,0 +1,74 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate a PyInstaller version-info file for ProxyChainManager.exe.
|
||||
|
||||
Usage:
|
||||
python scripts/generate_version_info.py --version 1.2.3 --out build/version_info.txt
|
||||
|
||||
The output is consumed by ProxyChainManager.spec when the file exists.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def _parse_version(version: str) -> tuple[int, int, int, int]:
|
||||
parts = re.findall(r"\d+", version)
|
||||
nums = [int(x) for x in parts[:4]]
|
||||
while len(nums) < 4:
|
||||
nums.append(0)
|
||||
return nums[0], nums[1], nums[2], nums[3]
|
||||
|
||||
|
||||
def build_version_info(version: str, commit: str = "") -> str:
|
||||
filevers = prodvers = _parse_version(version)
|
||||
commit_suffix = f" ({commit})" if commit else ""
|
||||
return f"""# UTF-8
|
||||
# Generated by scripts/generate_version_info.py — do not edit by hand.
|
||||
VSVersionInfo(
|
||||
ffi=FixedFileInfo(
|
||||
filevers={filevers},
|
||||
prodvers={prodvers},
|
||||
mask=0x3f,
|
||||
flags=0x0,
|
||||
OS=0x40004,
|
||||
fileType=0x1,
|
||||
subtype=0x0,
|
||||
date=(0, 0)
|
||||
),
|
||||
kids=[
|
||||
StringFileInfo(
|
||||
[
|
||||
StringTable(
|
||||
u'040904B0',
|
||||
[StringStruct(u'CompanyName', u'Proxy God'),
|
||||
StringStruct(u'FileDescription', u'Proxy God — multi-hop proxy chain manager'),
|
||||
StringStruct(u'FileVersion', u'{version}{commit_suffix}'),
|
||||
StringStruct(u'InternalName', u'ProxyChainManager'),
|
||||
StringStruct(u'LegalCopyright', u'MIT License'),
|
||||
StringStruct(u'OriginalFilename', u'ProxyChainManager.exe'),
|
||||
StringStruct(u'ProductName', u'Proxy God'),
|
||||
StringStruct(u'ProductVersion', u'{version}{commit_suffix}')])
|
||||
]
|
||||
),
|
||||
VarFileInfo([VarStruct(u'Translation', [1033, 1200])])
|
||||
]
|
||||
)
|
||||
"""
|
||||
|
||||
|
||||
def main() -> None:
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--version", required=True)
|
||||
ap.add_argument("--commit", default="")
|
||||
ap.add_argument("--out", required=True)
|
||||
args = ap.parse_args()
|
||||
out = Path(args.out)
|
||||
out.parent.mkdir(parents=True, exist_ok=True)
|
||||
out.write_text(build_version_info(args.version, args.commit), encoding="utf-8")
|
||||
print(f"Wrote {out}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
108
scripts/release_build.ps1
Normal file
108
scripts/release_build.ps1
Normal file
@@ -0,0 +1,108 @@
|
||||
#Requires -Version 5.1
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Production release build for Proxy God (ProxyChainManager.exe).
|
||||
|
||||
.DESCRIPTION
|
||||
Full pipeline:
|
||||
1. Resolve Python 3.10+
|
||||
2. Install pinned runtime + build deps
|
||||
3. Run compileall + unittest (gate)
|
||||
4. Stage SHA-verified bundled gost.exe
|
||||
5. Generate Windows VERSIONINFO for the exe
|
||||
6. PyInstaller one-file build (ProxyChainManager.spec)
|
||||
7. Optional Authenticode sign (SIGN_CERT_PATH / SIGN_CERT_PASSWORD)
|
||||
8. SHA256 sidecar + SBOM + RELEASE_MANIFEST.json
|
||||
9. Zip release folder -> releases/ProxyGod-v{version}-windows-amd64.zip
|
||||
|
||||
.PARAMETER Version
|
||||
Override semver (default: git tag > git describe > __version__).
|
||||
|
||||
.PARAMETER SkipTests
|
||||
Skip compileall/unittest (not recommended for production).
|
||||
|
||||
.PARAMETER SkipSign
|
||||
Skip Authenticode even if SIGN_CERT_PATH is set.
|
||||
|
||||
.PARAMETER NoZip
|
||||
Skip creating the release zip archive.
|
||||
|
||||
.EXAMPLE
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\release_build.ps1
|
||||
|
||||
.EXAMPLE
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\release_build.ps1 -Version 1.2.0
|
||||
#>
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$Version = "",
|
||||
[switch]$SkipTests,
|
||||
[switch]$SkipSign,
|
||||
[switch]$NoZip
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
. "$PSScriptRoot\_build_common.ps1"
|
||||
|
||||
$root = Get-RepoRoot
|
||||
Set-Location $root
|
||||
|
||||
$py = Resolve-PythonExe
|
||||
Write-Host "Using: $py"
|
||||
& $py -c "import sys; assert sys.version_info >= (3, 10); print(sys.version)"
|
||||
|
||||
$ver = Get-ReleaseVersion -Override $Version
|
||||
$commit = Get-GitCommit
|
||||
Write-Host "Release version: $ver (commit $commit)"
|
||||
|
||||
Install-BuildDependencies -PythonExe $py -Root $root
|
||||
|
||||
if (-not $SkipTests) {
|
||||
Invoke-UnitTests -PythonExe $py -Root $root
|
||||
} else {
|
||||
Write-Warning "Skipping test gate (-SkipTests)"
|
||||
}
|
||||
|
||||
Invoke-StageBundledGost -Root $root
|
||||
Invoke-GenerateVersionInfo -PythonExe $py -Root $root -Version $ver -Commit $commit
|
||||
|
||||
$exe = Invoke-PyInstallerBuild -PythonExe $py -Root $root
|
||||
|
||||
if (-not $SkipSign) {
|
||||
Invoke-CodeSign -ExePath $exe
|
||||
}
|
||||
|
||||
$hashInfo = Write-ExeHashSidecar -ExePath $exe
|
||||
|
||||
# Assemble release directory (versioned folder under releases/)
|
||||
$releaseDir = Join-Path $root "releases\v$ver"
|
||||
if (Test-Path $releaseDir) { Remove-Item -Recurse -Force $releaseDir }
|
||||
New-Item -ItemType Directory -Path $releaseDir | Out-Null
|
||||
|
||||
Copy-Item -LiteralPath $exe -Destination (Join-Path $releaseDir "ProxyChainManager.exe") -Force
|
||||
Copy-Item -LiteralPath $hashInfo.Sidecar -Destination (Join-Path $releaseDir "ProxyChainManager.exe.sha256") -Force
|
||||
Copy-Item -LiteralPath (Join-Path $root "LICENSE") -Destination $releaseDir -ErrorAction SilentlyContinue
|
||||
Copy-Item -LiteralPath (Join-Path $root "docs\OPERATOR_RUNBOOK.md") -Destination $releaseDir -ErrorAction SilentlyContinue
|
||||
|
||||
$sbom = Write-Sbom -PythonExe $py -OutDir $releaseDir -Version $ver
|
||||
Write-ReleaseManifest -OutDir $releaseDir -Version $ver -Commit $commit `
|
||||
-ExePath $exe -Sha256 $hashInfo.Hash -Extra @{
|
||||
sbom_file = (Split-Path -Leaf $sbom.Sbom)
|
||||
requirements_frozen = (Split-Path -Leaf $sbom.Freeze)
|
||||
}
|
||||
|
||||
# Also refresh dist/ sidecars for developers
|
||||
Copy-Item -LiteralPath $hashInfo.Sidecar -Destination (Join-Path $root "dist\ProxyChainManager.exe.sha256") -Force
|
||||
|
||||
if (-not $NoZip) {
|
||||
$zip = New-ReleaseZip -ReleaseDir $releaseDir -Version $ver -Root $root
|
||||
Write-Host "`n== Release complete =="
|
||||
Write-Host " Folder: $releaseDir"
|
||||
Write-Host " Zip: $zip"
|
||||
} else {
|
||||
Write-Host "`n== Release complete (no zip) =="
|
||||
Write-Host " Folder: $releaseDir"
|
||||
}
|
||||
|
||||
Write-Host "`nVerify:"
|
||||
Write-Host " Get-FileHash -Algorithm SHA256 releases\v$ver\ProxyChainManager.exe"
|
||||
@@ -1,75 +1,48 @@
|
||||
#Requires -Version 5.1
|
||||
<#
|
||||
.SYNOPSIS
|
||||
After git pull: upgrade pip, install deps, build ProxyChainManager.exe, copy to Desktop, create "Proxy God.lnk".
|
||||
Developer build: deps, tests (optional), PyInstaller, copy to Desktop, shortcut.
|
||||
|
||||
.DESCRIPTION
|
||||
Requires Python 3.10+ on PATH (python.exe) or the Windows py launcher (py -3).
|
||||
Does not auto-install Python; see README for winget one-liner if needed.
|
||||
#>
|
||||
$ErrorActionPreference = "Stop"
|
||||
$root = Split-Path -Parent (Split-Path -Parent $MyInvocation.MyCommand.Path)
|
||||
Set-Location $root
|
||||
Lighter than release_build.ps1 — skips SBOM/zip/manifest. For daily dev iteration.
|
||||
Production releases: use scripts\release_build.ps1 or build_release.bat
|
||||
|
||||
function Resolve-PythonExe {
|
||||
if (Get-Command py -ErrorAction SilentlyContinue) {
|
||||
try {
|
||||
$out = (& py -3 -c "import sys; print(sys.executable)" 2>$null).Trim()
|
||||
if ($out -and (Test-Path -LiteralPath $out)) { return $out }
|
||||
} catch { }
|
||||
}
|
||||
$p = Get-Command python.exe -ErrorAction SilentlyContinue
|
||||
if ($p) { return $p.Source }
|
||||
throw (
|
||||
"Python 3.10+ not found (tried 'py -3' and 'python'). Install, then re-run:`n" +
|
||||
" winget install Python.Python.3.12 --accept-package-agreements --accept-source-agreements`n" +
|
||||
"Or https://www.python.org/downloads/ (enable 'Add python.exe to PATH')."
|
||||
)
|
||||
}
|
||||
.PARAMETER SkipTests
|
||||
Skip unittest gate (faster iteration).
|
||||
#>
|
||||
[CmdletBinding()]
|
||||
param([switch]$SkipTests)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
. "$PSScriptRoot\_build_common.ps1"
|
||||
|
||||
$root = Get-RepoRoot
|
||||
Set-Location $root
|
||||
|
||||
$py = Resolve-PythonExe
|
||||
Write-Host "Using: $py"
|
||||
& $py -c "import sys; assert sys.version_info >= (3, 10), 'Need Python 3.10+'; print(sys.version)"
|
||||
& $py -c "import sys; assert sys.version_info >= (3, 10); print(sys.version)"
|
||||
|
||||
Write-Host "`n== pip (upgrade) =="
|
||||
& $py -m pip install --upgrade pip
|
||||
$ver = Get-ReleaseVersion
|
||||
Install-BuildDependencies -PythonExe $py -Root $root
|
||||
|
||||
Write-Host "`n== dependencies + PyInstaller =="
|
||||
& $py -m pip install -r "$root\requirements.txt"
|
||||
# Pinned dev dep (PyInstaller version) so builds are reproducible.
|
||||
& $py -m pip install -r "$root\dev-requirements.txt"
|
||||
|
||||
Write-Host "`n== Stage bundled GOST binary =="
|
||||
& powershell -NoProfile -ExecutionPolicy Bypass -File "$root\scripts\prepare_bundled_gost.ps1"
|
||||
if ($LASTEXITCODE -ne 0) { throw "prepare_bundled_gost.ps1 failed (exit $LASTEXITCODE)" }
|
||||
|
||||
Write-Host "`n== PyInstaller (using ProxyChainManager.spec) =="
|
||||
# Always build from the spec — it bundles signup_extension/, world_map.png,
|
||||
# customtkinter assets, and the right hidden imports. Do NOT override with
|
||||
# CLI flags or the data files will be missing from the .exe.
|
||||
$spec = Join-Path $root "ProxyChainManager.spec"
|
||||
if (-not (Test-Path $spec)) { throw "Spec not found: $spec" }
|
||||
& $py -m PyInstaller --noconfirm --clean $spec
|
||||
if ($LASTEXITCODE -ne 0) { throw "PyInstaller failed (exit $LASTEXITCODE)" }
|
||||
|
||||
$distExe = Join-Path $root "dist\ProxyChainManager.exe"
|
||||
if (-not (Test-Path $distExe)) {
|
||||
throw "Build failed: missing $distExe"
|
||||
if (-not $SkipTests) {
|
||||
Invoke-UnitTests -PythonExe $py -Root $root
|
||||
}
|
||||
|
||||
# SHA256 sidecar so the release artifact is self-verifiable.
|
||||
$distHashFile = "$distExe.sha256"
|
||||
$distHash = (Get-FileHash -Algorithm SHA256 -LiteralPath $distExe).Hash.ToLower()
|
||||
"$distHash *ProxyChainManager.exe" | Out-File -FilePath $distHashFile -Encoding ascii -Force
|
||||
Write-Host "SHA256: $distHash → $distHashFile"
|
||||
Invoke-StageBundledGost -Root $root
|
||||
Invoke-GenerateVersionInfo -PythonExe $py -Root $root -Version $ver -Commit (Get-GitCommit)
|
||||
|
||||
$exe = Invoke-PyInstallerBuild -PythonExe $py -Root $root
|
||||
$hashInfo = Write-ExeHashSidecar -ExePath $exe
|
||||
|
||||
$desk = [Environment]::GetFolderPath("Desktop")
|
||||
$deskExe = Join-Path $desk "ProxyChainManager.exe"
|
||||
Copy-Item -LiteralPath $distExe -Destination $deskExe -Force
|
||||
Copy-Item -LiteralPath $distHashFile -Destination "$deskExe.sha256" -Force
|
||||
Copy-Item -LiteralPath $exe -Destination $deskExe -Force
|
||||
Copy-Item -LiteralPath $hashInfo.Sidecar -Destination "$deskExe.sha256" -Force
|
||||
Write-Host "Copied: $deskExe (+ .sha256)"
|
||||
|
||||
Write-Host "`n== Desktop shortcut =="
|
||||
& powershell -NoProfile -ExecutionPolicy Bypass -File "$root\scripts\create_desktop_shortcut.ps1"
|
||||
& powershell -NoProfile -ExecutionPolicy Bypass -File (Join-Path $root "scripts\create_desktop_shortcut.ps1")
|
||||
|
||||
Write-Host "`nDone. Launch from Desktop: Proxy God.lnk (or ProxyChainManager.exe)"
|
||||
|
||||
Reference in New Issue
Block a user