param( [Parameter(Mandatory = $true)][string]$RepoRoot, [Parameter(Mandatory = $true)][string]$OutputRoot ) $ErrorActionPreference = "Stop" $repo = (Resolve-Path $RepoRoot).Path $assetFiles = @(Get-ChildItem (Join-Path $repo "companion") -Filter project.assets.json -File -Recurse -ErrorAction SilentlyContinue | Where-Object { $_.FullName -notmatch '[\\/]installer[\\/]output[\\/]' }) if ($assetFiles.Count -eq 0) { throw "No restored NuGet project.assets.json files were found after publish." } Remove-Item $OutputRoot -Recurse -Force -ErrorAction SilentlyContinue New-Item -ItemType Directory -Force -Path $OutputRoot | Out-Null function Get-ChildText([System.Xml.XmlNode]$Node, [string]$Name) { if (!$Node) { return $null } $child = $Node.SelectSingleNode("*[local-name()='$Name']") if (!$child) { return $null } return $child.InnerText.Trim() } $packages = @{} foreach ($assetFile in $assetFiles) { $assets = Get-Content $assetFile.FullName -Raw | ConvertFrom-Json $packageFolders = @($assets.packageFolders.PSObject.Properties.Name) foreach ($libraryProperty in $assets.libraries.PSObject.Properties) { if ($libraryProperty.Value.type -ne "package") { continue } $parts = $libraryProperty.Name -split '/', 2 if ($parts.Count -ne 2) { continue } $id = $parts[0] $version = $parts[1] $key = "$($id.ToLowerInvariant())/$($version.ToLowerInvariant())" if ($packages.ContainsKey($key)) { continue } $packageRoot = $null foreach ($folder in $packageFolders) { $candidate = Join-Path $folder ($key -replace '/', [IO.Path]::DirectorySeparatorChar) if (Test-Path $candidate) { $packageRoot = (Resolve-Path $candidate).Path; break } } if (!$packageRoot) { throw "Restored package files are missing for $id $version." } $packages[$key] = [ordered]@{ Id = $id; Version = $version; Root = $packageRoot } } } $inventory = [Collections.Generic.List[string]]::new() $inventory.Add("LUMI COMPANION - EXACT NUGET PACKAGE LICENCE INVENTORY") $inventory.Add("Generated: $([DateTime]::UtcNow.ToString('yyyy-MM-dd HH:mm:ss')) UTC") $inventory.Add("") $inventory.Add("This file is generated from the restored project.assets.json files used by the Companion build. Package-specific licence, notice, copying, and copyright files found in the restored packages are copied into the adjacent package directories.") $inventory.Add("") foreach ($package in @($packages.Values | Sort-Object Id, Version)) { $safeName = ("$($package.Id)-$($package.Version)" -replace '[^A-Za-z0-9._-]', '_') $packageOutput = Join-Path $OutputRoot $safeName New-Item -ItemType Directory -Force -Path $packageOutput | Out-Null $nuspec = Get-ChildItem $package.Root -Filter *.nuspec -File | Select-Object -First 1 $metadata = $null if ($nuspec) { [xml]$nuspecXml = Get-Content $nuspec.FullName -Raw $metadata = $nuspecXml.SelectSingleNode("/*[local-name()='package']/*[local-name()='metadata']") } $licenseText = Get-ChildText $metadata "license" if (!$licenseText) { $licenseText = Get-ChildText $metadata "licenseUrl" } $authorsText = Get-ChildText $metadata "authors" $copyrightText = Get-ChildText $metadata "copyright" $projectUrlText = Get-ChildText $metadata "projectUrl" $license = if ($licenseText) { $licenseText } else { "Not declared in package metadata" } $authors = if ($authorsText) { $authorsText } else { "Not declared" } $copyright = if ($copyrightText) { $copyrightText } else { "Not declared" } $projectUrl = if ($projectUrlText) { $projectUrlText } else { "Not declared" } $copied = [Collections.Generic.List[string]]::new() $noticeFiles = @(Get-ChildItem $package.Root -File -Recurse -ErrorAction SilentlyContinue | Where-Object { $_.Length -le 5MB -and $_.Name -match '^(license|licence|notice|third[-_. ]?party|thirdparty|copying|copyright)([._ -].*)?$' }) foreach ($notice in $noticeFiles) { $rootPrefix = $package.Root.TrimEnd([char[]]"\/") $relative = $notice.FullName.Substring($rootPrefix.Length).TrimStart([char[]]"\/") $targetName = ($relative -replace '[\\/:*?"<>|]', '__') $target = Join-Path $packageOutput $targetName Copy-Item $notice.FullName $target -Force $copied.Add($targetName) } if ($copied.Count -eq 0) { Remove-Item $packageOutput -Force } $inventory.Add("Package: $($package.Id) $($package.Version)") $inventory.Add("Authors: $authors") $inventory.Add("Copyright: $copyright") $inventory.Add("Licence declaration: $license") $inventory.Add("Project: $projectUrl") $inventory.Add("Copied notice files: $(if ($copied.Count) { $copied -join ', ' } else { 'none found in restored package' })") $inventory.Add("") } [IO.File]::WriteAllLines((Join-Path $OutputRoot "NuGet-LICENCE-INVENTORY.txt"), $inventory, [Text.UTF8Encoding]::new($false)) Write-Host "Collected legal metadata for $($packages.Count) restored NuGet packages."