param( [switch]$SkipBuild ) $ErrorActionPreference = "Stop" $ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path $RootDir = Split-Path -Parent $ScriptDir $SrcDir = Join-Path $RootDir "src" $VersionFile = Join-Path $SrcDir "version.py" $DefaultConfigDir = Join-Path $RootDir "packaging\default_config" $BuildDir = Join-Path $RootDir "build" $DistDir = Join-Path $RootDir "dist" $ReleaseRoot = Join-Path $RootDir "release" $AppExeName = "AutoPrint" function Read-VersionValue { param( [string]$Path, [string]$Name ) $pattern = "^\s*$Name\s*=\s*`"([^`"]+)`"" foreach ($line in Get-Content -LiteralPath $Path -Encoding UTF8) { if ($line -match $pattern) { return $Matches[1] } } throw "Cannot find $Name in $Path" } function Assert-InProject { param([string]$Path) $resolvedRoot = (Resolve-Path -LiteralPath $RootDir).Path if (Test-Path -LiteralPath $Path) { $resolvedPath = (Resolve-Path -LiteralPath $Path).Path if (-not $resolvedPath.StartsWith($resolvedRoot, [System.StringComparison]::OrdinalIgnoreCase)) { throw "Refusing to modify path outside project: $resolvedPath" } } } $AppName = Read-VersionValue -Path $VersionFile -Name "APP_NAME" $AppVersion = Read-VersionValue -Path $VersionFile -Name "APP_VERSION" $ReleaseName = "$AppExeName-$AppVersion" $ReleaseDir = Join-Path $ReleaseRoot $ReleaseName $PyInstallerOutputDir = Join-Path $DistDir $AppExeName $ExePath = Join-Path $ReleaseDir "$AppExeName.exe" Write-Host "Building $AppName $AppVersion" $pythonVersionText = & python --version if ($LASTEXITCODE -ne 0) { throw "Python is not available." } if ($pythonVersionText -notmatch "Python 3\.7\.") { throw "Python 3.7 is required, current version: $pythonVersionText" } & python -c "import PyInstaller" if ($LASTEXITCODE -ne 0) { throw "PyInstaller is not installed. Run: pip install -r requirements.txt" } if (-not (Test-Path -LiteralPath $DefaultConfigDir)) { throw "Default config directory not found: $DefaultConfigDir" } New-Item -ItemType Directory -Force -Path $ReleaseRoot | Out-Null foreach ($path in @($BuildDir, $PyInstallerOutputDir, $ReleaseDir)) { Assert-InProject -Path $path if (Test-Path -LiteralPath $path) { Remove-Item -LiteralPath $path -Recurse -Force } } if (-not $SkipBuild) { & python -m PyInstaller ` --noconfirm ` --clean ` --onedir ` --windowed ` --name $AppExeName ` --paths $SrcDir ` --add-data "$SrcDir\resources;resources" ` "$SrcDir\main.py" if ($LASTEXITCODE -ne 0) { throw "PyInstaller build failed." } } if (-not (Test-Path -LiteralPath $PyInstallerOutputDir)) { throw "PyInstaller output not found: $PyInstallerOutputDir" } Copy-Item -LiteralPath $PyInstallerOutputDir -Destination $ReleaseDir -Recurse Copy-Item -LiteralPath $DefaultConfigDir -Destination (Join-Path $ReleaseDir "config") -Recurse New-Item -ItemType Directory -Force -Path (Join-Path $ReleaseDir "logs") | Out-Null New-Item -ItemType Directory -Force -Path (Join-Path $ReleaseDir "output") | Out-Null $ReadmePath = Join-Path $ReleaseDir "README.txt" $Readme = @( "$AppName $AppVersion", "", "Start:", "Double-click $AppExeName.exe.", "", "Folders:", "- config: default config and custom templates.", "- logs: runtime logs.", "- output: default export folder.", "", "Do not delete dependency files in this folder." ) Set-Content -LiteralPath $ReadmePath -Value $Readme -Encoding UTF8 if (-not (Test-Path -LiteralPath $ExePath)) { throw "Executable not found: $ExePath" } Write-Host "Release created: $ReleaseDir"