😊

ソリューションファイルから各プロジェクトが参照しているパッケージのバージョンを取得する (PowerShell)

2023/05/17に公開
# Get-Package-Versions.ps1
# param は先頭に宣言する必要がある
param (
	[parameter(mandatory)]
	[string] $FilePath
)

Set-StrictMode -Version Latest


function Main([string] $filePath) {
	if ([string]::IsNullOrEmpty($filePath)) {
		Write-Error -Message "An error occurred." -ErrorAction Stop -Category InvalidArgument
		return 252
	}

	#Write-Verbose ""
	#Write-Verbose "################################################################################"
	#Write-Verbose ([string]::Format("{0}", (Split-Path -Leaf $PSCommandPath)))
	#Write-Verbose "FilePath:         $filePath"

	[array] $csprojPaths = Get-CSProjPaths $filePath
	foreach ($csprojPath in $csprojPaths) {
		Write-Verbose ""
		Write-Verbose ([string]::Format("■ {0}", (Split-Path -Leaf $csprojPath)))

		[string] $workspaceDir = Split-Path -Path $filePath -Parent
		[string] $csprojFullPath = Join-Path -Path $workspaceDir -ChildPath $csprojPath
		#Write-Verbose ([string]::Format("{0}", $csprojFullPath))
		[hashtable] $versions = Get-Package-Versions $csprojFullPath
		foreach ($include in $versions.Keys) {
			[string] $version = $versions[$include]
			if ((-not [string]::IsNullOrEmpty($include)) -and (-not [string]::IsNullOrEmpty($version))) {
				Write-Verbose ([string]::Format("・{0}: {1}", $include, $version))
			}
		}
	}

	#Write-Verbose ""
	#Write-Verbose "正常終了"
	return 0
}


######################################################################
# Solution and Project File

function Get-CSProjPaths([string] $filePath) {
	if ([string]::IsNullOrEmpty($filePath)) {
		Write-Error -Message "An error occurred." -ErrorAction Stop -Category InvalidArgument
		return $null
	}

	[array] $result = @()

	[string] $pattern = 'Project\("{[A-Z0-9\-]+}"\) = "[^"]+", "([^"]+\.csproj)", "{[A-Z0-9\-]+}"'
	[array] $matches = Select-String `
		-Path $filePath `
		-Pattern $pattern `
		-AllMatches `
	| ForEach-Object { $_.Matches }
	if (-not $matches) {
		Write-Error -Message "No csproj files found." -ErrorAction Stop -Category InvalidArgument
		return $result
	}

	[array] $csprojPaths = $matches | ForEach-Object { $_.Groups[1].Value } | Sort-Object
	foreach ($csprojPath in $csprojPaths) {
		#Write-Verbose ([string]::Format("{0}", $csprojPath))
		$result += $csprojPath
	}

	return $result
}

function Get-Package-Versions([string] $filePath) {
	if ([string]::IsNullOrEmpty($filePath)) {
		Write-Error -Message "An error occurred." -ErrorAction Stop -Category InvalidArgument
		return $null
	}

	[hashtable] $result = @{}

	try {
		[xml] $xml = Get-Content -Path $filePath
		[PSCustomObject] $namespaces = New-Object System.Xml.XmlNamespaceManager($xml.NameTable)
		$namespaces.AddNamespace("ns", $xml.Project.NamespaceURI)

		[array] $packageReferences = $xml.SelectNodes("//ns:PackageReference", $namespaces)
		foreach ($reference in $packageReferences) {
			$include = $reference.GetAttribute("Include")
			$version = $reference.GetAttribute("Version")
			if ((-not [string]::IsNullOrEmpty($include)) -and (-not [string]::IsNullOrEmpty($version))) {
				#Write-Verbose ([string]::Format("・{0}: {1}", $include, $version))
				$result[$include] = $version
			}
		}
	}
	catch {
		# Nothing to do
	}

	return $result
}


# Entry point
exit Main $FilePath

launch.jsonの例

{
	// IntelliSense を使用して利用可能な属性を学べます。
	// 既存の属性の説明をホバーして表示します。
	// 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387
	"version": "0.2.0",
	"configurations": [
		{
			"name": "すべてのパッケージのバージョンを取得する",
			"type": "PowerShell",
			"request": "launch",
			"script": "${workspaceFolder}/tools/Get-Package-Versions.ps1",
			"args": [
				"-FilePath ${workspaceFolder}/<YOUR_SOLUTION>.sln"
			],
			"cwd": "${workspaceFolder}",
			"createTemporaryIntegratedConsole": true
		}
	]
}

Discussion