# rebuild_bootpack_full_hub_v1.1.ps1 (WRITE-ENABLED FIX) # But : reconstruire/mettre à jour le BootPack HUB en alignant le pointeur [BUG_KB_JSON_POINTER] # Règles : PS 5.1 strict, ASCII-safe, UTF-8 avec BOM, lecture seule par défaut (Preview), écriture via -Write # Garde-fous : SAFE-CREATE (__tmp__ + .bak horodaté + move atomique), aucune here-string, aucun opérateur PS7/ternaire param( [Parameter(Mandatory=$true)][string]$Root, [switch]$Preview, [switch]$Write ) # ---------- Utilitaires sûrs ---------- function Get-FileSha256([string]$Path){ if(-not (Test-Path -LiteralPath $Path)){ return "MISSING" } try{ $h = Get-FileHash -LiteralPath $Path -Algorithm SHA256 return $h.Hash.ToUpperInvariant() }catch{ return "ERROR" } } function Read-TextNoBom([string]$Path){ if(-not (Test-Path -LiteralPath $Path)){ return "" } $raw = Get-Content -LiteralPath $Path -Raw -Encoding UTF8 # Strip BOM en tête if($raw.Length -gt 0 -and [int][char]$raw[0] -eq 0xFEFF){ $raw = $raw.Substring(1) } # Strip éventuels FEFF internes $raw = ($raw.ToCharArray() | ForEach-Object { if([int]$_ -ne 0xFEFF){ $_ } }) -join '' return $raw } function Normalize-CRLF([string]$s){ if($null -eq $s){ return "" } $s = $s -replace "(\r\n|\n|\r)", "`r`n" return $s } function Count-EntriesFromJson([string]$JsonPath){ if(-not (Test-Path -LiteralPath $JsonPath)){ return -1 } $raw = Read-TextNoBom -Path $JsonPath # Certains fichiers ont un footer après le dernier '}' -> tronquons proprement $last = $raw.LastIndexOf('}') if($last -gt 0){ $raw = $raw.Substring(0,$last+1) } try{ $obj = $raw | ConvertFrom-Json -ErrorAction Stop if($null -ne $obj -and $obj.PSObject.Properties.Match("entries").Count -gt 0 -and $null -ne $obj.entries){ $arr = @($obj.entries) return $arr.Count } else { return -1 } }catch{ return -1 } } function Get-FileSize([string]$Path){ if(-not (Test-Path -LiteralPath $Path)){ return 0 } try{ return (Get-Item -LiteralPath $Path).Length }catch{ return 0 } } function Write-SafeText([string]$Target,[string]$Content){ # Ecrit en UTF-8 avec BOM, via __tmp__ et backup .bak $parent = Split-Path -Parent $Target if(-not (Test-Path -LiteralPath $parent)){ New-Item -ItemType Directory -Path $parent -Force | Out-Null } $tmp = ($Target + ".__tmp__") $destTmp = ($Target + ".__tmp2__") # Sauvegarde .bak si cible existe $bak = $null if(Test-Path -LiteralPath $Target){ $bak = ($Target + "." + (Get-Date).ToString("yyyyMMdd_HHmmss") + ".bak") Copy-Item -LiteralPath $Target -Destination $bak -Force } # Encodage UTF-8 avec BOM $utf8Bom = New-Object System.Text.UTF8Encoding($true) [System.IO.File]::WriteAllText($tmp, (Normalize-CRLF $Content), $utf8Bom) if(Test-Path -LiteralPath $destTmp){ Remove-Item -LiteralPath $destTmp -Force } Copy-Item -LiteralPath $tmp -Destination $destTmp -Force Move-Item -LiteralPath $destTmp -Destination $Target -Force return $bak } function Replace-PointerBlock([string]$BootPackPath,[string]$KbPath,[string]$KbSha,[int]$Entries,[long]$Size,[string]$UpdatedIso){ $txt = Read-TextNoBom -Path $BootPackPath $txt = Normalize-CRLF $txt $lines = $txt -split "`r`n", -1 $out = @() $i = 0 $n = $lines.Length $replaced = $false while($i -lt $n){ $line = $lines[$i] if($line -ceq "[BUG_KB_JSON_POINTER]"){ # Garder l'entête, puis écraser les 5 lignes qui suivent $out += $line $i = $i + 1 # Sauter les anciennes lignes clés tant qu'on ne retombe pas sur un en-tête de section while($i -lt $n){ $l = $lines[$i] if($l -match "^\[.+\]$"){ break } # prochaine section $i = $i + 1 } # Injecter les 5 lignes normalisées $out += ("Path={0}" -f $KbPath) $out += ("SHA256={0}" -f $KbSha) $out += ("Entries={0}" -f $Entries) $out += ("Size={0}" -f $Size) $out += ("Updated={0}" -f $UpdatedIso) $replaced = $true continue } $out += $line $i = $i + 1 } if(-not $replaced){ if($out.Count -gt 0 -and $out[$out.Count-1] -ne ""){ $out += "" } $out += "[BUG_KB_JSON_POINTER]" $out += ("Path={0}" -f $KbPath) $out += ("SHA256={0}" -f $KbSha) $out += ("Entries={0}" -f $Entries) $out += ("Size={0}" -f $Size) $out += ("Updated={0}" -f $UpdatedIso) } $nl = "`r`n" return ($out -join $nl) } # ---------- Résolution chemins ---------- $BootPack1 = Join-Path $Root "bootpack\\bootpack.txt" $BootPack2 = Join-Path $Root "bootpack\\bootpack_paste.txt" $Profile = Join-Path $Root "profiles\\project\\HUB\\PROJECT_PROFILE.txt" $KbPath = Join-Path $Root "bug_kb\\BUG_KB.json.txt" $snapDir = Join-Path $Root "snapshots" # ---------- Faits (lecture seule) ---------- $profileSha = Get-FileSha256 -Path $Profile $kbSha = Get-FileSha256 -Path $KbPath $kbEntries = Count-EntriesFromJson -JsonPath $KbPath $kbSize = Get-FileSize -Path $KbPath $kbUpdated = (Get-Item -LiteralPath $KbPath -ErrorAction SilentlyContinue).LastWriteTime.ToString("yyyy-MM-ddTHH:mm:ssK") # Dernier snapshot si présent $snapFile = $null if(Test-Path -LiteralPath $snapDir){ $cands = Get-ChildItem -LiteralPath $snapDir -Filter "snapshot_*.json" -File | Sort-Object -Property LastWriteTime -Descending if($cands -and $cands.Count -gt 0){ $snapFile = $cands[0].FullName } } $snapSha = "MISSING" if($snapFile){ $snapSha = Get-FileSha256 -Path $snapFile } # Règles actives (affichage informatif) $rules = @("GOV_SCRIPT_GATE v1.4","SAFE-WRITE v1.1","TXT-ONLY v1.0","SYNC-MEM-ARCHIVE-RULE v1.0","SYNC-GUARD v1.1") $memId = (Get-Date).ToString("yyyy-MM-ddTHH:mm:ss") # ---------- PREVIEW ---------- Write-Host "== PREVIEW :: REBUILD BOOTPACK (HUB) ==" Write-Host ("Target BootPack : {0}" -f $BootPack1) Write-Host ("PROFILE_FILE : {0} (SHA={1})" -f $Profile, $profileSha) Write-Host ("KB Pointer : {0} (SHA={1}, Entries={2}, Size={3}, Updated={4})" -f $KbPath, $kbSha, $kbEntries, $kbSize, $kbUpdated) if($snapFile){ Write-Host ("SNAPSHOT_FILE : {0} (SHA={1})" -f $snapFile, $snapSha) }else{ Write-Host ("SNAPSHOT_FILE : {0} (SHA={1})" -f (Join-Path $Root "snapshots\\snapshot_YYYY-MM-DD.json"), $snapSha) } Write-Host ("RULESET_ACTIVE : {0}" -f ($rules -join " ; ")) Write-Host ("MEM_SYNC_ID : {0}" -f $memId) Write-Host ("MANIFEST_SHA256 : {0}" -f "UNKNOWN") if(-not $Write){ Write-Host "Mode PREVIEW : aucune ecriture effectuee." exit 0 } # ---------- WRITE ---------- $updatedIso = (Get-Date).ToString("yyyy-MM-ddTHH:mm:ssK") $buf1 = Replace-PointerBlock -BootPackPath $BootPack1 -KbPath $KbPath -KbSha $kbSha -Entries $kbEntries -Size $kbSize -UpdatedIso $updatedIso $buf2 = Replace-PointerBlock -BootPackPath $BootPack2 -KbPath $KbPath -KbSha $kbSha -Entries $kbEntries -Size $kbSize -UpdatedIso $updatedIso $bak1 = Write-SafeText -Target $BootPack1 -Content $buf1 $bak2 = Write-SafeText -Target $BootPack2 -Content $buf2 $bak1Msg = $bak1; if(-not $bak1Msg){ $bak1Msg = "" } $bak2Msg = $bak2; if(-not $bak2Msg){ $bak2Msg = "" } Write-Host ("[OK] BootPack updated (HUB) -> {0}" -f $BootPack1) Write-Host ("Backup1: {0}" -f $bak1Msg) Write-Host ("Backup2: {0}" -f $bak2Msg) exit 0