<# Builds a deployable release folder for the Purple Envelope Orders Site. "Build" for this Classic ASP app just means: assemble the exact file set that should ship to production, with the production web.config swapped in. There's no compile step. - Archives the given git ref via `git archive`, which respects .gitattributes export-ignore rules (drops docs/, tests/, .claude/, dev-only config, etc. - see .gitattributes for the full list). - Enforces $KeepTopLevel below as the authoritative allow-list: anything extracted from the archive that isn't on this list gets deleted. This is the real safety net - a file added to the repo root later and never added to .gitattributes would otherwise ship to production by default. Here, it has to be explicitly added to $KeepTopLevel to ship. - Overwrites public/web.config in the archive with public/web.config.production.template. - Leaves the assembled tree in -OutDir, ready to zip and ship. Usage: powershell -File scripts\build-release.ps1 powershell -File scripts\build-release.ps1 -Ref master -OutDir dist\release #> param( [string]$Ref = 'HEAD', [string]$OutDir = '' ) $ErrorActionPreference = 'Stop' # Everything the live site needs to run: app code, framework internals, the IIS root, and # the migration runner (+ its generator siblings, harmless to ship alongside it). Only # db\migrations is kept under db - never db\webdata.accdb, even if something ever put a # stray copy there. $KeepTopLevel = @('app', 'core', 'public', 'scripts', 'db') $KeepUnderDb = @('migrations') $repoRoot = Split-Path $PSScriptRoot -Parent Push-Location $repoRoot try { if([string]::IsNullOrWhiteSpace($OutDir)){ $OutDir = Join-Path $repoRoot ('dist\release_' + (Get-Date -Format 'yyyyMMdd_HHmmss')) } if(Test-Path $OutDir){ Remove-Item -Recurse -Force $OutDir } New-Item -ItemType Directory -Force -Path $OutDir | Out-Null $templatePath = Join-Path $repoRoot 'public\web.config.production.template' if(!(Test-Path $templatePath)){ throw "public\web.config.production.template not found - can't build a production release without it." } Write-Host "Archiving $Ref (honoring .gitattributes export-ignore rules)..." $archivePath = Join-Path $OutDir '_archive.zip' git archive --format=zip --output $archivePath $Ref if($LASTEXITCODE -ne 0){ throw 'git archive failed' } Expand-Archive -Path $archivePath -DestinationPath $OutDir -Force Remove-Item $archivePath Write-Host "Pruning to the allow-list: $($KeepTopLevel -join ', '), db\$($KeepUnderDb -join ', db\')" Get-ChildItem -Path $OutDir -Force | ForEach-Object { if($_.Name -notin $KeepTopLevel){ Write-Host " removing (not on allow-list): $($_.Name)" Remove-Item -Recurse -Force $_.FullName } } $dbDir = Join-Path $OutDir 'db' if(Test-Path $dbDir){ Get-ChildItem -Path $dbDir -Force | ForEach-Object { if($_.Name -notin $KeepUnderDb){ Write-Host " removing (not on allow-list): db\$($_.Name)" Remove-Item -Recurse -Force $_.FullName } } } if(!(Test-Path (Join-Path $OutDir 'public'))){ throw "Release has no public\ folder after pruning - check `$KeepTopLevel in this script" } $liveConfig = Join-Path $OutDir 'public\web.config' Copy-Item $templatePath $liveConfig -Force Remove-Item (Join-Path $OutDir 'public\web.config.production.template') -ErrorAction SilentlyContinue Write-Host "Release built at $OutDir" Write-Host " public\web.config swapped in from web.config.production.template" } finally { Pop-Location }