Préparation pour la chaîne d'outils a2m

- 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 <noreply@anthropic.com>
This commit is contained in:
angeandClaude Opus 5.5 committed 2026-10-20 17:14:32 +02:00
1 parent ec3ed21b1e
commit bc01a01e09
8 files changed
+125 -295

No files matched your search

+1 -1
View File
@@ -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
+46
View File
@@ -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.
+46
View File
@@ -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.
+20 -33
View File
@@ -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 -- <dossier> [--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-<version>.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<version>`, 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-<version>.zip`, `a2m release [patch|minor|major]` (version du manifest, CHANGELOG,
commit, tag `v<version>`, 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=<dossier> --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.
+8 -8
View File
@@ -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-<version>.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-<version>.zip` ; `-RunTests` lance
d'abord les tests ; Blender trouvé automatiquement, ou `-Blender <chemin>` / 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<version>`, 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-<version>.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\<version>\extensions\user_default\slice_wrap`.
+4 -3
View File
@@ -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/",
]
-78
View File
@@ -1,78 +0,0 @@
<#
.SYNOPSIS
Construit le paquet d'extension Blender de Slice Wrap dans dist\.
.DESCRIPTION
Valide blender_manifest.toml, construit slice_wrap-<version>.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 <chemin\blender.exe> 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
-172
View File
@@ -1,172 +0,0 @@
<#
.SYNOPSIS
Publie une release de Slice Wrap sur Gitea : tag v<version>, 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<version> 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-<version>.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/<owner>/<repo>.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 « - <version> … » 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