From bc01a01e093c1af38404cb0b14dca96499939e55 Mon Sep 17 00:00:00 2001 From: Ange-Marie MAURIN Date: Tue, 20 Oct 2026 17:14:32 +0200 Subject: [PATCH] =?UTF-8?q?Pr=C3=A9paration=20pour=20la=20cha=C3=AEne=20d'?= =?UTF-8?q?outils=20a2m?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Manifest : Blender 5.1 minimum, maintainer Ange-Marie MAURIN, website (fiche a2msystemes.org), .a2m/ exclu du paquet - CHANGELOG.md (en) et CHANGELOG.fr.md (fr) : historique 1.0.0 → 1.4.0 repris du CLAUDE.md, mise au format a2m dans la section non publiée - tools/Build-Package.ps1 et tools/New-GiteaRelease.ps1 retirés : remplacés par a2m build et a2m release - README, CLAUDE.md, .gitignore : commandes a2m, historique dans le CHANGELOG, note « dépôt d'extensions » rattachée à APC Co-Authored-By: Claude Opus 5.5 --- .gitignore | 2 +- CHANGELOG.fr.md | 46 ++++++++++ CHANGELOG.md | 46 ++++++++++ CLAUDE.md | 53 +++++------- README.md | 16 ++-- blender_manifest.toml | 7 +- tools/Build-Package.ps1 | 78 ----------------- tools/New-GiteaRelease.ps1 | 172 ------------------------------------- 8 files changed, 125 insertions(+), 295 deletions(-) create mode 100644 CHANGELOG.fr.md create mode 100644 CHANGELOG.md delete mode 100644 tools/Build-Package.ps1 delete mode 100644 tools/New-GiteaRelease.ps1 diff --git a/.gitignore b/.gitignore index 01f1792..d867124 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,7 @@ __pycache__/ *.py[cod] -# Paquets d'extension (construits par tools/Build-Package.ps1) +# Paquets d'extension (construits par a2m build) dist/ *.zip diff --git a/CHANGELOG.fr.md b/CHANGELOG.fr.md new file mode 100644 index 0000000..08f5cf7 --- /dev/null +++ b/CHANGELOG.fr.md @@ -0,0 +1,46 @@ +# Journal des modifications + +Format : [Keep a Changelog](https://keepachangelog.com/fr/1.1.0/) ; versions : [SemVer](https://semver.org/lang/fr/). +La section « Non publié » est reprise par `a2m release` sous le numéro de la nouvelle version +(version anglaise : [CHANGELOG.md](CHANGELOG.md)). + +## [Non publié] + +### Modifié + +- Version minimale de Blender : 5.1. +- Publication par la chaîne d'outils a2m, pour le dépôt d'extensions d'a2msystemes.org (mises à jour proposées + directement dans Blender) ; fiche du projet : https://a2msystemes.org/projets/slice_wrap. + +## [1.4.0] - 2026-10-17 + +### Modifié + +- Réglage Target supprimé : l'objet sélectionné est utilisé. +- Confirmation avant les calculs longs, barre de progression (barre d'état et panneau) ; Blender n'est plus bloqué, + Échap annule sans rien laisser. + +## [1.3.0] + +### Ajouté + +- Suffix (`_slicewrapped`), numérotation automatique (plus aucun objet écrasé), collection optionnelle. + +## [1.2.0] + +### Ajouté + +- Analyze (carte de détail, tags, direction des plis), Show Detail / Shapes, Protect Detail. + +## [1.1.0] + +### Ajouté + +- Bouton unique « Slice Wrap » et Despike After ; Steps en sous-panneau. +- Despike : Distance, Strength, Iterations, filtres par groupes de sommets ; réparation des ratés au Freeze. + +## [1.0.0] + +### Ajouté + +- Envelope, Regenerate, Wrap Setup, Freeze (Reshape), Despike. diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..a1ec5f2 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,46 @@ +# Changelog + +Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions: [SemVer](https://semver.org/). +The "Unreleased" section is taken by `a2m release` under the number of the new version +(French version: [CHANGELOG.fr.md](CHANGELOG.fr.md)). + +## [Unreleased] + +### Changed + +- Minimum Blender version: 5.1. +- Published by the a2m toolchain, for the a2msystemes.org extension repository (updates offered directly in + Blender); project page: https://a2msystemes.org/en/projects/slice_wrap. + +## [1.4.0] - 2026-10-17 + +### Changed + +- Target setting removed: the selected object is used. +- Confirmation before long computations, progress bar (status bar and panel); Blender is no longer blocked, Esc + cancels without leaving anything behind. + +## [1.3.0] + +### Added + +- Suffix (`_slicewrapped`), automatic numbering (no object is ever overwritten), optional collection. + +## [1.2.0] + +### Added + +- Analyze (detail map, tags, fold direction), Show Detail / Shapes, Protect Detail. + +## [1.1.0] + +### Added + +- Single "Slice Wrap" button and Despike After; Steps as a sub-panel. +- Despike: Distance, Strength, Iterations, vertex group filters; missed rays repaired at Freeze. + +## [1.0.0] + +### Added + +- Envelope, Regenerate, Wrap Setup, Freeze (Reshape), Despike. diff --git a/CLAUDE.md b/CLAUDE.md index d7c6e62..e2707e5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -79,52 +79,39 @@ flow ici : la retopologie guidée est un projet séparé, **Slice Retopo** `voxelize`, `blocky_mesh`), nettoyage (`taubin`, `keep_largest_island`, `despike`, `group_weights`, `fix_missed_rays`), analyse (`analyze_shape`, `write_analysis`), `unique_object_name`, `build_envelope`. - `tests/run_tests.py` : suite (sphère trouée bruitée ; `--head` = tête réelle + rendus). Exclu du paquet. -- `blender_manifest.toml` : extension 4.2+ ; `[build]` exclut `__pycache__/`, zips, `.claude/`, `.git/`, `CLAUDE.md`, - `tests/`, `tools/`, `dist/`. -- `tools/Build-Package.ps1` (paquet), `tools/New-GiteaRelease.ps1` (release Gitea). Exclus du paquet. +- `blender_manifest.toml` : extension **5.1+** (décidé le 2026-10-20) ; `[build]` exclut `__pycache__/`, zips, + `.a2m/`, `.claude/`, `.git/`, `.gitignore`, `CLAUDE.md`, `tests/`, `dist/`. +- `.a2m/project.toml` : données du projet pour la chaîne d'outils a2m et le site (écrit par `a2m adopt`). +- `CHANGELOG.md` (en) / `CHANGELOG.fr.md` (fr) : historique ; la section « Unreleased » / « Non publié » donne les + notes de la prochaine release. ## Commandes - Tests : `"C:\Program Files\Blender Foundation\Blender 5.2\blender.exe" -b -P tests/run_tests.py -- [--head]` → `RESULTAT : TOUT OK` (~1 min, 29 contrôles dont annulations) ; avec `--head`, regarder `slice_wrap_head_face.png` / `_cote.png`. -- Paquet : `.\tools\Build-Package.ps1 [-RunTests]` → `dist\slice_wrap-.zip` (validate + build + contrôle - du contenu). **Monter `version`** à chaque livraison. L'utilisateur installe lui-même le zip. -- Release : `.\tools\New-GiteaRelease.ps1 [-DryRun]` — Gitea `https://a2msystemes-git.duckdns.org`, dépôt - `ange/slice_wrap_addon` (déduit de `origin`), jeton `GITEA_TOKEN`. Exige un dépôt propre et poussé ; crée le tag - `v`, la release (notes = entrée de l'Historique ci-dessous) et joint le zip. Publication = action - externe : ne la lancer qu'à la demande de l'utilisateur. -- `.gitignore` : `__pycache__/`, `dist/`, `*.zip`, rendus de test. Le manifeste exclut aussi `tools/`, `dist/`, `.git/`. + **À lancer avant chaque release** (la chaîne a2m ne lance pas encore les tests de l'addon). +- Construction et publication : chaîne d'outils **a2m** (repo `ange/apc-toolchain`, remplace les anciens + `tools/Build-Package.ps1` et `tools/New-GiteaRelease.ps1`, retirés le 2026-10-20) : + `a2m validate --load` (manifest validé et addon chargé dans Blender 5.1 et 5.2, profil temporaire), + `a2m build` → `dist\slice_wrap-.zip`, `a2m release [patch|minor|major]` (version du manifest, CHANGELOG, + commit, tag `v`, release Gitea avec le zip et `publication.json`), `a2m release --dev` (pré-release de + test, réservée aux membres). Publication = action externe : ne la lancer qu'à la demande de l'utilisateur. +- `.gitignore` : `__pycache__/`, `dist/`, `*.zip`, rendus de test. ## Historique -- 1.0.0 Envelope / Regenerate / Wrap Setup / Freeze (Reshape) / Despike. -- 1.1.0 Bouton unique « Slice Wrap » + Despike After ; Steps en sous-panneau ; Despike : Distance, Strength, - Iterations, filtres par groupes de sommets ; réparation des ratés au Freeze. -- 1.2.0 Analyze (carte de détail, tags, direction des plis), Show Detail / Shapes, Protect Detail. -- 1.3.0 Suffix (`_slicewrapped`), numérotation automatique (plus d'écrasement), collection optionnelle. -- 1.4.0 Réglage Target supprimé (objet sélectionné uniquement) ; confirmation avant les calculs longs, barre de - progression (barre d'état + panneau), Blender non bloqué, Échap annule sans rien laisser. +Voir [CHANGELOG.fr.md](CHANGELOG.fr.md) (une entrée par version ; les changements à venir dans « Non publié »). ## Mesures de référence (Blender 5.2.1, tête Pixal3D brute 11,8 M faces) Enveloppe 70 k faces en 11 s ; chaîne complète ~1,5–2 min ; ~93 k ratés réparés au Freeze ; Despike filtré « Oreilles » 683 corrections vs 4 749 global ; Analyze 9 s (Flat 86 %, Ridge 5 %, Bulge 3 %, Fold 3 %, Hollow 2 %, Saddle 1 % — hélice des oreilles et lèvres en Ridge, paupières et commissures en Fold). -## À faire : dépôt d'extensions Blender sur a2msystemes.org (prévu, 2026-10-17) +## Dépôt d'extensions Blender sur a2msystemes.org (idée du 2026-10-17, prise en charge par APC) But : les addons de l'utilisateur (Slice Wrap, Slice Retopo, suivants) installables et mis à jour via -*Preferences > Get Extensions > Repositories > Add Remote Repository* (`https://a2msystemes.org/blender/index.json`, -dossier à confirmer), au lieu d'*Install from Disk*. -- **Prérequis en cours (autre projet)** : mise en ligne du site par git. Site réel `https://a2msystemes.org` - (une page « en construction »), code `https://a2msystemes-git.duckdns.org/ange/site-a2msystemes-org`. Reprendre ici - une fois ce déploiement en place. -- **Principe** : dossier statique HTTPS = `index.json` + zips (+ `index.html`), généré par - `blender --command extension server-generate --repo-dir= --html`. Une seule version par addon - (id unique) ; Blender détecte les mises à jour au démarrage (« Check for Updates on Startup ») d'après `version` - du manifeste, l'installation reste un clic (*Update*). -- **À écrire** : `tools/Publish-ExtensionRepo.ps1` (ou étape de `New-GiteaRelease.ps1`) : Build-Package → copier - le zip dans le dossier `blender/` du dépôt du site (retirer l'ancienne version du même id) → `server-generate` → - commit + push du dépôt du site (déploiement git). -- **Questions encore ouvertes** : dossier exact sur le site ; dépôt public (probable, GPL) ou privé (jeton - *Requires Access Token* + contrôle côté serveur) ; liste des addons (Slice Retopo doit avoir un - `blender_manifest.toml`). +*Preferences > Get Extensions > Repositories > Add Remote Repository*, au lieu d'*Install from Disk*. +Cette note est à l'origine du projet **APC** (*A2M Publication Contract*) : le dépôt n'est plus à écrire ici. +- **Publication** : `a2m release` (chaîne d'outils `ange/apc-toolchain`) joint à chaque release Gitea le zip et un + `publication.json` (contrat `ange/apc-spec`) ; le script de déploiement du site (`ange/site-a2msystemes-org`) + en fabrique `blender-addons/index.json` et les pages du projet. Adresse du dépôt et pages : voir le site. - **À rappeler à l'utilisateur** : les addons déjà installés par *Install from Disk* (dépôt `user_default`) ne reçoivent pas les mises à jour → les désinstaller puis réinstaller une fois depuis le nouveau dépôt. diff --git a/README.md b/README.md index aba182c..20fac0c 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Slice Wrap — add-on Blender (4.2 → 5.x) +# Slice Wrap — add-on Blender (5.1 → 5.x) Transforme un maillage **troué, à doubles parois ou bruité** (scan, génération IA type Pixal3D / Trellis) en une **surface propre, fermée et détaillée**, prête à sculpter. Aucune dépendance : numpy seulement @@ -105,14 +105,14 @@ Les couleurs portent le suffixe `_Color` car groupes de sommets et attributs par - Après Freeze, l'objet garde une **base légère** + niveaux Multires : sculptez, ou bakez les détails. ## Installation -- **Paquet** : `slice_wrap-1.4.0.zip` (page *Releases* du dépôt Gitea, ou `dist\` après construction) → +- **Depuis le dépôt d'extensions d'a2msystemes.org** (à venir) : les mises à jour seront proposées directement dans + Blender. Voir https://a2msystemes.org/projets/slice_wrap. +- **Paquet** : `slice_wrap-.zip` (page *Releases* du dépôt Gitea, ou `dist\` après construction) → *Edit > Preferences > Get Extensions > ⌄ > Install from Disk…* -- **Construire le paquet** : `.\tools\Build-Package.ps1` (→ `dist\slice_wrap-.zip` ; `-RunTests` lance - d'abord les tests ; Blender trouvé automatiquement, ou `-Blender ` / variable `BLENDER_EXE`). -- **Publier une release Gitea** : monter `version` dans `blender_manifest.toml`, commiter et pousser, puis - `.\tools\New-GiteaRelease.ps1` (tests, paquet, tag `v`, release avec le zip en pièce jointe). - Jeton : variable `GITEA_TOKEN` (sinon demandé). `-DryRun` montre ce qui serait fait sans rien publier ; - `-Prerelease`, `-Draft`, `-Notes "…"` (par défaut : l'entrée de la version dans l'historique). +- **Construire et publier** : chaîne d'outils `a2m` (repo `ange/apc-toolchain`) — `a2m validate --load`, + `a2m build` (→ `dist\slice_wrap-.zip`), `a2m release [patch|minor|major]` (version, CHANGELOG, tag, + release Gitea avec le zip et `publication.json`), `a2m release --dev` (pré-release de test). + Historique des versions : [CHANGELOG.fr.md](CHANGELOG.fr.md). - **Développement** : copier / lier ce dossier dans `%APPDATA%\Blender Foundation\Blender\\extensions\user_default\slice_wrap`. diff --git a/blender_manifest.toml b/blender_manifest.toml index 5093b74..6b9fd81 100644 --- a/blender_manifest.toml +++ b/blender_manifest.toml @@ -4,12 +4,13 @@ id = "slice_wrap" version = "1.4.0" name = "Slice Wrap" tagline = "Closed envelope of holey meshes, then wrap, freeze and despike" -maintainer = "a-mma" +maintainer = "Ange-Marie MAURIN" type = "add-on" +website = "https://a2msystemes.org/projets/slice_wrap" tags = ["Mesh", "Modeling", "Sculpt"] -blender_version_min = "4.2.0" +blender_version_min = "5.1.0" license = [ "SPDX:GPL-3.0-or-later", @@ -19,11 +20,11 @@ license = [ paths_exclude_pattern = [ "__pycache__/", "/*.zip", + "/.a2m/", "/.claude/", "/.git/", "/.gitignore", "/CLAUDE.md", "/tests/", - "/tools/", "/dist/", ] diff --git a/tools/Build-Package.ps1 b/tools/Build-Package.ps1 deleted file mode 100644 index b99f9e6..0000000 --- a/tools/Build-Package.ps1 +++ /dev/null @@ -1,78 +0,0 @@ -<# -.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 diff --git a/tools/New-GiteaRelease.ps1 b/tools/New-GiteaRelease.ps1 deleted file mode 100644 index 94c7874..0000000 --- a/tools/New-GiteaRelease.ps1 +++ /dev/null @@ -1,172 +0,0 @@ -<# -.SYNOPSIS - Publie une release de Slice Wrap sur Gitea : tag v, release et paquet zip en pièce jointe. - -.DESCRIPTION - 1. Lit la version dans blender_manifest.toml. - 2. Vérifie que le dépôt est propre (tout est commité) et que HEAD est poussé sur origin. - 3. Construit le paquet (tools\Build-Package.ps1, tests compris sauf -SkipTests). - 4. Crée le tag annoté v sur HEAD et le pousse (s'il existe déjà, il doit pointer sur HEAD). - 5. Crée la release via l'API Gitea (ou réutilise celle du tag) et y joint slice_wrap-.zip - (une pièce jointe du même nom est remplacée). - Notes de version : -Notes, sinon l'entrée de la version dans la section « Historique » de CLAUDE.md. - - Jeton (Gitea > Paramètres > Applications, portée « repository : lecture et écriture »), cherché dans l'ordre : - 1. variable d'environnement GITEA_TOKEN ; - 2. %APPDATA%\Gitea\token.xml, chiffré par Windows (DPAPI) : seul votre compte, sur ce PC, peut le lire. - Pour le créer : Read-Host "Jeton Gitea" -AsSecureString | Export-Clixml "$env:APPDATA\Gitea\token.xml" - 3. sinon il est demandé (saisie masquée), avec proposition de l'enregistrer dans ce fichier. - -.EXAMPLE - .\tools\New-GiteaRelease.ps1 -DryRun # montre ce qui serait fait, ne publie rien -.EXAMPLE - $env:GITEA_TOKEN = '...'; .\tools\New-GiteaRelease.ps1 -.EXAMPLE - .\tools\New-GiteaRelease.ps1 -Prerelease -Notes "Version de test" -#> -param( - [string]$Server = 'https://a2msystemes-git.duckdns.org', - # Propriétaire et dépôt : déduits de l'URL du remote « origin » si absents - [string]$Owner, - [string]$Repo, - [string]$Notes, - [switch]$Prerelease, - [switch]$Draft, - [switch]$SkipTests, - # Publier même si le dépôt contient des modifications non commitées - [switch]$AllowDirty, - [switch]$DryRun, - [string]$Blender = $env:BLENDER_EXE -) - -$ErrorActionPreference = 'Stop' -$root = Split-Path $PSScriptRoot -Parent - -function Invoke-Git { - $out = & git -C $root @args 2>&1 - if ($LASTEXITCODE -ne 0) { throw "git $($args -join ' ') : $out" } - return $out -} - -# ------------------------------------------------------------------ version, dépôt, notes -$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] -$tag = "v$version" - -if (-not $Owner -or -not $Repo) { - $url = (& git -C $root remote get-url origin 2>$null) - if (-not $url) { throw "Aucun remote « origin » : ajoutez-le (git remote add origin $Server//.git) ou passez -Owner / -Repo" } - if ($url -notmatch '[:/]([^/:]+)/([^/]+?)(\.git)?/?$') { throw "URL du remote non reconnue : $url" } - if (-not $Owner) { $Owner = $Matches[1] } - if (-not $Repo) { $Repo = $Matches[2] } -} - -if (-not $Notes) { - # Entrée « - … » de la section Historique de CLAUDE.md (lignes de continuation indentées comprises) - $claude = Join-Path $root 'CLAUDE.md' - if (Test-Path $claude) { - $lines = Get-Content $claude -Encoding utf8 - $i = [array]::FindIndex($lines, [Predicate[string]] { param($l) $l -match "^- $([regex]::Escape($version))\b" }) - if ($i -ge 0) { - $entry = @($lines[$i] -replace "^- $([regex]::Escape($version))\s*", '') - for ($j = $i + 1; $j -lt $lines.Count -and $lines[$j] -match '^\s{2,}\S'; $j++) { $entry += $lines[$j].Trim() } - $Notes = $entry -join ' ' - } - } - if (-not $Notes) { $Notes = "Slice Wrap $version" } -} -$body = @" -$Notes - -**Installation** : Blender 4.2+ > *Edit > Preferences > Get Extensions > ⌄ > Install from Disk…* > ``slice_wrap-$version.zip`` -"@ - -# ------------------------------------------------------------------ état du dépôt -& git -C $root rev-parse --verify --quiet HEAD *> $null -if ($LASTEXITCODE -ne 0) { throw "Le dépôt n'a encore aucun commit : commitez et poussez avant de publier une release." } -$dirty = Invoke-Git status --porcelain -if ($dirty -and -not $AllowDirty) { throw "Modifications non commitées :`n$($dirty -join "`n")`nCommitez-les (ou -AllowDirty)." } -$head = (Invoke-Git rev-parse HEAD).Trim() -$branch = (Invoke-Git rev-parse --abbrev-ref HEAD).Trim() -& git -C $root fetch origin --tags --quiet 2>$null -$remoteHead = (& git -C $root rev-parse "origin/$branch" 2>$null) -if ($remoteHead -ne $head -and -not $DryRun) { throw "HEAD ($($head.Substring(0,8))) n'est pas poussé sur origin/$branch : faites git push d'abord." } -$tagCommit = (& git -C $root rev-list -n 1 $tag 2>$null) -if ($tagCommit -and $tagCommit.Trim() -ne $head) { throw "Le tag $tag existe déjà sur un autre commit ($($tagCommit.Substring(0,8))) : montez la version dans blender_manifest.toml." } - -Write-Host "Release $tag de $Owner/$Repo sur $Server (commit $($head.Substring(0,8)), branche $branch)" -ForegroundColor Cyan -Write-Host "Notes : $Notes" - -# ------------------------------------------------------------------ paquet -$buildArgs = @{} -if ($Blender) { $buildArgs.Blender = $Blender } -if (-not $SkipTests) { $buildArgs.RunTests = $true } -$zip = & (Join-Path $PSScriptRoot 'Build-Package.ps1') @buildArgs | Select-Object -Last 1 -$zipName = Split-Path $zip -Leaf - -if ($DryRun) { - Write-Host "`n[DryRun] Rien n'est publié. Seraient faits :" -ForegroundColor Yellow - if (-not $tagCommit) { Write-Host " git tag -a $tag $($head.Substring(0,8)) ; git push origin $tag" } - Write-Host " POST $Server/api/v1/repos/$Owner/$Repo/releases (tag $tag$(if ($Prerelease) {', préversion'})$(if ($Draft) {', brouillon'}))" - Write-Host " pièce jointe $zipName" - return -} - -# ------------------------------------------------------------------ jeton -# Ordre : variable GITEA_TOKEN, puis fichier chiffré par Windows (DPAPI), sinon saisie masquée -$tokenFile = Join-Path $env:APPDATA 'Gitea\token.xml' -$token = $env:GITEA_TOKEN -if (-not $token -and (Test-Path $tokenFile)) { - $token = [Net.NetworkCredential]::new('', (Import-Clixml $tokenFile)).Password -} -if (-not $token) { - $sec = Read-Host "Jeton Gitea (portée repository, lecture/écriture)" -AsSecureString - $token = [Net.NetworkCredential]::new('', $sec).Password - if ($token -and (Read-Host "L'enregistrer chiffré dans $tokenFile ? (o/N)") -match '^[oOyY]') { - New-Item -ItemType Directory -Force (Split-Path $tokenFile) | Out-Null - $sec | Export-Clixml $tokenFile - Write-Host "Jeton enregistré (lisible seulement par votre compte Windows sur ce PC)" - } -} -if (-not $token) { throw "Jeton Gitea manquant" } -$headers = @{ Authorization = "token $token"; Accept = 'application/json' } -$api = "$Server/api/v1/repos/$Owner/$Repo" - -# ------------------------------------------------------------------ tag -if (-not $tagCommit) { - Invoke-Git tag -a $tag -m "Slice Wrap $version" | Out-Null - Invoke-Git push origin $tag | Out-Null - Write-Host "Tag $tag créé et poussé" -} - -# ------------------------------------------------------------------ release -$release = $null -try { - $release = Invoke-RestMethod -Method Get -Uri "$api/releases/tags/$tag" -Headers $headers - Write-Host "Release $tag existante (id $($release.id)) : réutilisée" -} catch { - if ($_.Exception.Response.StatusCode.value__ -ne 404) { throw } -} -if (-not $release) { - $payload = @{ - tag_name = $tag - name = "Slice Wrap $version" - body = $body - draft = [bool]$Draft - prerelease = [bool]$Prerelease - } | ConvertTo-Json - $release = Invoke-RestMethod -Method Post -Uri "$api/releases" -Headers $headers ` - -ContentType 'application/json; charset=utf-8' -Body ([Text.Encoding]::UTF8.GetBytes($payload)) - Write-Host "Release créée (id $($release.id))" -} - -# ------------------------------------------------------------------ pièce jointe (remplacée si elle existe) -foreach ($a in @($release.assets) | Where-Object { $_ -and $_.name -eq $zipName }) { - Invoke-RestMethod -Method Delete -Uri "$api/releases/$($release.id)/assets/$($a.id)" -Headers $headers | Out-Null - Write-Host "Ancienne pièce jointe $zipName supprimée" -} -Invoke-RestMethod -Method Post -Uri "$api/releases/$($release.id)/assets?name=$zipName" -Headers $headers ` - -Form @{ attachment = Get-Item $zip } | Out-Null - -Write-Host "Publié : $($release.html_url)" -ForegroundColor Green