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