$ErrorActionPreference = 'Stop' Add-Type -AssemblyName System.IO.Compression Add-Type -AssemblyName System.IO.Compression.FileSystem $repoRoot = Resolve-Path (Join-Path $PSScriptRoot '..') $projectDir = Join-Path $repoRoot 'ComputerShopWpf' $binDir = Join-Path $projectDir 'bin\Debug' $databaseDir = Join-Path $projectDir 'Database' $buildDir = Join-Path $repoRoot 'Installer\build' $packageDir = Join-Path $buildDir 'package' $payloadZip = Join-Path $buildDir 'payload.zip' $stubExe = Join-Path $buildDir 'ComputerShopWpf_Setup.stub.exe' $setupPath = Join-Path (Resolve-Path 'outputs') 'ComputerShopWpf_Setup.exe' $sourcePath = Join-Path $PSScriptRoot 'SetupStub.cs' $cscPath = Join-Path $env:WINDIR 'Microsoft.NET\Framework\v4.0.30319\csc.exe' $marker = [System.Text.Encoding]::ASCII.GetBytes('CSWPF_SETUP_PAYLOAD_V1') $resolvedRepo = [System.IO.Path]::GetFullPath($repoRoot.Path) $resolvedBuild = [System.IO.Path]::GetFullPath($buildDir) if (-not $resolvedBuild.StartsWith($resolvedRepo, [System.StringComparison]::OrdinalIgnoreCase)) { throw "Refusing to clean outside repository: $resolvedBuild" } if (-not (Test-Path -LiteralPath $cscPath)) { throw "C# compiler was not found at $cscPath" } if (-not (Test-Path -LiteralPath (Join-Path $binDir 'ComputerShopWpf.exe'))) { throw "Build output was not found. Rebuild the solution first: $binDir" } if (Test-Path -LiteralPath $buildDir) { Remove-Item -LiteralPath $buildDir -Recurse -Force } New-Item -ItemType Directory -Path $packageDir | Out-Null New-Item -ItemType Directory -Path (Join-Path $packageDir 'Database') | Out-Null Get-ChildItem -LiteralPath $binDir -File | Where-Object { $_.Extension -in @('.exe', '.config', '.dll') } | Copy-Item -Destination $packageDir -Force Copy-Item -LiteralPath (Join-Path $repoRoot 'README.md') -Destination $packageDir -Force Copy-Item -LiteralPath (Join-Path $databaseDir 'create_database.sql') -Destination (Join-Path $packageDir 'Database') -Force Copy-Item -LiteralPath (Join-Path $PSScriptRoot 'uninstall.ps1') -Destination $packageDir -Force if (Test-Path -LiteralPath $payloadZip) { Remove-Item -LiteralPath $payloadZip -Force } $zip = [System.IO.Compression.ZipFile]::Open($payloadZip, [System.IO.Compression.ZipArchiveMode]::Create) try { Get-ChildItem -LiteralPath $packageDir -Recurse -File | ForEach-Object { $relative = $_.FullName.Substring($packageDir.Length + 1).Replace('\', '/') [System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile( $zip, $_.FullName, $relative, [System.IO.Compression.CompressionLevel]::Optimal) | Out-Null } } finally { $zip.Dispose() } & $cscPath ` /nologo ` /target:winexe ` /platform:anycpu ` /out:$stubExe ` /reference:System.Windows.Forms.dll ` /reference:System.Drawing.dll ` /reference:System.IO.Compression.dll ` /reference:System.IO.Compression.FileSystem.dll ` $sourcePath if ($LASTEXITCODE -ne 0) { throw "C# installer stub compilation failed with exit code $LASTEXITCODE" } $stubBytes = [System.IO.File]::ReadAllBytes($stubExe) $payloadBytes = [System.IO.File]::ReadAllBytes($payloadZip) $lengthBytes = [System.BitConverter]::GetBytes([Int64]$payloadBytes.Length) if (Test-Path -LiteralPath $setupPath) { Remove-Item -LiteralPath $setupPath -Force } $out = [System.IO.File]::Open($setupPath, [System.IO.FileMode]::CreateNew, [System.IO.FileAccess]::Write) try { $out.Write($stubBytes, 0, $stubBytes.Length) $out.Write($payloadBytes, 0, $payloadBytes.Length) $out.Write($lengthBytes, 0, $lengthBytes.Length) $out.Write($marker, 0, $marker.Length) } finally { $out.Dispose() } Get-Item -LiteralPath $setupPath | Select-Object FullName, Length, LastWriteTime