Created
November 5, 2025 00:20
-
-
Save Wind010/72258e8d8720397186c2f0a79aed25fe to your computer and use it in GitHub Desktop.
Adds all projects found under the specified repo roots to the solution. Updates the original projects package references to project references where applicable.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Adds all projects found under the specified repo roots to the solution. | |
| # Updates the original projects package references to project references where applicable. | |
| # Usage: .\Convert-To-ProjectReferences.ps1 -SolutionPath "<path_to_solution>" -RepoRoots "<path_to_repo_root_1>", "<path_to_repo_root_2>" | |
| # The IgnoreTestProjects parameter is defaulted to $true. | |
| # The -Debug switch exists. | |
| param( | |
| [Parameter(Mandatory=$true)] | |
| [string]$SolutionPath, | |
| [Parameter(Mandatory=$true)] | |
| [string[]]$RepoRoots, | |
| [Parameter(Mandatory=$false)] | |
| [string[]]$IgnoreTestProjects=$true | |
| ) | |
| $ErrorActionPreference = "Stop" | |
| function Get-CsprojFilesInSolution { | |
| param($solutionPath) | |
| Write-Host "π§βπ» Scanning project files under '$SolutionPath' ..." -ForegroundColor Blue | |
| $lines = Get-Content $solutionPath | |
| $csprojPaths = $lines | Where-Object { $_ -match '\.csproj"' } | ForEach-Object { | |
| if ($_ -match '"([^"]+\.csproj)"') { $matches[1] } | |
| } | |
| $solutionDir = Split-Path $solutionPath | |
| $csprojPaths | ForEach-Object { Join-Path $solutionDir $_ } | |
| } | |
| function Get-ProjectMap { | |
| param($repoRoots) | |
| $projectMap = @{} | |
| foreach ($root in $repoRoots) { | |
| Write-Host "π§βπ» Generating project map for project files under '$root' ..." -ForegroundColor Blue | |
| Get-ChildItem -Path $root -Recurse -Filter *.csproj | ForEach-Object { | |
| if ($IgnoreTestProjects -and $_.Directory -match 'Test|Tests') { | |
| Write-Warning "Ignoring test project: $($_.FullName)" | |
| return | |
| } | |
| [xml]$projectXml = Get-Content $_.FullName | |
| $assemblyNameNode = $projectXml.Project.PropertyGroup.AssemblyName | |
| if ([string]::IsNullOrWhiteSpace($assemblyNameNode)) { | |
| Write-Warning "Project $($_.FullName) does not have an AssemblyName defined. Skipping." | |
| } | |
| else { | |
| Write-Debug "Adding $assemblyNameNode to project map." | |
| $projectMap[$assemblyNameNode] = $_.FullName | |
| } | |
| } | |
| } | |
| return $projectMap | |
| } | |
| function Get-RelativePath { | |
| param($fromPath, $toPath) | |
| Write-Host "π§βπ» Getting relative paths from '$fromPath' to '$toPath'..." -ForegroundColor Blue | |
| $fromUri = New-Object System.Uri($fromPath) | |
| $toUri = New-Object System.Uri($toPath) | |
| $relativeUri = $fromUri.MakeRelativeUri($toUri) | |
| $relativePath = [System.Uri]::UnescapeDataString($relativeUri.ToString()) | |
| if ($toPath -like "*.csproj" -and $relativePath -notlike "*.csproj") { | |
| $relativePath = $relativePath -replace "/", "\" | |
| } | |
| return $relativePath | |
| } | |
| function Add-ProjectsToSolution { | |
| param( | |
| [Parameter(Mandatory=$true)] | |
| [string]$SolutionPath, | |
| [Parameter(Mandatory=$true)] | |
| [hashtable]$ProjectMap | |
| ) | |
| foreach ($projectPath in $ProjectMap.Values) { | |
| Write-Host "π§βπ» Adding $projectPath to solution $SolutionPath..." | |
| dotnet sln $SolutionPath add $projectPath | |
| } | |
| } | |
| function Replace-PackageWithProjectReference { | |
| param($csprojPath, $projectMap) | |
| [xml]$projectXml = Get-Content $csprojPath | |
| $changed = $false | |
| Write-Host "`tProcessing '$csprojPath' ..." | |
| foreach ($itemGroup in $projectXml.Project.ItemGroup) { | |
| $packageRefs = @($itemGroup.PackageReference) | |
| foreach ($packageRef in $packageRefs) { | |
| $packageName = $packageRef.Include | |
| if ([string]::IsNullOrWhitespace($packageName)) { | |
| continue | |
| } | |
| if ($projectMap.ContainsKey($packageName)) { | |
| # Remove PackageReference | |
| Write-Debug "Replacing PackageReference '$packageName' with ProjectReference." | |
| $itemGroup.RemoveChild($packageRef) | |
| # Add ProjectReference with relative path | |
| Write-Debug "Adding ProjectReference to '$($projectMap[$packageName])'." | |
| $refPath = Get-RelativePath $csprojPath $projectMap[$packageName] | |
| $projectRef = $projectXml.CreateElement("ProjectReference") | |
| $projectRef.SetAttribute("Include", $refPath) | |
| $itemGroup.AppendChild($projectRef) | |
| $changed = $true | |
| } | |
| else { | |
| Write-Debug "No matching project found for package '$packageName'." | |
| } | |
| } | |
| } | |
| if ($changed) { | |
| $projectXml.Save($csprojPath) | |
| Write-Host "Updated $csprojPath" | |
| } | |
| } | |
| # Main | |
| $csprojFiles = Get-CsprojFilesInSolution $SolutionPath | |
| $projectMap = Get-ProjectMap $RepoRoots | |
| Add-ProjectsToSolution -SolutionPath $SolutionPath -ProjectMap $projectMap | |
| foreach ($csproj in $csprojFiles) { | |
| Replace-PackageWithProjectReference $csproj $projectMap | Out-Null | |
| } | |
| Write-Host "β Done! Projects in '$SolutionPath' had matching package references replaced by project references." -ForegroundColor Green |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment