93 lines
4.8 KiB
PowerShell
93 lines
4.8 KiB
PowerShell
# make-tester-zip.ps1 - build a clean, portable RemSound copy to hand to external testers.
|
|||
|
|
#
|
||
|
|
# What it produces
|
||
|
|
# ----------------
|
||
|
|
# A zip that MIRRORS the publish folder (every current code fix, the program files, pdb symbols,
|
||
|
|
# all runtimes, default sounds, readme, Tolk, install scripts) but with NONE of your personal state -
|
||
|
|
# no profiles, config, logs or recordings. A tester unzips it, runs RemSound.exe, and it builds its own
|
||
|
|
# fresh config on first launch.
|
||
|
|
#
|
||
|
|
# How it stays clean
|
||
|
|
# ------------------
|
||
|
|
# * It ALWAYS publishes into a fresh, empty folder the app has never run in, so there is nothing
|
||
|
|
# personal to leak (same principle as build-release.ps1).
|
||
|
|
# * It then SCANS the staged files AND the finished zip and ABORTS (deletes the zip, exits non-zero)
|
||
|
|
# if anything that could carry personal data is present.
|
||
|
|
# Unlike build-release.ps1 this is NOT a public release: no tag, no version/notes checks, no GitHub -
|
||
|
|
# it's a private portable copy for a couple of testers. Nothing is stripped, so it matches exactly what
|
||
|
|
# you test with locally.
|
||
|
|
#
|
||
|
|
# Usage:
|
||
|
|
# powershell -ExecutionPolicy Bypass -File make-tester-zip.ps1 # -> D:\Dropbox\remsound.zip, runs the test gate first
|
||
|
|
# powershell -ExecutionPolicy Bypass -File make-tester-zip.ps1 -Output C:\path\to\build.zip
|
||
|
|
# powershell -ExecutionPolicy Bypass -File make-tester-zip.ps1 -SkipGate # skip the build-and-test gate (faster; use when you just ran it)
|
||
|
|
|
||
|
|
param(
|
||
|
|
[string]$Output = 'D:\Dropbox\remsound.zip',
|
||
|
|
[switch]$SkipGate
|
||
|
|
)
|
||
|
|
|
||
|
|
$ErrorActionPreference = 'Stop'
|
||
|
|
$repo = $PSScriptRoot
|
||
|
|
$proj = Join-Path $repo 'src\RemSound.App\RemSound.App.csproj'
|
||
|
|
$staging = Join-Path ([System.IO.Path]::GetTempPath()) ("remsound-testers-" + [guid]::NewGuid().ToString('N'))
|
||
|
|
|
||
|
|
# 0. Don't hand testers a broken build - run the build-and-test gate first (unless told to skip). The
|
||
|
|
# gate publishes + tests its OWN throwaway copy, so it doesn't touch the clean staging folder below.
|
||
|
|
$gate = Join-Path $repo 'run-tests.ps1'
|
||
|
|
if (-not $SkipGate -and (Test-Path $gate)) {
|
||
|
|
Write-Host "Running the build-and-test gate first..." -ForegroundColor Cyan
|
||
|
|
& $gate
|
||
|
|
if ($LASTEXITCODE -ne 0) { throw "build-and-test gate FAILED - not making a tester zip. Fix the failures, or re-run with -SkipGate to override." }
|
||
|
|
}
|
||
|
|
|
||
|
|
# 1. Fresh Release publish = exactly what deploy-test puts in the publish folder (program files, pdb,
|
||
|
|
# all runtimes, default sounds, readme, Tolk, install scripts). Fresh = never run = no user state.
|
||
|
|
Write-Host "Publishing Release to a fresh folder: $staging" -ForegroundColor Cyan
|
||
|
|
& dotnet publish $proj -c Release -o $staging --nologo -v q
|
||
|
|
if ($LASTEXITCODE -ne 0) { throw "dotnet publish failed ($LASTEXITCODE)" }
|
||
|
|
|
||
|
|
# 2. SAFETY SCAN - the same rule build-release.ps1 uses. Nothing that could carry personal data may ship.
|
||
|
|
$forbiddenFolders = @('logs','profiles','recordings','config','user settings and logs')
|
||
|
|
function Test-Forbidden([string]$path) {
|
||
|
|
$p = $path.Replace('\','/')
|
||
|
|
foreach ($f in $forbiddenFolders) { if ($p -match "(^|/)$f/") { return $true } }
|
||
|
|
if ($p -match '\.log$') { return $true }
|
||
|
|
if ($p -match '(^|/)(remsound\.config\.json|global config\.json)$') { return $true }
|
||
|
|
return $false
|
||
|
|
}
|
||
|
|
$bad = @()
|
||
|
|
Get-ChildItem -Path $staging -Recurse -Force | ForEach-Object {
|
||
|
|
$rel = $_.FullName.Substring($staging.Length).TrimStart('\','/')
|
||
|
|
if (Test-Forbidden $rel) { $bad += $rel }
|
||
|
|
}
|
||
|
|
if ($bad.Count -gt 0) {
|
||
|
|
Remove-Item -LiteralPath $staging -Recurse -Force
|
||
|
|
throw "ABORT - staged copy contains files that must not ship: $($bad -join ', ')"
|
||
|
|
}
|
||
|
|
Write-Host "Scan clean: no profiles / config / logs / recordings present." -ForegroundColor Green
|
||
|
|
|
||
|
|
# 3. Zip it.
|
||
|
|
$outDir = Split-Path -Parent $Output
|
||
|
|
if ($outDir -and -not (Test-Path -LiteralPath $outDir)) { New-Item -ItemType Directory -Path $outDir -Force | Out-Null }
|
||
|
|
if (Test-Path -LiteralPath $Output) { Remove-Item -LiteralPath $Output -Force }
|
||
|
|
Compress-Archive -Path (Join-Path $staging '*') -DestinationPath $Output -CompressionLevel Optimal -Force
|
||
|
|
|
||
|
|
# 4. SAFETY SCAN again, on the finished zip - belt and braces.
|
||
|
|
Add-Type -AssemblyName System.IO.Compression.FileSystem
|
||
|
|
$zip = [System.IO.Compression.ZipFile]::OpenRead($Output)
|
||
|
|
try {
|
||
|
|
$leaked = @($zip.Entries | Where-Object { Test-Forbidden $_.FullName })
|
||
|
|
$entryCount = $zip.Entries.Count
|
||
|
|
} finally { $zip.Dispose() }
|
||
|
|
Remove-Item -LiteralPath $staging -Recurse -Force
|
||
|
|
if ($leaked.Count -gt 0) {
|
||
|
|
Remove-Item -LiteralPath $Output -Force
|
||
|
|
throw "ABORT - finished zip contains forbidden entries: $($leaked.FullName -join ', ')"
|
||
|
|
}
|
||
|
|
|
||
|
|
$mb = [math]::Round((Get-Item -LiteralPath $Output).Length / 1MB, 2)
|
||
|
|
Write-Host ""
|
||
|
|
Write-Host "OK - tester zip ready: $Output ($mb MB, $entryCount entries)." -ForegroundColor Green
|
||
|
|
Write-Host " Mirrors the publish folder; verified no profiles / config / logs / recordings." -ForegroundColor Green
|