# gov_profile_launcher_v1.0.ps1 # Objet : Launcher menu (HUB/SEEDBOX) pour (Preview/Write) + Audit MANIFEST. Execution via powershell.exe -ExecutionPolicy Bypass, logs propres. # Invariants L0 attendus : GOV_SCRIPT_GATE v1.4 ; SAFE-WRITE v1.1 ; TXT-ONLY v1.0 ; SYNC-MEM-ARCHIVE-RULE v1.0 ; SYNC-GUARD v1.1 # PS 5.1-safe : pas de ternaire, pas d'opérateurs ?. / ??, pas de -Recurse dans Select-String. param() # === Utilitaires === function Ensure-Dir([string]$p){ if($p -and !(Test-Path $p)){ New-Item -ItemType Directory -Force -Path $p | Out-Null } } function Ask-YesNo([string]$q){ while($true){ $a=Read-Host ($q+' (O/N)') if($a -in @('O','o','Y','y')){ return $true } if($a -in @('N','n')){ return $false } } } function Run-Profile([string]$profile,[string]$mode){ $root='\\DS-918\chatgpt\ChatGPT-Gouvernance-Projets\_registry' $logs=Join-Path $root 'logs' Ensure-Dir $logs $ts=Get-Date -Format 'yyyyMMdd_HHmmss' $log=Join-Path $logs ('run_'+$profile+'_'+$mode+'_'+$ts+'.txt') # Résolution du script cible selon le profil if($profile -eq 'HUB'){ $script=Join-Path $root 'scripts\rebuild_bootpack_full_hub_v1.1.ps1' } elseif($profile -eq 'SEEDBOX'){ $script=Join-Path $root 'scripts\rebuild_bootpack_full_seedbox_v1.1.ps1' } else { Write-Host "[ERR] Profil inconnu: $profile"; return } if(!(Test-Path $script)){ Write-Host "[ERR] Script cible introuvable: $script"; return } # Preview -> no-touch ; Write -> demande de confirmation if($mode -eq 'Write'){ $ok = Ask-YesNo "Confirmer l'écriture réelle pour $profile" if(-not $ok){ Write-Host "Annulé."; return } } # Exécution via powershell.exe -ExecutionPolicy Bypass + log $arg = @('-NoProfile','-ExecutionPolicy','Bypass','-File',$script) if($mode -eq 'Preview'){ $arg += '-Preview' } else { $arg += '-Write' } & 'powershell.exe' @arg 2>&1 | Tee-Object -FilePath $log | Out-Null # Affiche la fin du log (aperçu immédiat) + chemin if(Test-Path $log){ Get-Content -LiteralPath $log -Tail 60 'Log -> '+$log | Write-Host } else { Write-Host "[WARN] Fichier log non trouvé: $log" } } function Audit-Manifest(){ $bp='\\DS-918\chatgpt\ChatGPT-Gouvernance-Projets\_registry\bootpack\bootpack.txt' if(!(Test-Path $bp)){ Write-Host "[ERR] BootPack introuvable: $bp"; return } $raw = Get-Content -LiteralPath $bp -Raw -Encoding UTF8 $sync= [regex]::Match($raw,'(?ms)^\[SYNC_MANIFEST\]\s*(.*?)(?=^\[|\Z)').Groups[1].Value.Trim() if(-not $sync){ Write-Host "[ERR] Section [SYNC_MANIFEST] absente."; return } $infile = [regex]::Match($sync,'(?mi)^\s*MANIFEST_SHA256\s*:\s*([0-9a-f]+)\s*$').Groups[1].Value.ToLower() $lines = $sync -split "`r?`n" $buf = New-Object System.Text.StringBuilder foreach($ln in $lines){ if($ln -match '^\s*MANIFEST_SHA256\s*:'){ continue } [void]$buf.AppendLine($ln.TrimEnd()) } $core = $buf.ToString() $sha = [BitConverter]::ToString([Security.Cryptography.SHA256]::Create().ComputeHash([Text.Encoding]::UTF8.GetBytes($core))).Replace('-','').ToLower() 'InFile = '+$infile | Write-Host 'Recalc = '+$sha | Write-Host 'STATUS = '+($(if($infile -eq $sha){'MATCH'}else{'DIFF'})) | Write-Host } function Show-Menu(){ Clear-Host Write-Host "=== GOV PROFILE LAUNCHER v1.0 ===" Write-Host "L0 requis : GOV_SCRIPT_GATE v1.4 ; SAFE-WRITE v1.1 ; TXT-ONLY v1.0 ; SYNC-MEM-ARCHIVE-RULE v1.0 ; SYNC-GUARD v1.1" Write-Host "" Write-Host "1) HUB - Preview" Write-Host "2) HUB - Write (commit)" Write-Host "3) SEEDBOX - Preview" Write-Host "4) SEEDBOX - Write (commit)" Write-Host "5) Audit MANIFEST (recalc vs fichier)" Write-Host "0) Quitter" Write-Host "" } # === Boucle principale === while($true){ Show-Menu $c = Read-Host "Choix" switch($c){ '1' { Run-Profile -profile 'HUB' -mode 'Preview' ; Pause } '2' { Run-Profile -profile 'HUB' -mode 'Write' ; Pause } '3' { Run-Profile -profile 'SEEDBOX' -mode 'Preview' ; Pause } '4' { Run-Profile -profile 'SEEDBOX' -mode 'Write' ; Pause } '5' { Audit-Manifest ; Pause } '0' { break } default { Write-Host "Choix invalide." ; Start-Sleep -Seconds 1 } } }