MegaBites-AI/Windows-powershell
0372
1# Copyright (c) Microsoft Corporation.2# Licensed under the MIT License.3 4# This script is used to completely rebuild the cgmanifgest.json file,5# which is used to generate the notice file.6# Requires the module dotnet.project.assets from the PowerShell Gallery authored by @TravisEz137 8param(9 [switch] $Fix,10 [switch] $IsStable,11 [switch] $ForceHarvestedOnly12)13 14Import-Module dotnet.project.assets15Import-Module "$PSScriptRoot\..\.github\workflows\GHWorkflowHelper" -Force16. "$PSScriptRoot\..\tools\buildCommon\startNativeExecution.ps1"17. "$PSScriptRoot\clearlyDefined\Find-LastHarvestedVersion.ps1"18 19$targetsConfigPath = Join-Path -Path $PSScriptRoot -ChildPath 'findMissingNotices.targets.json'20if (-not (Test-Path -LiteralPath $targetsConfigPath)) {21 throw "Missing target framework config file '$targetsConfigPath'. Add '/tools/findMissingNotices.targets.json' with 'dotnetTargetName' and 'windowsTargetNames' entries."22}23 24try {25 $targetsConfig = Get-Content -LiteralPath $targetsConfigPath -Raw -ErrorAction Stop | ConvertFrom-Json -AsHashtable -ErrorAction Stop26} catch {27 throw "Failed to load target framework config from '$targetsConfigPath'. Ensure the file contains valid JSON. Error: $($_.Exception.Message)"28}29 30if ($targetsConfig -isnot [hashtable]) {31 throw "Invalid target framework config '$targetsConfigPath': expected a JSON object with 'dotnetTargetName' and 'windowsTargetNames'."32}33 34if (-not $targetsConfig.ContainsKey('dotnetTargetName') -or [string]::IsNullOrWhiteSpace($targetsConfig['dotnetTargetName'])) {35 throw "Invalid target framework config '$targetsConfigPath': 'dotnetTargetName' must be a non-empty string."36}37 38if (-not $targetsConfig.ContainsKey('windowsTargetNames')) {39 throw "Invalid target framework config '$targetsConfigPath': 'windowsTargetNames' must be present and must be an array."40}41 42if ($null -eq $targetsConfig['windowsTargetNames'] -or $targetsConfig['windowsTargetNames'] -isnot [array]) {43 throw "Invalid target framework config '$targetsConfigPath': 'windowsTargetNames' must be an array (empty array is allowed)."44}45 46$script:dotnetTargetName = [string]$targetsConfig['dotnetTargetName']47$script:windowsTargetNames = @()48foreach ($windowsTargetName in $targetsConfig['windowsTargetNames']) {49 if ($windowsTargetName -isnot [string] -or [string]::IsNullOrWhiteSpace($windowsTargetName)) {50 throw "Invalid target framework config '$targetsConfigPath': every entry in 'windowsTargetNames' must be a non-empty string."51 }52 53 $script:windowsTargetNames += $windowsTargetName54}55 56# Empty windowsTargetNames is valid and means "use base target fallback only".57 58$packageSourceName = 'findMissingNoticesNugetOrg'59if (!(Get-PackageSource -Name $packageSourceName -ErrorAction SilentlyContinue)) {60 $null = Register-PackageSource -Name $packageSourceName -Location https://www.nuget.org/api/v2 -ProviderName NuGet61}62 63$existingRegistrationTable = @{}64$cgManifestPath = (Resolve-Path -Path $PSScriptRoot\cgmanifest\main\cgmanifest.json).ProviderPath65$existingRegistrationsJson = Get-Content $cgManifestPath | ConvertFrom-Json -AsHashtable66$existingRegistrationsJson.Registrations | ForEach-Object {67 $registration = [Registration]$_68 if ($registration.Component) {69 $name = $registration.Component.Name()70 if (!$existingRegistrationTable.ContainsKey($name)) {71 $existingRegistrationTable.Add($name, $registration)72 }73 }74}75 76Class Registration {77 [Component]$Component78 [bool]$DevelopmentDependency79}80 81Class Component {82 [ValidateSet("nuget")]83 [String] $Type84 [Nuget]$Nuget85 86 [string]ToString() {87 $message = "Type: $($this.Type)"88 if ($this.Type -eq "nuget") {89 $message += "; $($this.Nuget)"90 }91 return $message92 }93 94 [string]Name() {95 switch ($this.Type) {96 "nuget" {97 return $($this.Nuget.Name)98 }99 default {100 throw "Unknown component type: $($this.Type)"101 }102 }103 throw "How did we get here?!?"104 }105 106 [string]Version() {107 switch ($this.Type) {108 "nuget" {109 return $($this.Nuget.Version)110 }111 default {112 throw "Unknown component type: $($this.Type)"113 }114 }115 throw "How did we get here?!?"116 }117}118 119Class Nuget {120 [string]$Name121 [string]$Version122 123 [string]ToString() {124 return "$($this.Name) - $($this.Version)"125 }126}127 128$winDesktopSdk = 'Microsoft.NET.Sdk.WindowsDesktop'129if (!$IsWindows) {130 $winDesktopSdk = 'Microsoft.NET.Sdk'131 Write-Warning "Always using $winDesktopSdk since this is not windows!!!"132}133 134function ConvertTo-SemVer {135 param(136 [String] $Version137 )138 139 [System.Management.Automation.SemanticVersion]$desiredVersion = [System.Management.Automation.SemanticVersion]::Empty140 141 try {142 $desiredVersion = $Version143 } catch {144 <#145 Json.More.Net broke the rules and published 2.0.1.2 as 2.0.1.146 So, I'm making the logic work for that scenario by147 thorwing away any part that doesn't match non-pre-release semver portion148 #>149 $null = $Version -match '^(\d+\.\d+\.\d+).*'150 $desiredVersion = $matches[1]151 }152 153 return $desiredVersion154}155 156function New-NugetComponent {157 param(158 [string]$name,159 [string]$version,160 [switch]$DevelopmentDependency161 )162 163 $nuget = [Nuget]@{164 Name = $name165 Version = $version166 }167 $Component = [Component]@{168 Type = "nuget"169 Nuget = $nuget170 }171 172 $registration = [Registration]@{173 Component = $Component174 DevelopmentDependency = $DevelopmentDependency175 }176 177 return $registration178}179 180$nugetPublicVersionCache = [System.Collections.Generic.Dictionary[string, string]]::new()181function Get-NuGetPublicVersion {182 param(183 [parameter(Mandatory)]184 [string]$Name,185 [parameter(Mandatory)]186 [string]$Version187 )188 189 if($nugetPublicVersionCache.ContainsKey($Name)) {190 return $nugetPublicVersionCache[$Name]191 }192 193 [System.Management.Automation.SemanticVersion]$desiredVersion = ConvertTo-SemVer -Version $Version194 195 $publicVersion = $null196 $publicVersion = Find-Package -Name $Name -AllowPrereleaseVersions -source $packageSourceName -AllVersions -ErrorAction SilentlyContinue | ForEach-Object {197 [System.Management.Automation.SemanticVersion]$packageVersion = ConvertTo-SemVer -Version $_.Version198 $_ | Add-Member -Name SemVer -MemberType NoteProperty -Value $packageVersion -PassThru199 } | Where-Object {200 $_.SemVer -le $desiredVersion201 } | Sort-Object -Property semver -Descending | Select-Object -First 1 -ExpandProperty Version202 203 if(!$publicVersion) {204 Write-Warning "No public version found for $Name, using $Version"205 $publicVersion = $Version206 }207 208 if(!$nugetPublicVersionCache.ContainsKey($Name)) {209 $nugetPublicVersionCache.Add($Name, $publicVersion)210 }211 212 return $publicVersion213}214 215function Get-CGRegistrations {216 param(217 [Parameter(Mandatory)]218 [ValidateSet(219 "linux-musl-x64",220 "linux-arm",221 "linux-arm64",222 "linux-x64",223 "osx-arm64",224 "osx-x64",225 "win-arm64",226 "win-x64",227 "win-x86",228 "modules")]229 [string]$Runtime,230 231 [Parameter(Mandatory)]232 [System.Collections.Generic.Dictionary[string, Registration]] $RegistrationTable233 )234 235 $registrationChanged = $false236 237 $baseTargetName = $script:dotnetTargetName238 $unixProjectName = 'powershell-unix'239 $windowsProjectName = 'powershell-win-core'240 $actualRuntime = $Runtime241 242 switch -regex ($Runtime) {243 "alpine-.*" {244 $folder = $unixProjectName245 $target = "$baseTargetName|$Runtime"246 $neutralTarget = "$baseTargetName"247 }248 "linux-.*" {249 $folder = $unixProjectName250 $target = "$baseTargetName|$Runtime"251 $neutralTarget = "$baseTargetName"252 }253 "osx-.*" {254 $folder = $unixProjectName255 $target = "$baseTargetName|$Runtime"256 $neutralTarget = "$baseTargetName"257 }258 "win-.*" {259 $sdkToUse = $winDesktopSdk260 $folder = $windowsProjectName261 $target = "$baseTargetName|$actualRuntime"262 $neutralTarget = "$baseTargetName"263 }264 "modules" {265 $folder = "modules"266 $actualRuntime = 'linux-x64'267 $target = "$baseTargetName|$actualRuntime"268 $neutralTarget = "$baseTargetName"269 }270 Default {271 throw "Invalid runtime name: $Runtime"272 }273 }274 275 Write-Verbose "Getting registrations for $folder - $actualRuntime ..." -Verbose276 Get-PSDrive -Name $folder -ErrorAction Ignore | Remove-PSDrive277 Push-Location $PSScriptRoot\..\src\$folder278 try {279 Start-NativeExecution -VerboseOutputOnError -sb {280 dotnet restore --runtime $actualRuntime "/property:SDKToUse=$sdkToUse"281 }282 $null = New-PADrive -Path $PSScriptRoot\..\src\$folder\obj\project.assets.json -Name $folder283 284 if ($Runtime -like "win-*") {285 # Windows target selection is optional and ordered:286 # 1. Try full Windows TFMs from config in order.287 # 2. Fall back to the base non-Windows TFM if present.288 try {289 $availableTargets = @(Get-ChildItem -Path "${folder}:/targets" -ErrorAction Stop | Select-Object -ExpandProperty Name)290 } catch {291 throw "Unable to enumerate available targets for runtime '$Runtime' in '$folder'. Ensure dotnet restore succeeded and project.assets.json contains target data. Error: $($_.Exception.Message)"292 }293 294 $selectedTargetName = $null295 296 foreach ($windowsTargetName in $script:windowsTargetNames) {297 if ($windowsTargetName -in $availableTargets) {298 $selectedTargetName = $windowsTargetName299 break300 }301 }302 303 if (-not $selectedTargetName -and $baseTargetName -in $availableTargets) {304 Write-Verbose "No configured windows target matched for '$Runtime'. Falling back to base target '$baseTargetName'." -Verbose305 $selectedTargetName = $baseTargetName306 }307 308 if (-not $selectedTargetName) {309 Write-Verbose "Available targets for '$folder': $($availableTargets -join ', ')" -Verbose310 if ($script:windowsTargetNames.Count -eq 0) {311 throw "Unable to find a target for '$Runtime'. Tried fallback base target '$baseTargetName' (no windowsTargetNames configured). Ensure project.assets.json contains this target or update dotnetTargetName in '$targetsConfigPath'."312 }313 314 throw "Unable to find a target for '$Runtime'. Tried configured windowsTargetNames '$($script:windowsTargetNames -join "', '")' and fallback base target '$baseTargetName'. Update '$targetsConfigPath' with a valid windows target from the available list."315 }316 317 $target = "$selectedTargetName|$actualRuntime"318 $neutralTarget = $selectedTargetName319 }320 321 # Defensive check: non-Windows paths set targets in the switch block,322 # Windows path may override them after inspecting available assets targets.323 if (-not $target -or -not $neutralTarget) {324 throw "Unable to determine restore targets for runtime '$Runtime'."325 }326 327 try {328 $targets = Get-ChildItem -Path "${folder}:/targets/$target" -ErrorAction Stop | Where-Object { $_.Type -eq 'package' } | select-object -ExpandProperty name329 $targets += Get-ChildItem -Path "${folder}:/targets/$neutralTarget" -ErrorAction Stop | Where-Object { $_.Type -eq 'project' } | select-object -ExpandProperty name330 } catch {331 Get-ChildItem -Path "${folder}:/targets" | Out-String | Write-Verbose -Verbose332 throw333 }334 } finally {335 Pop-Location336 Get-PSDrive -Name $folder -ErrorAction Ignore | Remove-PSDrive337 }338 339 # Name to skip for TPN generation340 $skipNames = @(341 "Microsoft.PowerShell.Native"342 "Microsoft.Management.Infrastructure.Runtime.Unix"343 "Microsoft.Management.Infrastructure"344 "Microsoft.PowerShell.Commands.Diagnostics"345 "Microsoft.PowerShell.Commands.Management"346 "Microsoft.PowerShell.Commands.Utility"347 "Microsoft.PowerShell.ConsoleHost"348 "Microsoft.PowerShell.SDK"349 "Microsoft.PowerShell.Security"350 "Microsoft.Management.Infrastructure.CimCmdlets"351 "Microsoft.WSMan.Management"352 "Microsoft.WSMan.Runtime"353 "System.Management.Automation"354 "Microsoft.PowerShell.GraphicalHost"355 "Microsoft.PowerShell.CoreCLR.Eventing"356 )357 358 Write-Verbose "Found $($targets.Count) targets to process..." -Verbose359 $targets | ForEach-Object {360 $target = $_361 $parts = ($target -split '\|')362 $name = $parts[0]363 364 if ($name -in $skipNames) {365 Write-Verbose "Skipping $name..."366 367 } else {368 $targetVersion = $parts[1]369 $publicVersion = Get-NuGetPublicVersion -Name $name -Version $targetVersion370 371 # Add the registration to the cgmanifest if the TPN does not contain the name of the target OR372 # the exisitng CG contains the registration, because if the existing CG contains the registration,373 # that might be the only reason it is in the TPN.374 if (!$RegistrationTable.ContainsKey($target)) {375 $DevelopmentDependency = $false376 if (!$existingRegistrationTable.ContainsKey($name) -or $existingRegistrationTable.$name.Component.Version() -ne $publicVersion) {377 $registrationChanged = $true378 }379 if ($existingRegistrationTable.ContainsKey($name) -and $existingRegistrationTable.$name.DevelopmentDependency) {380 $DevelopmentDependency = $true381 }382 383 $registration = New-NugetComponent -Name $name -Version $publicVersion -DevelopmentDependency:$DevelopmentDependency384 $RegistrationTable.Add($target, $registration)385 }386 }387 }388 389 return $registrationChanged390}391 392$registrations = [System.Collections.Generic.Dictionary[string, Registration]]::new()393$lastCount = 0394$registrationChanged = $false395foreach ($runtime in "win-x64", "linux-x64", "osx-x64", "linux-musl-x64", "linux-arm", "linux-arm64", "osx-arm64", "win-arm64", "win-x86") {396 $registrationChanged = (Get-CGRegistrations -Runtime $runtime -RegistrationTable $registrations) -or $registrationChanged397 $count = $registrations.Count398 $newCount = $count - $lastCount399 $lastCount = $count400 Write-Verbose "$newCount new registrations, $count total..." -Verbose401}402 403$newRegistrations = $registrations.Keys | Sort-Object | ForEach-Object { $registrations[$_] }404 405if ($IsStable) {406 foreach ($registion in $newRegistrations) {407 $name = $registion.Component.Name()408 $version = $registion.Component.Version()409 $developmentDependency = $registion.DevelopmentDependency410 if ($version -match '-' -and !$developmentDependency) {411 throw "Version $version of $name is preview. This is not allowed."412 }413 }414}415 416$count = $newRegistrations.Count417$registrationsToSave = $newRegistrations418$tpnRegistrationsToSave = $null419 420# If -ForceHarvestedOnly is specified with -Fix, only include harvested packages421# and revert non-harvested packages to their previous versions422if ($Fix -and $ForceHarvestedOnly) {423 Write-Verbose "Checking harvest status and filtering to harvested packages with reversion..." -Verbose424 425 # Import ClearlyDefined module to check harvest status426 Import-Module -Name "$PSScriptRoot/clearlyDefined/src/ClearlyDefined" -Force427 428 # Import cache from previous runs to speed up lookups429 Import-ClearlyDefinedCache430 431 # Get harvest data for all registrations432 $fullCgList = $newRegistrations |433 ForEach-Object {434 [PSCustomObject]@{435 type = $_.Component.Type436 Name = $_.Component.Nuget.Name437 PackageVersion = $_.Component.Nuget.Version438 }439 }440 441 $fullList = $fullCgList | Get-ClearlyDefinedData442 443 # Build a lookup table of harvest status by package name + version444 $harvestStatus = @{}445 foreach ($item in $fullList) {446 $key = "$($item.Name)|$($item.PackageVersion)"447 $harvestStatus[$key] = $item.harvested448 }449 450 # Build a lookup table of old versions from existing manifest451 $oldVersions = @{}452 foreach ($registration in $existingRegistrationsJson.Registrations) {453 $name = $registration.Component.Nuget.Name454 if (!$oldVersions.ContainsKey($name)) {455 $oldVersions[$name] = $registration456 }457 }458 459 # Process each new registration: keep harvested, revert non-harvested460 $tpnRegistrationsToSave = @()461 $harvestedCount = 0462 $revertedCount = 0463 464 foreach ($reg in $newRegistrations) {465 $name = $reg.Component.Nuget.Name466 $version = $reg.Component.Nuget.Version467 $key = "$name|$version"468 469 if ($harvestStatus.ContainsKey($key) -and $harvestStatus[$key]) {470 # Package is harvested, include it471 $tpnRegistrationsToSave += $reg472 $harvestedCount++473 } else {474 # Package not harvested, find last harvested version475 $lastHarvestedVersion = Find-LastHarvestedVersion -Name $name -CurrentVersion $version476 477 # Use last harvested version if found, otherwise use old version as fallback478 if ($lastHarvestedVersion) {479 if ($lastHarvestedVersion -ne $version) {480 $revertedReg = New-NugetComponent -Name $name -Version $lastHarvestedVersion -DevelopmentDependency:$reg.DevelopmentDependency481 $tpnRegistrationsToSave += $revertedReg482 $revertedCount++483 Write-Verbose "Reverted $name from v$version to last harvested v$lastHarvestedVersion" -Verbose484 } else {485 $tpnRegistrationsToSave += $reg486 }487 } elseif ($oldVersions.ContainsKey($name)) {488 $tpnRegistrationsToSave += $oldVersions[$name]489 $revertedCount++490 Write-Verbose "Reverted $name to previous version (no harvested version found)" -Verbose491 } else {492 Write-Warning "$name v$version not harvested and no previous version found. Excluding from manifest."493 }494 }495 }496 497 Write-Verbose "Completed filtering for TPN: $harvestedCount harvested + $revertedCount reverted = $($tpnRegistrationsToSave.Count) total" -Verbose498}499 500$newJson = @{501 Registrations = $registrationsToSave502 '$schema' = "https://json.schemastore.org/component-detection-manifest.json"503} | ConvertTo-Json -depth 99504 505if ($Fix -and $registrationChanged) {506 $newJson | Set-Content $cgManifestPath507 Set-GWVariable -Name CGMANIFEST_PATH -Value $cgManifestPath508}509 510# If -ForceHarvestedOnly was used, write the TPN manifest with filtered registrations511if ($Fix -and $ForceHarvestedOnly -and $tpnRegistrationsToSave.Count -gt 0) {512 $tpnManifestDir = Join-Path -Path $PSScriptRoot -ChildPath "cgmanifest\tpn"513 New-Item -ItemType Directory -Path $tpnManifestDir -Force | Out-Null514 $tpnManifestPath = Join-Path -Path $tpnManifestDir -ChildPath "cgmanifest.json"515 516 $tpnManifest = @{517 Registrations = @($tpnRegistrationsToSave)518 '$schema' = "https://json.schemastore.org/component-detection-manifest.json"519 }520 521 $tpnJson = $tpnManifest | ConvertTo-Json -depth 99522 $tpnJson | Set-Content $tpnManifestPath -Encoding utf8NoBOM523 Write-Verbose "TPN manifest created/updated with $($tpnRegistrationsToSave.Count) registrations (filtered for harvested packages)" -Verbose524}525 526# Skip legacy TPN update when -ForceHarvestedOnly already produced a filtered manifest527if ($Fix -and $registrationChanged -and -not $ForceHarvestedOnly) {528 # Import ClearlyDefined module to check harvest status529 Write-Verbose "Checking harvest status for newly added packages..." -Verbose530 Import-Module -Name "$PSScriptRoot/clearlyDefined/src/ClearlyDefined" -Force531 532 # Get harvest data for all registrations533 $fullCgList = $newRegistrations |534 ForEach-Object {535 [PSCustomObject]@{536 type = $_.Component.Type537 Name = $_.Component.Nuget.Name538 PackageVersion = $_.Component.Nuget.Version539 }540 }541 542 $fullList = $fullCgList | Get-ClearlyDefinedData543 $needHarvest = $fullList | Where-Object { !$_.harvested }544 545 if ($needHarvest.Count -gt 0) {546 Write-Verbose "Found $($needHarvest.Count) packages that need harvesting. Starting harvest..." -Verbose547 $needHarvest | Select-Object -ExpandProperty coordinates | ConvertFrom-ClearlyDefinedCoordinates | Start-ClearlyDefinedHarvest548 } else {549 Write-Verbose "All packages are already harvested." -Verbose550 }551 552 # After manifest update and harvest, update TPN manifest with individual package status553 Write-Verbose "Updating TPN manifest with individual package harvest status..." -Verbose554 $tpnManifestDir = Join-Path -Path $PSScriptRoot -ChildPath "cgmanifest\tpn"555 $tpnManifestPath = Join-Path -Path $tpnManifestDir -ChildPath "cgmanifest.json"556 557 # Load current TPN manifest to get previous versions558 $currentTpnManifest = @()559 if (Test-Path $tpnManifestPath) {560 $currentTpnJson = Get-Content $tpnManifestPath | ConvertFrom-Json -AsHashtable561 $currentTpnManifest = $currentTpnJson.Registrations562 }563 564 # Build a lookup table of old versions565 $oldVersions = @{}566 foreach ($registration in $currentTpnManifest) {567 $name = $registration.Component.Nuget.Name568 if (!$oldVersions.ContainsKey($name)) {569 $oldVersions[$name] = $registration570 }571 }572 573 # Note: Do not recheck harvest status here. Harvesting is an async process that takes a significant amount of time.574 # Use the harvest data from the initial check. Newly triggered harvests will be captured575 # on the next run of this script after harvesting completes.576 $finalHarvestData = $fullList577 578 # Update packages individually based on harvest status579 $tpnRegistrations = @()580 $harvestedCount = 0581 $restoredCount = 0582 583 foreach ($item in $finalHarvestData) {584 $matchingNewRegistration = $newRegistrations | Where-Object {585 $_.Component.Nuget.Name -eq $item.Name -and586 $_.Component.Nuget.Version -eq $item.PackageVersion587 }588 589 if ($matchingNewRegistration) {590 if ($item.harvested) {591 # Use new harvested version592 $tpnRegistrations += $matchingNewRegistration593 $harvestedCount++594 } else {595 # Package not harvested - find the last harvested version from ClearlyDefined API596 Write-Verbose "Finding last harvested version for $($item.Name)..." -Verbose597 598 $lastHarvestedVersion = $null599 try {600 # Search through all versions of this package to find the last harvested one601 # Create a list of versions we know about from all runtimes602 $packageVersionsToCheck = $newRegistrations | Where-Object {603 $_.Component.Nuget.Name -eq $item.Name604 } | ForEach-Object { $_.Component.Nuget.Version } | Sort-Object -Unique -Descending605 606 foreach ($versionToCheck in $packageVersionsToCheck) {607 $versionCheckList = [PSCustomObject]@{608 type = "nuget"609 Name = $item.Name610 PackageVersion = $versionToCheck611 }612 613 $versionStatus = $versionCheckList | Get-ClearlyDefinedData614 if ($versionStatus -and $versionStatus.harvested) {615 $lastHarvestedVersion = $versionToCheck616 break # Found the most recent harvested version617 }618 }619 } catch {620 Write-Verbose "Error checking harvested versions for $($item.Name): $_" -Verbose621 }622 623 # Use last harvested version if found, otherwise use old version as fallback624 if ($lastHarvestedVersion) {625 $revertedReg = New-NugetComponent -Name $item.Name -Version $lastHarvestedVersion -DevelopmentDependency:$matchingNewRegistration.DevelopmentDependency626 $tpnRegistrations += $revertedReg627 $restoredCount++628 Write-Verbose "Reverted $($item.Name) from v$($item.PackageVersion) to last harvested v$lastHarvestedVersion" -Verbose629 } elseif ($oldVersions.ContainsKey($item.Name)) {630 $tpnRegistrations += $oldVersions[$item.Name]631 $restoredCount++632 Write-Verbose "Reverted $($item.Name) to previous version in TPN (no harvested version found)" -Verbose633 } else {634 Write-Warning "$($item.Name) v$($item.PackageVersion) not harvested and no harvested version found. Excluding from TPN manifest."635 }636 }637 }638 }639 640 # Save updated TPN manifest641 if ($tpnRegistrations.Count -gt 0) {642 $tpnManifest = @{643 Registrations = @($tpnRegistrations)644 '$schema' = "https://json.schemastore.org/component-detection-manifest.json"645 }646 647 $tpnJson = $tpnManifest | ConvertTo-Json -depth 99648 $tpnJson | Set-Content $tpnManifestPath -Encoding utf8NoBOM649 Write-Verbose "TPN manifest updated: $harvestedCount new harvested + $restoredCount reverted to last harvested versions" -Verbose650 }651}652 653if (!$Fix -and $registrationChanged) {654 $temp = Get-GWTempPath655 656 $tempJson = Join-Path -Path $temp -ChildPath "cgmanifest$((Get-Date).ToString('yyyMMddHHmm')).json"657 $newJson | Set-Content $tempJson -Encoding utf8NoBOM658 Set-GWVariable -Name CGMANIFEST_PATH -Value $tempJson659 throw "cgmanifest is out of date. run ./tools/findMissingNotices.ps1 -Fix. Generated cgmanifest is here: $tempJson"660}661 662Write-Verbose "$count registrations created!" -Verbose663 