Add report.bat to collect diagnostics, and a VERSION.txt stamp

Nothing in this repo ever updated Melissa's PC, so "it isn't working"
could not be told apart from "she is running last month's copy" - which
is what it turned out to be today.

report.bat writes one text file to the Desktop with the installed
version, which files are present, whether WinSCPnet.dll loads or is
blocked, every entity.json (passwords masked to ********), what is
actually sitting in today's date folders, and the tail of the last log.

A missing VERSION.txt is itself the answer: it means the copy predates
2026-08-26 and has no setup wizard.
This commit is contained in:
2026-08-26 20:37:11 -05:00
parent 63ea07167c
commit 215db8ab19
4 changed files with 181 additions and 0 deletions
+3
View File
@@ -11,3 +11,6 @@ WinSCP/
# Keep example templates # Keep example templates
!Entities/EXAMPLE/entity.json !Entities/EXAMPLE/entity.json
!Entities/EXAMPLE/Export1/PRACTICE_NAME/practice.json !Entities/EXAMPLE/Export1/PRACTICE_NAME/practice.json
# Version stamp - report.bat uses this to tell which build is installed
!VERSION.txt
+173
View File
@@ -0,0 +1,173 @@
# Collects everything needed to diagnose "it isn't working" into one text file
# on the Desktop. Passwords are masked. Called by report.bat.
$scriptRoot = Split-Path -Parent $PSCommandPath
$stamp = Get-Date -Format "yyyyMMdd-HHmm"
$desktop = [Environment]::GetFolderPath("Desktop")
if (-not $desktop) { $desktop = $scriptRoot }
$outFile = Join-Path $desktop "MelissaUploader-Report-$stamp.txt"
$lines = New-Object System.Collections.ArrayList
function Add-Line($text) { [void]$lines.Add([string]$text) }
function Add-Head($text) {
Add-Line ""
Add-Line ("=" * 60)
Add-Line " $text"
Add-Line ("=" * 60)
}
Add-Line "Melissa Uploader report"
Add-Line "Taken : $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')"
Add-Line "PC : $env:COMPUTERNAME"
Add-Line "User : $env:USERNAME"
Add-Line "Folder : $scriptRoot"
Add-Line "Windows : $([Environment]::OSVersion.VersionString)"
Add-Line "PowerShell: $($PSVersionTable.PSVersion)"
# -- Which version of the tool is installed -----------------------------------
Add-Head "Version"
$versionFile = Join-Path $scriptRoot "VERSION.txt"
if (Test-Path $versionFile) {
Add-Line "VERSION.txt: $((Get-Content $versionFile -Raw).Trim())"
} else {
Add-Line "VERSION.txt: MISSING"
Add-Line ">> This copy is older than 2026-08-26. It does not have the setup"
Add-Line ">> wizard. Whoever maintains this needs to copy the new files over."
}
# -- Which files are present --------------------------------------------------
Add-Head "Files in the folder"
$expected = @("engine.ps1", "run-all.bat", "run-all.ps1", "run.bat", "setup.bat",
"setup-entity.bat", "Setup-Entity.ps1", "Get-WinSCP.ps1", "Get-Report.ps1")
foreach ($name in $expected) {
$path = Join-Path $scriptRoot $name
if (Test-Path $path) {
$item = Get-Item $path
Add-Line (" {0,-20} {1} {2,8} bytes" -f $name, $item.LastWriteTime.ToString("yyyy-MM-dd HH:mm"), $item.Length)
} else {
Add-Line (" {0,-20} MISSING" -f $name)
}
}
# -- WinSCP -------------------------------------------------------------------
Add-Head "WinSCP"
$winscpDll = Join-Path $scriptRoot "WinSCP\WinSCPnet.dll"
if (-not (Test-Path $winscpDll)) {
Add-Line "WinSCPnet.dll MISSING - setup.bat has not been run (or it failed)."
} else {
$dll = Get-Item $winscpDll
Add-Line "WinSCPnet.dll found: $($dll.Length) bytes, $($dll.LastWriteTime)"
$zone = Get-Item $winscpDll -Stream Zone.Identifier -ErrorAction SilentlyContinue
if ($zone) {
Add-Line "BLOCKED by Windows (Mark of the Web) - engine.ps1 unblocks this itself on every run."
} else {
Add-Line "Not blocked."
}
try {
Add-Type -Path $winscpDll -ErrorAction Stop
Add-Line "Loads OK."
} catch {
Add-Line "WILL NOT LOAD: $($_.Exception.Message)"
}
}
# -- Entities -----------------------------------------------------------------
Add-Head "Entities"
$entitiesDir = Join-Path $scriptRoot "Entities"
if (-not (Test-Path $entitiesDir)) {
Add-Line "No Entities\ folder at all."
} else {
$entities = @(Get-ChildItem -Path $entitiesDir -Directory | Where-Object { $_.Name -ne "EXAMPLE" })
if ($entities.Count -eq 0) { Add-Line "Entities\ exists but is empty." }
foreach ($entity in $entities) {
Add-Line ""
Add-Line ("-- $($entity.Name) " + ("-" * 40))
$configFile = Join-Path $entity.FullName "entity.json"
if (-not (Test-Path $configFile)) {
Add-Line " entity.json MISSING - this entity is skipped by run-all."
} else {
$raw = Get-Content $configFile -Raw
# Mask anything that looks like a password before this leaves the PC.
$masked = $raw -replace '("password"\s*:\s*")[^"]*(")', '$1********$2'
Add-Line " entity.json:"
foreach ($l in ($masked -split "`r?`n")) { Add-Line " $l" }
try {
$raw | ConvertFrom-Json | Out-Null
Add-Line " Reads as valid JSON."
} catch {
Add-Line " *** THIS FILE IS NOT VALID JSON *** $($_.Exception.Message)"
}
}
$export1 = Join-Path $entity.FullName "Export1"
if (-not (Test-Path $export1)) {
Add-Line " No Export1\ folder."
continue
}
$today = Get-Date -Format "yyyyMMdd"
foreach ($practice in (Get-ChildItem -Path $export1 -Directory)) {
$dateDir = Join-Path $practice.FullName $today
if (Test-Path $dateDir) {
$files = @(Get-ChildItem -Path $dateDir -File)
$byExt = ($files | Group-Object Extension |
ForEach-Object { "$($_.Count)x $($_.Name)" }) -join ", "
if (-not $byExt) { $byExt = "empty" }
Add-Line " $($practice.Name): $today\ has $($files.Count) file(s) - $byExt"
} else {
Add-Line " $($practice.Name): no $today\ folder"
}
$pj = Join-Path $practice.FullName "practice.json"
if (Test-Path $pj) {
$pmasked = (Get-Content $pj -Raw) -replace '("password"\s*:\s*")[^"]*(")', '$1********$2'
Add-Line " practice.json: $($pmasked -replace '\s+', ' ')"
}
}
}
}
# -- Logs ---------------------------------------------------------------------
Add-Head "Most recent log"
$logsDir = Join-Path $scriptRoot "Logs"
if (-not (Test-Path $logsDir)) {
Add-Line "No Logs\ folder - engine.ps1 has never completed a run here."
} else {
$log = Get-ChildItem -Path $logsDir -Filter "*.log" |
Where-Object { $_.Name -notmatch "winscp_debug" } |
Sort-Object LastWriteTime -Descending | Select-Object -First 1
if (-not $log) {
Add-Line "Logs\ is empty."
} else {
Add-Line "$($log.Name) (last written $($log.LastWriteTime))"
Add-Line ""
$content = Get-Content $log.FullName -Tail 120
foreach ($l in $content) { Add-Line " $l" }
}
}
Add-Head "End of report"
# -- Write it out -------------------------------------------------------------
$text = $lines -join "`r`n"
try {
Set-Content -Path $outFile -Value $text -Encoding UTF8
$saved = $true
} catch {
$outFile = Join-Path $scriptRoot "MelissaUploader-Report-$stamp.txt"
Set-Content -Path $outFile -Value $text -Encoding UTF8
$saved = $true
}
Write-Host $text
Write-Host ""
Write-Host ("=" * 60) -ForegroundColor Cyan
if ($saved) {
Write-Host "Report saved to:" -ForegroundColor Green
Write-Host " $outFile" -ForegroundColor Green
Write-Host ""
Write-Host "Send that file to Brad - it has everything needed to work out" -ForegroundColor Cyan
Write-Host "what went wrong. Passwords are replaced with ******** in it." -ForegroundColor Cyan
}
Write-Host ("=" * 60) -ForegroundColor Cyan
+1
View File
@@ -0,0 +1 @@
63ea071 2026-08-26 setup wizard
+4
View File
@@ -0,0 +1,4 @@
@echo off
REM Collects what's needed to diagnose a problem into a text file on the Desktop.
powershell.exe -ExecutionPolicy Bypass -File "%~dp0Get-Report.ps1"
pause