# Package-content guard (run BEFORE `cargo publish`).
# Asserts required files (docs + sources + tests) and forbidden patterns (secrets).
# Usage: powershell -ExecutionPolicy Bypass -File scripts/check-package.ps1 [-AllowDirty]
# NOTE: ASCII-only on purpose (Windows PowerShell 5.1 decodes BOM-less .ps1 as ANSI).
[CmdletBinding()]
param(
[switch]$AllowDirty
)
$ErrorActionPreference = 'Stop'
$root = Split-Path -Parent $PSScriptRoot
Push-Location $root
try {
# ---- required: the ui crate must carry ----
$required = @(
'Cargo.toml',
'README.md',
'DESIGN.md',
'CHANGELOG.md',
'src/lib.rs',
'src/state.rs',
'src/plugin.rs',
'src/render.rs',
'src/runtime.rs',
'src/faces.rs',
'src/pluginwin.rs',
'src/pick.rs',
'src/launch.rs',
'src/launcher.rs',
'src/launcher_ui.rs',
'src/fonts.rs',
'src/main.rs',
# 0.4.3 (audit P4): these five were shipped but never asserted -- a rename or
# accidental delete would have gone unnoticed. 16 src files, 16 assertions.
'src/tokens.rs',
'src/toast.rs',
'src/splitters.rs',
'src/ui_ext.rs',
'src/format.rs',
'tests/ui_state.rs',
'tests/ui_kittest.rs',
'tests/ui_pluginwin.rs',
'tests/ui_runtime.rs',
'tests/launch.rs',
'tests/pick_ui.rs',
'tests/ui_ops.rs',
'tests/ui_market.rs',
'tests/ui_ctp.rs',
'tests/ui_ctp_live.rs',
# 0.4.3 (audit P4): the 529-line converter-chain suite was not asserted either.
'tests/ui_converter_chain.rs',
# 2026-10-28 (archive wave 0.9.0): new product faces must ship --
# appconfig (CLI/gate shared writer), ekapp bin (desktop + add-account),
# the restore/contract suites. Same house rule as audit P4: every source
# face gets its own assertion.
'src/appconfig.rs',
'src/bin/ekapp.rs',
'tests/ui_restore.rs',
'tests/ui_app_contract.rs'
)
# ---- forbidden: must never reach the registry ----
$forbiddenPatterns = @(
'\.local\.env$',
'(^|/)\.env$',
'\.pfx$',
'\.pem$',
'\.key$',
'(^|/)probe/',
'\.py$',
'\.toml\.local$'
)
$cargoArgs = @('package', '--list')
if ($AllowDirty) { $cargoArgs += '--allow-dirty' }
Write-Host "running: cargo $($cargoArgs -join ' ')"
# Cargo writes progress/warnings to stderr (index chatter, file-lock notes, unused-patch
# notes). Under Stop, PowerShell turns native stderr into a terminating error and the
# script dies before the checks. Toggle to Continue for the call; judge by exit code.
$ErrorActionPreference = 'Continue'
$list = & cargo @cargoArgs 2>$null
$cargoCode = $LASTEXITCODE
$ErrorActionPreference = 'Stop'
if ($cargoCode -ne 0) {
Write-Host "FAIL: 'cargo package --list' returned $cargoCode (dirty tree? pass -AllowDirty)" -ForegroundColor Red
exit 1
}
$files = @($list | ForEach-Object { $_.Trim() } | Where-Object { $_ -ne '' })
Write-Host "package file count: $($files.Count)"
$ok = $true
# ---- required present ----
foreach ($r in $required) {
$norm = $r -replace '/', '\'
$hit = @($files | Where-Object { ($_ -replace '/', '\') -eq $norm })
if ($hit.Count -gt 0) {
Write-Host (" OK " + $r)
} else {
Write-Host (" MISS " + $r) -ForegroundColor Red
$ok = $false
}
}
# ---- forbidden absent ----
foreach ($p in $forbiddenPatterns) {
$bad = @($files | Where-Object { $_ -match $p })
if ($bad.Count -gt 0) {
Write-Host (" LEAK (pattern " + $p + "): " + ($bad -join ', ')) -ForegroundColor Red
$ok = $false
} else {
Write-Host (" clean (pattern " + $p + ")")
}
}
if ($ok) {
Write-Host "PASS: package content looks correct" -ForegroundColor Green
Write-Host "note: for the compressed size (crates.io limit 10 MiB) run 'cargo package' and check target/package/*.crate"
exit 0
} else {
Write-Host "FAIL: package content is not publishable" -ForegroundColor Red
exit 1
}
} finally {
Pop-Location
}