# Regenerate index.json for THIS gallery repository (PLT\Gallery). # # 为什么自带一份: # 主仓库 build\regen-gallery-index.ps1 写死了 "<脚本父目录的父目录>\gallery", # 只能服务 TeamAAS-2.1\gallery,无法指向这里。 # # 格式:对齐远端 Gallery 仓库的 GalleryPackage schema(含多版本 versions[]), # 以保持内容为准:字段全部来自包内 manifest.json,不再手写,避免 index 与包不一致。 # 版本历史会继承旧 index 中同 id 的其它版本条目(releasedAt / status / notes 不丢)。 # # 用法(改了 packages\ 下任何 zip 之后执行): # powershell -NoProfile -ExecutionPolicy Bypass -File .\regen-index.ps1 # # 布局约定:packages\\\-.zip(zip 根目录必须含 manifest.json) $ErrorActionPreference = 'Stop' $galleryDir = $PSScriptRoot $packagesDir = Join-Path $galleryDir 'packages' $indexPath = Join-Path $galleryDir 'index.json' if (-not (Test-Path $packagesDir)) { throw "packages directory not found: $packagesDir" } Add-Type -AssemblyName System.IO.Compression.FileSystem # 旧 index:仅用于继承版本历史 $history = @{} if (Test-Path $indexPath) { try { $old = (Get-Content $indexPath -Raw -Encoding UTF8) | ConvertFrom-Json foreach ($p in $old.packages) { foreach ($v in $p.versions) { $history["$($p.id)@$($v.version)"] = @{ releasedAt = $v.releasedAt status = if ($v.status) { $v.status } else { 'stable' } notes = $v.notes } } } } catch { Write-Warning "旧 index.json 解析失败,版本历史不继承:$($_.Exception.Message)" } } $today = (Get-Date).ToString('yyyy-MM-dd') $sha = [System.Security.Cryptography.SHA256]::Create() $entries = New-Object System.Collections.Generic.List[object] $seen = @{} foreach ($zipFile in (Get-ChildItem -Path $packagesDir -Recurse -Filter '*.zip' | Sort-Object FullName)) { $zip = [System.IO.Compression.ZipFile]::OpenRead($zipFile.FullName) try { $manifestEntry = $zip.Entries | Where-Object { $_.FullName -replace '\\', '/' -eq 'manifest.json' } | Select-Object -First 1 if (-not $manifestEntry) { Write-Warning "skip (no manifest.json at root): $($zipFile.Name)" continue } $reader = New-Object System.IO.StreamReader($manifestEntry.Open(), [System.Text.Encoding]::UTF8) try { $manifest = $reader.ReadToEnd() } finally { $reader.Dispose() } $j = $manifest | ConvertFrom-Json if (-not $j.id -or -not $j.version) { Write-Warning "skip (manifest missing id/version): $($zipFile.Name)" continue } $key = "$($j.id)@$($j.version)" if ($seen.ContainsKey($key)) { Write-Warning "skip duplicate: $key ($($zipFile.Name))" continue } $seen[$key] = $true $fs = [System.IO.File]::OpenRead($zipFile.FullName) try { $hashBytes = $sha.ComputeHash($fs) $size = $fs.Length } finally { $fs.Dispose() } $hashHex = ([System.BitConverter]::ToString($hashBytes) -replace '-', '').ToLowerInvariant() # downloadUrl 存相对路径:GalleryClient 会按实际源根拼接,源换域名也不用改包。 $relativeUrl = $zipFile.FullName.Substring($galleryDir.Length + 1) -replace '\\', '/' $hostName = if ($j.host) { [string]$j.host } else { 'platform' } $kind = if ($j.kind) { [string]$j.kind } else { 'extension' } $requires = $null if ($j.requiresHost) { $requires = if ($j.requiresHost.minVersion) { "$($j.requiresHost.id)>=$($j.requiresHost.minVersion)" } else { [string]$j.requiresHost.id } } $prev = $history[$key] $versionEntry = [ordered]@{ version = [string]$j.version downloadUrl = $relativeUrl sha256 = $hashHex size = $size releasedAt = if ($prev) { $prev.releasedAt } else { $today } status = if ($prev) { $prev.status } else { 'stable' } # 本版特性优先取被发布物料自己的 manifest.notes(每个版本各自的说明), # 其次继承旧 index 里的同版本记录,最后才是兜底文案。 notes = if ($j.notes) { [string]$j.notes } elseif ($prev -and $prev.notes) { $prev.notes } else { '首次收录' } } $entry = [ordered]@{ id = [string]$j.id name = [string]$j.name version = [string]$j.version publisher = if ($j.publisher) { [string]$j.publisher } else { $null } kind = $kind host = $hostName description = if ($j.description) { [string]$j.description } else { $null } notes = if ($j.notes) { [string]$j.notes } else { $null } downloadUrl = $relativeUrl sha256 = $hashHex size = $size references = @($j.references) targetFramework = if ($j.targetFramework) { [string]$j.targetFramework } else { $null } requiresHost = $requires arch = @($j.arch) dependencies = @($j.dependencies) versions = @($versionEntry) } $entries.Add([PSCustomObject]$entry) Write-Host ("indexed {0} {1} [{2}] {3} bytes {4}" -f $j.id, $j.version, $hostName, $size, $hashHex) } finally { $zip.Dispose() } } $sha.Dispose() # --------------------------------------------------------------------------- # 合并同 id 的多条为单一条目: # index.json 的契约是 packages[] 每个 id 只出现一次,多版本装在 versions[] 里 # (SDK 侧 GalleryPackage.AllVersions 就按这个读)。 # 若同 id 各版本各占一条,平台按 id 取一条展示 —— 表现为"版本下拉只有最新一版", # 旧版本永远选不到。这里按 id 归并,并把顶层字段对齐到最高版本。 # --------------------------------------------------------------------------- $byId = [ordered]@{} foreach ($e in $entries) { $id = [string]$e.id if ($byId.Contains($id)) { $byId[$id].versions = @($byId[$id].versions) + @($e.versions) if (-not $byId[$id].notes -and $e.notes) { $byId[$id].notes = $e.notes } } else { $byId[$id] = $e } } $merged = New-Object System.Collections.Generic.List[object] foreach ($id in $byId.Keys) { $p = $byId[$id] $versions = @($p.versions | Sort-Object -Property @{ Expression = { $v = $null if ([version]::TryParse([string]$_.version, [ref]$v)) { $v } else { [version]'0.0' } } } -Descending) if ($versions.Count -eq 0) { continue } $top = $versions[0] $p.versions = $versions $p.version = [string]$top.version $p.downloadUrl = $top.downloadUrl $p.sha256 = $top.sha256 $p.size = $top.size $p.notes = if ($p.notes) { $p.notes } else { $top.notes } $merged.Add([PSCustomObject]$p) } # Stable order: host (platform first), then id. $sorted = @($merged | Sort-Object @{ Expression = { $_.host } }, @{ Expression = { $_.id } }) $index = [ordered]@{ schemaVersion = 1 generatedAt = (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') packages = $sorted } $json = $index | ConvertTo-Json -Depth 12 # ConvertTo-Json 会把 ">" 转义成 \u003e(合法 JSON 但可读性差,且与线上文件不一致),还原。 $json = $json -replace '\\u003e', '>' -replace '\\u003c', '<' -replace '\\u0026', '&' [System.IO.File]::WriteAllText($indexPath, $json, (New-Object System.Text.UTF8Encoding($false))) Write-Host "REGEN DONE: $($sorted.Count) package(s) -> $indexPath"