?param([string]$FromFile,[switch]$Execute) $ErrorActionPreference='Stop' $root="\\DS-918\chatgpt\ChatGPT-Gouvernance-Projets\_registry" $kb = Join-Path $root "bug_kb\BUG_KB.json.txt" $boot = Join-Path $root "bootpack\bootpack.txt" $stage= "C:\Temp_Gouvernance" if(!(Test-Path $stage)){ New-Item -ItemType Directory -Force -Path $stage|Out-Null } $Utf8NoBom = New-Object Text.UTF8Encoding($false) $Iso = Get-Date -Format 'yyyy-MM-ddTHH:mm:ssK' function Read-KbObj([string]$Path){ if(!(Test-Path $Path)){ return [pscustomobject]@{ entries=@(); updated=$Iso } } $t = Get-Content -LiteralPath $Path -Raw $m='--- DOC-VERSION-FOOTER ---' $i=$t.IndexOf($m); if($i -ge 0){ $t=$t.Substring(0,$i).Trim() } try { $o = $t | ConvertFrom-Json } catch { return [pscustomobject]@{ entries=@(); updated=$Iso } } if($o -and $o.entries){ if($o.entries -is [array]){ return $o } else { $o.entries=@($o.entries); return $o } } return [pscustomobject]@{ entries=@(); updated=$Iso } } function Write-SafeTxt([string]$Dest,[string[]]$Lines,[string]$SourceTag){ $IsoLocal = Get-Date -Format 'yyyy-MM-ddTHH:mm:ssK' $Policy='TXT-ONLY v1.0; SAFE-WRITE v1.1; GOV_SCRIPT_GATE v1.3' $stage="C:\Temp_Gouvernance" if(!(Test-Path $stage)){ New-Item -ItemType Directory -Force -Path $stage|Out-Null } $tmpLocal = Join-Path $stage ([IO.Path]::GetFileName($Dest)) [IO.File]::WriteAllText($tmpLocal,(($Lines) -join "`r`n"),$Utf8NoBom) $sha=[Security.Cryptography.SHA256]::Create() try{ $bytes=[IO.File]::ReadAllBytes($tmpLocal) $h=[BitConverter]::ToString($sha.ComputeHash($bytes)).Replace('-','').ToLower() } finally { $sha.Dispose() } [IO.File]::AppendAllText($tmpLocal,"`r`n`r`n--- DOC-VERSION-FOOTER ---`r`nGenerated: $IsoLocal`r`nSHA-256: $h`r`nPolicy: $Policy`r`nSource: KB_QUICKLOG`r`n",$Utf8NoBom) $tmp="$Dest.tmp"; if(!(Test-Path (Split-Path $Dest))){ New-Item -ItemType Directory -Force -Path (Split-Path $Dest)|Out-Null } Copy-Item $tmpLocal $tmp -Force if(Test-Path $Dest){ Copy-Item $Dest ($Dest+".bak_"+(Get-Date -Format 'yyyyMMdd_HHmmss')) -Force } Move-Item $tmp $Dest -Force } function Update-Bootpack-BugJson([string]$BootpackPath,[string]$JsonString){ if(!(Test-Path $BootpackPath)){ return } $bp = Get-Content -LiteralPath $BootpackPath -Raw $nl="`r`n" $start=[regex]::Match($bp,'(?ms)^\[BUG_KB_JSON\]\s*') if($start.Success){ $before = $bp.Substring(0,$start.Index + $start.Length) $afterMatch = [regex]::Match($bp.Substring($start.Index + $start.Length),'(?ms)^\[\w[^\]]*\]') if($afterMatch.Success){ $afterIdx = $start.Index + $start.Length + $afterMatch.Index $new = $before + $JsonString + $nl + $bp.Substring($afterIdx) } else { $new = $before + $JsonString + $nl } } else { $new = $bp.TrimEnd() + $nl + "[BUG_KB_JSON]" + $nl + $JsonString + $nl } $tmp="$BootpackPath.tmp" [IO.File]::WriteAllText($tmp,$new,$Utf8NoBom) if(Test-Path $BootpackPath){ Copy-Item $BootpackPath ($BootpackPath+'.bak_'+(Get-Date -Format 'yyyyMMdd_HHmmss')) -Force } Move-Item $tmp $BootpackPath -Force } if(-not $FromFile -or -not (Test-Path $FromFile)){ Write-Error "Missing -FromFile"; exit 1 } $lines = Get-Content -LiteralPath $FromFile -ErrorAction Stop $map=@{}; foreach($ln in $lines){ if($ln -match '^\s*([^:]+)\s*:\s*(.*)$'){ $map[$matches[1].Trim()]=$matches[2] } } $title = if([string]::IsNullOrWhiteSpace($map['Title'])){ "Untitled KB entry" } else { $map['Title'] } $blocking = ($map['Blocking'] -match 'true|1|yes') $rootcause = $map['RootCause']; $workaround=$map['Workaround']; $fix=$map['Fix'] $tags = @(); if($map['Tags']){ $tags = @($map['Tags'].Split(',') | ForEach-Object { $_.Trim() } | Where-Object { $_ }) } $seen = @(); if($map['SeenInThreads']){ $seen=@($map['SeenInThreads'].Split(',') | ForEach-Object { $_.Trim() } | Where-Object { $_ }) } $slug = ($title.ToLower() -replace '[^a-z0-9]+','-').Trim('-') $id = "KB-AUTO-"+$slug+"-"+(Get-Date -Format 'yyyyMMdd_HHmmss') $kbObj = Read-KbObj $kb $existing = $kbObj.entries | Where-Object { $_.title -and (($_.title.ToLower() -replace '[^a-z0-9]+','-').Trim('-') -eq $slug) } if($existing){ foreach($e in $existing){ if($blocking){ $e.blocking=$true } if($rootcause){ $e.note=$rootcause } if($workaround){ $e.workaround=$workaround } if($fix){ $e.fix=$fix } if($tags){ $e.tags = @([string[]]($e.tags + $tags | Select-Object -Unique)) } if($seen){ $e.seen_in_threads = @([string[]]($e.seen_in_threads + $seen | Select-Object -Unique)) } $e.last_seen = (Get-Date -Format 'yyyy-MM-ddTHH:mm:ssK') } }else{ $entry = [pscustomobject]@{ id=$id; title=$title; blocking=$blocking; workaround=$workaround; note=$rootcause; fix=$fix; tags=$tags; seen_in_threads=$seen; last_seen=(Get-Date -Format 'yyyy-MM-ddTHH:mm:ssK') } $kbObj.entries += ,$entry } $kbObj.updated = (Get-Date -Format 'yyyy-MM-ddTHH:mm:ssK') $json = $kbObj | ConvertTo-Json -Depth 20 Write-SafeTxt -Dest $kb -Lines ($json -split "`r?`n") -SourceTag "KB_QUICKLOG" Update-Bootpack-BugJson -BootpackPath $boot -JsonString $json $day=(Get-Date -Format 'yyyy-MM-dd'); $t=(Get-Date -Format 'HHmmss') $archDir = Join-Path (Join-Path $root 'threads_archive') $day $archDir = Join-Path $archDir $t if(!(Test-Path $archDir)){ New-Item -ItemType Directory -Force -Path $archDir | Out-Null } [IO.File]::WriteAllText((Join-Path $archDir 'kb_quicklog_snapshot.txt'),"FromFile: $FromFile`r`nUpdated: $($kbObj.updated)`r`n",(New-Object Text.UTF8Encoding($false))) $log = Join-Path $root 'logs\registry_activity.log' $old=@(); if(Test-Path $log){ $old += Get-Content -LiteralPath $log -ErrorAction SilentlyContinue } $old += ("[{0}] kb_quicklog: from={1}" -f (Get-Date -Format 'yyyy-MM-ddTHH:mm:ssK'),$FromFile) [IO.File]::WriteAllText($log, ($old -join "`r`n"), (New-Object Text.UTF8Encoding($false))) Write-Host "[KB] updated -> $kb"