DotNetPackageProvider.ps1
|
function Invoke-DotNetPackageCommand { [CmdletBinding()] param( [Parameter(Mandatory)][scriptblock]$Command, [Parameter(Mandatory)][string]$Operation ) # The canonical tool commands do not check every native exit code. # Isolate stale caller state, and restore it even when a command throws. $previousExitCode = $global:LASTEXITCODE try { $global:LASTEXITCODE = 0 $output = @(& $Command) if ($global:LASTEXITCODE -ne 0) { throw "$Operation failed with dotnet exit code $global:LASTEXITCODE." } $output } finally { $global:LASTEXITCODE = $previousExitCode } } function Assert-DotNetPackageTool { [CmdletBinding()] param([Parameter(Mandatory)][object]$Tool) if ($Tool.PSTypeNames -notcontains 'DotNetTool' -or $Tool.PackageId -isnot [string] -or $Tool.PackageId -notmatch '\A[A-Za-z0-9][A-Za-z0-9._-]*\z' -or $Tool.Global -isnot [bool] -or -not $Tool.Global -or -not $Tool.PSObject.Properties['Version'] -or ($null -ne $Tool.Version -and $Tool.Version -isnot [string])) { throw 'Shmuelie.DotNet returned an invalid global tool.' } } function Get-DotNetPackageProvider { [CmdletBinding()] param() [pscustomobject]@{ Name = 'DotNet' Platforms = @('Windows', 'Linux', 'MacOS') RequiredModules = @('Shmuelie.DotNet') RequiredCommands = @('dotnet', 'Shmuelie.DotNet\Get-DotNetTool', 'Shmuelie.DotNet\Update-DotNetTool') OptionNames = @('Name') TestAvailable = { param([hashtable]$Options) $sdks = @(Invoke-DotNetPackageCommand -Operation 'SDK discovery' -Command { dotnet --list-sdks }) if (-not ($sdks | Where-Object { $_ -match '^\d+\.\d+\.\d+\S*\s+\[.+\]\s*$' })) { return [pscustomobject]@{ Available = $false; Reason = 'Install a .NET SDK accessible through dotnet on PATH; a runtime alone cannot update tools.' } } [pscustomobject]@{ Available = $true; Reason = $null } } GetTargets = { param([hashtable]$Options) $name = '*' if ($Options.ContainsKey('Name')) { if ($Options.Name -isnot [string] -or [string]::IsNullOrWhiteSpace($Options.Name)) { throw "DotNet option 'Name' must be a nonempty wildcard string." } $name = $Options.Name $null = [System.Management.Automation.WildcardPattern]::new($name).IsMatch('') } $tools = @(Invoke-DotNetPackageCommand -Operation 'Global tool discovery' -Command { Shmuelie.DotNet\Get-DotNetTool -Name $name -ErrorAction Stop }) foreach ($tool in $tools) { Assert-DotNetPackageTool -Tool $tool } # The public API cannot discover latest versions without an update. foreach ($tool in $tools) { New-PackageUpdateTarget -Target $tool.PackageId -PreviousVersion $tool.Version -Data $tool } } Update = { param($Target, [hashtable]$Options) $results = @(Invoke-DotNetPackageCommand -Operation "Update of '$($Target.Target)'" -Command { Shmuelie.DotNet\Update-DotNetTool -InputObject $Target.Data -Confirm:$false -ErrorAction Stop }) if ($results.Count -ne 1 -or $results[0].PSTypeNames -notcontains 'DotNetToolUpdateResult' -or $results[0].PackageId -ne $Target.Target -or $results[0].Updated -isnot [bool] -or -not $results[0].PSObject.Properties['Version'] -or ($null -ne $results[0].Version -and $results[0].Version -isnot [string])) { throw "Shmuelie.DotNet returned invalid update output for '$($Target.Target)'; the outcome is unknown." } $installed = @(Invoke-DotNetPackageCommand -Operation "Version discovery for '$($Target.Target)'" -Command { Shmuelie.DotNet\Get-DotNetTool -Name $Target.Target -ErrorAction Stop }) if ($installed.Count -ne 1 -or $installed[0].PackageId -ne $Target.Target) { throw "Cannot observe the installed global tool '$($Target.Target)' after its update." } Assert-DotNetPackageTool -Tool $installed[0] $version = $installed[0].Version if ($Target.PreviousVersion -and $version) { $status = if ($version -eq $Target.PreviousVersion) { 'Unchanged' } else { 'Updated' } } elseif ($results[0].Updated) { $status = 'Updated' } else { throw "Cannot determine whether '$($Target.Target)' changed; the canonical command reported no update and installed versions are unknown." } New-PackageUpdateResult -Provider DotNet -Target $Target.Target -PreviousVersion $Target.PreviousVersion -ResultingVersion $version -Status $status } } } |