Files
Core_Be/tools/Update-Md5Reference.ps1
T
Dhananjaya99 bffc9b1686 first commit
2026-09-28 11:44:26 +05:30

222 lines
7.4 KiB
PowerShell

param(
[string]$OutputPath = "PROJECT_MD5_REFERENCE.md",
[switch]$Verify
)
$ErrorActionPreference = "Stop"
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$fallbackRoot = Resolve-Path -LiteralPath (Join-Path $scriptDir "..")
try {
$gitRoot = git -C $fallbackRoot rev-parse --show-toplevel 2>$null
if ([string]::IsNullOrWhiteSpace($gitRoot)) {
$repoRoot = $fallbackRoot.Path
}
else {
$repoRoot = (Resolve-Path -LiteralPath $gitRoot.Trim()).Path
}
}
catch {
$repoRoot = $fallbackRoot.Path
}
Push-Location $repoRoot
try {
$normalizedOutputPath = $OutputPath.Replace("\", "/")
$files = @()
try {
$files = git ls-files --cached --modified --others --exclude-standard |
ForEach-Object { $_.Replace("\", "/") } |
Where-Object { $_ -and $_ -ne $normalizedOutputPath }
}
catch {
$ignoredDirectories = @(".git", ".vs", "bin", "obj", "publish", "Migrations")
$ignoredExtensions = @(".user", ".suo", ".userosscache", ".sln.docstates", ".log", ".zip")
$files = Get-ChildItem -LiteralPath $repoRoot -Recurse -File |
Where-Object {
$relative = [System.IO.Path]::GetRelativePath($repoRoot, $_.FullName).Replace("\", "/")
$parts = $relative -split "/"
$extension = [System.IO.Path]::GetExtension($_.Name)
($ignoredDirectories | Where-Object { $parts -contains $_ }).Count -eq 0 -and
($ignoredExtensions -notcontains $extension) -and
$relative -ne $normalizedOutputPath
} |
ForEach-Object { [System.IO.Path]::GetRelativePath($repoRoot, $_.FullName).Replace("\", "/") }
}
$files = $files | Sort-Object -Unique
$hashRows = foreach ($file in $files) {
$fullPath = Join-Path $repoRoot $file
if (Test-Path -LiteralPath $fullPath -PathType Leaf) {
$item = Get-Item -LiteralPath $fullPath
$hash = Get-FileHash -LiteralPath $fullPath -Algorithm MD5
[PSCustomObject]@{
Path = $file
Size = $item.Length
MD5 = $hash.Hash.ToLowerInvariant()
}
}
}
$statusLines = @()
try {
$statusLines = git status --short |
Where-Object {
$statusPath = ($_ -replace "^[ MADRCU?!]{2}\s+", "").Replace("\", "/")
$statusPath -ne $normalizedOutputPath
}
}
catch {
$statusLines = @()
}
if ($Verify) {
if (-not (Test-Path -LiteralPath $OutputPath -PathType Leaf)) {
throw "Cannot verify because $OutputPath does not exist."
}
$expectedRows = Get-Content -LiteralPath $OutputPath |
ForEach-Object {
if ($_ -match "^\| (?<Path>.+?) \| (?<Size>\d+) \| (?<MD5>[a-f0-9]{32}) \|$") {
[PSCustomObject]@{
Path = $Matches.Path
Size = [int64]$Matches.Size
MD5 = $Matches.MD5
}
}
}
$expectedByPath = @{}
foreach ($row in $expectedRows) {
$expectedByPath[$row.Path] = $row
}
$currentByPath = @{}
foreach ($row in $hashRows) {
$currentByPath[$row.Path] = $row
}
$added = @($currentByPath.Keys | Where-Object { -not $expectedByPath.ContainsKey($_) } | Sort-Object)
$removed = @($expectedByPath.Keys | Where-Object { -not $currentByPath.ContainsKey($_) } | Sort-Object)
$changed = @(
foreach ($path in $expectedByPath.Keys) {
if ($currentByPath.ContainsKey($path) -and $currentByPath[$path].MD5 -ne $expectedByPath[$path].MD5) {
"$path ($($expectedByPath[$path].MD5) -> $($currentByPath[$path].MD5))"
}
}
) | Sort-Object
if ($added.Count -eq 0 -and $removed.Count -eq 0 -and $changed.Count -eq 0) {
Write-Host "MD5 reference verified. No changes detected across $($hashRows.Count) files."
return
}
Write-Host "MD5 reference verification found differences."
if ($added.Count -gt 0) {
Write-Host ""
Write-Host "Added files:"
$added | ForEach-Object { Write-Host " $_" }
}
if ($removed.Count -gt 0) {
Write-Host ""
Write-Host "Removed files:"
$removed | ForEach-Object { Write-Host " $_" }
}
if ($changed.Count -gt 0) {
Write-Host ""
Write-Host "Changed files:"
$changed | ForEach-Object { Write-Host " $_" }
}
exit 1
}
$projectName = Split-Path -Leaf $repoRoot
$generatedAt = Get-Date -Format "yyyy-MM-dd HH:mm:ss zzz"
$targetFramework = ""
if (Test-Path -LiteralPath "Fitness_core_BE.csproj") {
$projectXml = [xml](Get-Content -Raw -LiteralPath "Fitness_core_BE.csproj")
$targetFramework = $projectXml.Project.PropertyGroup.TargetFramework
}
$topLevelCounts = $hashRows |
Group-Object {
if ($_.Path.Contains("/")) {
($_.Path -split "/")[0]
}
else {
"."
}
} |
Sort-Object Name
$lines = [System.Collections.Generic.List[string]]::new()
$lines.Add("# Project MD5 Reference")
$lines.Add("")
$lines.Add("Generated: $generatedAt")
$lines.Add("")
$lines.Add("Project: $projectName")
if (-not [string]::IsNullOrWhiteSpace($targetFramework)) {
$lines.Add("")
$lines.Add("Target framework: $targetFramework")
}
$lines.Add("")
$lines.Add("Purpose: baseline MD5 hashes for future implementation checks. MD5 is used here for change detection, not for security-sensitive integrity verification.")
$lines.Add("")
$lines.Add('Manifest scope: Git tracked files plus untracked files not ignored by `.gitignore`; build outputs, IDE files, ignored migrations, and this manifest are excluded.')
$lines.Add("")
$lines.Add("Regenerate command:")
$lines.Add("")
$lines.Add('```powershell')
$lines.Add('.\tools\Update-Md5Reference.ps1')
$lines.Add('```')
$lines.Add("")
$lines.Add("Verify command:")
$lines.Add("")
$lines.Add('```powershell')
$lines.Add('.\tools\Update-Md5Reference.ps1 -Verify')
$lines.Add('```')
$lines.Add("")
$lines.Add("## Project Shape")
$lines.Add("")
$lines.Add("| Area | Files |")
$lines.Add("| --- | ---: |")
foreach ($group in $topLevelCounts) {
$lines.Add("| $($group.Name) | $($group.Count) |")
}
$lines.Add("")
$lines.Add("## Git Status At Baseline")
$lines.Add("")
if ($statusLines.Count -gt 0) {
$lines.Add('```text')
foreach ($status in $statusLines) {
$lines.Add($status)
}
$lines.Add('```')
}
else {
$lines.Add("Clean working tree or Git status unavailable.")
}
$lines.Add("")
$lines.Add("## MD5 Hashes")
$lines.Add("")
$lines.Add("| Path | Size Bytes | MD5 |")
$lines.Add("| --- | ---: | --- |")
foreach ($row in $hashRows) {
$lines.Add("| $($row.Path) | $($row.Size) | $($row.MD5) |")
}
$lines.Add("")
Set-Content -LiteralPath $OutputPath -Value $lines -Encoding UTF8
Write-Host "Wrote $OutputPath with $($hashRows.Count) MD5 hashes."
}
finally {
Pop-Location
}