From 215db8ab19f43c6c4d7faa3afdc6b9fab0b044ec Mon Sep 17 00:00:00 2001 From: Brad Lance Date: Wed, 26 Aug 2026 20:37:11 -0500 Subject: [PATCH] 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. --- .gitignore | 3 + Get-Report.ps1 | 173 +++++++++++++++++++++++++++++++++++++++++++++++++ VERSION.txt | 1 + report.bat | 4 ++ 4 files changed, 181 insertions(+) create mode 100644 Get-Report.ps1 create mode 100644 VERSION.txt create mode 100644 report.bat diff --git a/.gitignore b/.gitignore index 76d943b..b830471 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,6 @@ WinSCP/ # Keep example templates !Entities/EXAMPLE/entity.json !Entities/EXAMPLE/Export1/PRACTICE_NAME/practice.json + +# Version stamp - report.bat uses this to tell which build is installed +!VERSION.txt diff --git a/Get-Report.ps1 b/Get-Report.ps1 new file mode 100644 index 0000000..8ebecf6 --- /dev/null +++ b/Get-Report.ps1 @@ -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 diff --git a/VERSION.txt b/VERSION.txt new file mode 100644 index 0000000..5858312 --- /dev/null +++ b/VERSION.txt @@ -0,0 +1 @@ +63ea071 2026-08-26 setup wizard diff --git a/report.bat b/report.bat new file mode 100644 index 0000000..d592911 --- /dev/null +++ b/report.bat @@ -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