<# .SYNOPSIS Construit le paquet d'extension Blender de Slice Wrap dans dist\. .DESCRIPTION Valide blender_manifest.toml, construit slice_wrap-.zip avec « blender --command extension build » (exclusions : section [build] du manifeste), puis vérifie que le zip ne contient ni tests, ni .claude, ni CLAUDE.md, ni .git. Renvoie le chemin du zip. .EXAMPLE .\tools\Build-Package.ps1 .EXAMPLE .\tools\Build-Package.ps1 -RunTests -Blender "C:\Program Files\Blender Foundation\Blender 5.2\blender.exe" #> param( # Exécutable Blender (défaut : variable BLENDER_EXE, sinon le Blender 5.x le plus récent installé) [string]$Blender = $env:BLENDER_EXE, # Dossier de sortie [string]$OutDir = (Join-Path (Split-Path $PSScriptRoot -Parent) 'dist'), # Lance d'abord la suite de tests en arrière-plan (exige « RESULTAT : TOUT OK ») [switch]$RunTests ) $ErrorActionPreference = 'Stop' $root = Split-Path $PSScriptRoot -Parent function Find-Blender { $base = 'C:\Program Files\Blender Foundation' $exe = Get-ChildItem $base -Directory -Filter 'Blender *' -ErrorAction SilentlyContinue | Sort-Object { [version]($_.Name -replace '^Blender\s+', '') } -Descending | ForEach-Object { Join-Path $_.FullName 'blender.exe' } | Where-Object { Test-Path $_ } | Select-Object -First 1 if (-not $exe) { throw "Blender introuvable : passez -Blender ou définissez BLENDER_EXE" } return $exe } if (-not $Blender) { $Blender = Find-Blender } if (-not (Test-Path $Blender)) { throw "Blender introuvable : $Blender" } $manifest = Get-Content (Join-Path $root 'blender_manifest.toml') -Raw if ($manifest -notmatch '(?m)^\s*version\s*=\s*"([^"]+)"') { throw "version introuvable dans blender_manifest.toml" } $version = $Matches[1] if ($manifest -notmatch '(?m)^\s*id\s*=\s*"([^"]+)"') { throw "id introuvable dans blender_manifest.toml" } $id = $Matches[1] Write-Host "Slice Wrap $version — $Blender" -ForegroundColor Cyan if ($RunTests) { $testOut = Join-Path ([IO.Path]::GetTempPath()) 'slice_wrap_tests' New-Item -ItemType Directory -Force $testOut | Out-Null Write-Host "Tests (~1 min)…" $log = & $Blender -b --factory-startup -P (Join-Path $root 'tests\run_tests.py') -- $testOut 2>&1 $log | Where-Object { $_ -match '^(OK|FAIL|RESULTAT)' } | ForEach-Object { Write-Host $_ } if (-not ($log -match 'RESULTAT : TOUT OK')) { throw "Les tests ont échoué : paquet non construit" } } Write-Host "Validation du manifeste…" $out = & $Blender --command extension validate $root 2>&1 if ($LASTEXITCODE -ne 0) { $out | Write-Host; throw "Manifeste invalide" } New-Item -ItemType Directory -Force $OutDir | Out-Null $zip = Join-Path $OutDir "$id-$version.zip" if (Test-Path $zip) { Remove-Item $zip -Force } Write-Host "Construction…" $out = & $Blender --command extension build --source-dir $root --output-dir $OutDir 2>&1 if ($LASTEXITCODE -ne 0 -or -not (Test-Path $zip)) { $out | Write-Host; throw "Échec de la construction" } # Contrôle du contenu : rien de privé ni de développement dans le paquet Add-Type -AssemblyName System.IO.Compression.FileSystem $archive = [IO.Compression.ZipFile]::OpenRead($zip) try { $entries = $archive.Entries | ForEach-Object FullName } finally { $archive.Dispose() } $bad = $entries | Where-Object { $_ -match '^(tests|tools|dist|\.claude|\.git)/|^CLAUDE\.md$|__pycache__|\.zip$' } if ($bad) { throw "Fichiers indésirables dans le paquet : $($bad -join ', ')" } Write-Host "Paquet : $zip ($([math]::Round((Get-Item $zip).Length / 1KB)) Ko)" -ForegroundColor Green $entries | ForEach-Object { Write-Host " $_" } return $zip