# kb_bug_intake_gate_v1.1.ps1 # Corrige: -Tags et -SeenIn peuvent arriver comme une seule chaine "a,b,c" depuis CMD. # On normalise en tableau en splittant sur [,;] et espaces multiples. PS 5.1-safe. [CmdletBinding()] param( [switch]$Preview, [switch]$Execute, [string]$Root = "\\DS-918\chatgpt\ChatGPT-Gouvernance-Projets\_registry", [string]$StageRoot = "C:\Temp_Gouvernance", [Parameter(Mandatory=$true)] [string]$Title, [switch]$Blocking, [string]$Workaround = "", [string]$Note = "", [string]$Fix = "", [string[]]$Tags = @(), [string[]]$SeenIn = @(), [switch]$NoRunPipeline ) function Ensure-Dir([string]$Path){ if(-not (Test-Path -LiteralPath $Path)){ New-Item -ItemType Directory -Force -Path $Path | Out-Null } } function Ensure-Parent([string]$Target){ $p=Split-Path -Parent $Target; if($p){ Ensure-Dir $p } } function NowIso(){ (Get-Date).ToString("yyyy-MM-ddTHH:mm:ssK") } function AsciiFold([string]$s){ if($null -eq $s){ return "" } $s = $s -replace "`u2018","'" -replace "`u2019","'" -replace "`u201A","'" -replace "`u201B","'" $s = $s -replace "`u201C",'"' -replace "`u201D",'"' -replace "`u201E",'"' $s = $s -replace "`u2013","-" -replace "`u2014","-" $s = $s -replace "`u00A0"," " $norm = $s.Normalize([Text.NormalizationForm]::FormD) $sb = New-Object Text.StringBuilder foreach($ch in $norm.ToCharArray()){ $cat = [Globalization.CharUnicodeInfo]::GetUnicodeCategory($ch) if($cat -ne [Globalization.UnicodeCategory]::NonSpacingMark){ [void]$sb.Append($ch) } } $s2 = $sb.ToString() $s2 = [regex]::Replace($s2,"[^\x09\x0A\x0D\x20-\x7E]"," ") $s2 = $s2 -replace "@\{","(" $s2 = [regex]::Replace($s2,"\s{2,}"," ") $s2.Trim() } function Trunc([string]$s,[int]$n){ if($null -eq $s){ return "" } if($s.Length -le $n){ return $s } return $s.Substring(0,$n) } function Normalize-List($raw){ # Flatten: accepte string, string[], valeurs "a,b;c", espace, etc. $out = @() if($null -eq $raw){ return $out } $items = @() if($raw -is [string[]]){ $items = $raw } else { $items = @("$raw") } foreach($i in $items){ if($null -eq $i){ continue } $s = [string]$i # Enleve quotes externes if($s.Length -ge 2 -and $s.StartsWith('"') -and $s.EndsWith('"')){ $s = $s.Substring(1, $s.Length-2) } # Split sur virgules/point-virgules/espaces multiples $parts = $s -split "[,;]" # on gerera espaces juste apres foreach($p in $parts){ $t = $p.Trim() if(-not [string]::IsNullOrWhiteSpace($t)){ # si l'utilisateur a mis des espaces entre, ils restent ok (ex: "a b" voulu) $out += $t } } } return $out } # Cibles $Inbox = Join-Path $Root "updates\inbox\kb" Ensure-Dir $Inbox # Normalisation des champs $title = Trunc (AsciiFold $Title) 200 $work = Trunc (AsciiFold $Workaround) 4000 $note = Trunc (AsciiFold $Note) 8000 $fix = Trunc (AsciiFold $Fix) 4000 $tagsNorm = Normalize-List $Tags $tags = @() foreach($t in $tagsNorm){ $tags += (Trunc (AsciiFold ([string]$t)) 100) } $seenNorm = Normalize-List $SeenIn $seen = @() foreach($s in $seenNorm){ $seen += (Trunc (AsciiFold ([string]$s)) 200) } # Entry (schema KB) function Slugify([string]$s){ $t = AsciiFold $s $t = $t.ToLower() $t = [regex]::Replace($t,"[^a-z0-9]+","-") $t = [regex]::Replace($t,"-+","-").Trim("-") if([string]::IsNullOrEmpty($t)){ $t = "bug" } if($t.Length -gt 60){ $t = $t.Substring(0,60).Trim("-") } $t } $slug = Slugify $title $tsId = (Get-Date -f yyyyMMddHHmmss) $id = ("bug-" + $slug + "-" + $tsId) $entry = [ordered]@{ id=$id title=$title blocking=[bool]$Blocking workaround=$work note=$note fix=$fix tags=@($tags) seen_in_threads=@($seen) last_seen=NowIso } $json = ($entry | ConvertTo-Json -Depth 5 -Compress) # Fichier cible $autoName = ("AUTO_{0}_{1}.txt" -f (Get-Date -f yyyyMMdd_HHmmss), $slug) $target = Join-Path $Inbox $autoName # PREVIEW if($Preview -or (-not $Execute)){ Write-Host "== PREVIEW :: BUG INTAKE GATE v1.1 ==" Write-Host ("Will write -> {0}" -f $target) Write-Host "Content(JSON entry):" Write-Host $json Write-Host "No write performed (Preview)." exit 0 } # EXECUTE function Write-SafeText([string]$Target,[string]$Content,[string]$StageRoot){ Ensure-Dir $StageRoot; Ensure-Parent $Target $tmp=Join-Path $StageRoot ("write_" + [IO.Path]::GetRandomFileName()) $utf8=New-Object Text.UTF8Encoding($true) [IO.File]::WriteAllText($tmp,$Content,$utf8) $tmpR="$Target.tmp" Copy-Item -LiteralPath $tmp -Destination $tmpR -Force Move-Item -LiteralPath $tmpR -Destination $Target -Force Remove-Item -LiteralPath $tmp -Force } Write-SafeText -Target $target -Content $json -StageRoot $StageRoot Write-Host ("[OK] AUTO written -> {0}" -f $target) if(-not $NoRunPipeline){ $wrapNas = Join-Path (Join-Path $Root "scripts\wrappers") "gov_execute_nas.cmd" if(Test-Path -LiteralPath $wrapNas){ Write-Host ("[RUN] Pipeline via NAS wrapper: {0}" -f $wrapNas) & $wrapNas } else { $wrapLoc = "C:\Temp_Gouvernance\gov_execute.cmd" if(Test-Path -LiteralPath $wrapLoc){ Write-Host ("[RUN] Pipeline via local wrapper: {0}" -f $wrapLoc) & $wrapLoc } else { Write-Host "[WARN] Aucun wrapper trouvé. AUTO déposé, mais pipeline non lancée." } } }