Netscoot.Core/Public/Get-SolutionInventory.ps1
|
function Get-SolutionInventory { <# .SYNOPSIS List the full contents of every solution in a repository (projects of any type, solution folders, and solution items), plus on-disk projects that no solution references. .DESCRIPTION Where Test-SolutionConsistency compares membership and Repair-SolutionReferences finds dangling entries, this gives the complete picture without reading the files by hand. It parses each .sln/.slnx directly (not via `dotnet sln list`, which only returns CLI-buildable projects), so it also surfaces non-CLI project types (e.g. .pssproj), solution folders, and loose solution items. It then compares against the projects on disk and flags any that are in no solution at all. Read-only: One record per item, so you can group, filter, or format it however you like. .PARAMETER RepositoryRoot Root to scan. Accepts pipeline input (path string, or any object with a FullName/Path property). Defaults to the enclosing git repository root. Nested git worktrees are skipped. .OUTPUTS Netscoot.SolutionItem - one per item. .EXAMPLE # Everything across all solutions, plus projects in none Get-SolutionInventory -RepositoryRoot . | Format-Table -AutoSize # Only the projects on disk that no solution references Get-SolutionInventory | Where-Object Kind -eq 'UnreferencedProject' # Only loose solution items (e.g. a README in a solution folder) Get-SolutionInventory | Where-Object Kind -eq 'SolutionItem' # Kind is the [Netscoot.SolutionItemKind] enum, so this also works Get-SolutionInventory | Where-Object Kind -eq ([Netscoot.SolutionItemKind]::UnreferencedProject) #> [CmdletBinding()] [OutputType('Netscoot.SolutionItem')] param( [Parameter(Position = 0, ValueFromPipeline, ValueFromPipelineByPropertyName)] [Alias('FullName', 'Path', 'PSPath')] [string]$RepositoryRoot ) process { if (-not $RepositoryRoot) { $RepositoryRoot = Get-RepositoryRoot -StartPath (Get-Location).Path } $RepositoryRoot = Resolve-FullPath $RepositoryRoot function _rel([string]$p) { (Get-RelativePathSafe -From $RepositoryRoot -To $p) } $solutions = @(Find-Solutions -Root $RepositoryRoot) $seen = [System.Collections.Generic.List[string]]::new() foreach ($sln in $solutions) { $rel = _rel $sln.FullName $content = Get-SolutionContent -SolutionFile $sln.FullName foreach ($p in $content.Projects) { $seen.Add($p.Abs) [pscustomobject]@{ PSTypeName = 'Netscoot.SolutionItem' Solution = $rel Kind = [Netscoot.SolutionItemKind]::Project Type = $p.Ext.TrimStart('.') Name = Split-Path -Leaf $p.Abs Path = $p.Stored } } foreach ($f in $content.Folders) { [pscustomobject]@{ PSTypeName = 'Netscoot.SolutionItem'; Solution = $rel; Kind = [Netscoot.SolutionItemKind]::SolutionFolder; Type = ''; Name = $f; Path = '' } } foreach ($i in $content.Items) { [pscustomobject]@{ PSTypeName = 'Netscoot.SolutionItem'; Solution = $rel; Kind = [Netscoot.SolutionItemKind]::SolutionItem; Type = ''; Name = (Split-Path -Leaf $i); Path = $i } } } # Projects on disk (managed and native) that no solution references at all. foreach ($disk in (Find-ProjectFiles -Root $RepositoryRoot -IncludeNative)) { $abs = Resolve-FullPath $disk.FullName if (-not (Test-PathInList -Path $abs -List $seen)) { [pscustomobject]@{ PSTypeName = 'Netscoot.SolutionItem' Solution = '(none)' Kind = [Netscoot.SolutionItemKind]::UnreferencedProject Type = $disk.Extension.TrimStart('.') Name = $disk.Name Path = _rel $abs } } } } } |