PSGalleryBatch.psm1
|
function Find-GalleryModuleBatch { [CmdletBinding()] param( [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] [string]$Name, [Parameter()] [switch]$AllowPrerelease, [Parameter()] [switch]$Install, [Parameter()] [switch]$Update, [Parameter()] [switch]$Silent ) process { # Auto-wildcard for convenience if ($Name -notmatch '\*') { $Name = "*$Name*" } Write-Host "Searching for modules matching '$Name'..." -ForegroundColor Cyan $modules = @() try { # Try to use Find-PSResource (PSResourceGet v3) first as it supports wildcards + prerelease if (Get-Command Find-PSResource -ErrorAction SilentlyContinue) { Write-Verbose "Using Find-PSResource" $modules = @(Find-PSResource -Name $Name -Repository PSGallery -Prerelease:$AllowPrerelease -ErrorAction Stop) } else { Write-Verbose "Using Find-Module (PowerShellGet v2)" if ($AllowPrerelease -and $Name -match '\*') { Write-Warning "PowerShellGet (Find-Module) does not support Wildcards with -AllowPrerelease. SEARCHING STABLE ONLY." $modules = @(Find-Module -Name $Name -Repository PSGallery -ErrorAction Stop) } else { $modules = @(Find-Module -Name $Name -Repository PSGallery -AllowPrerelease:$AllowPrerelease -ErrorAction Stop) } } if ($modules.Count -eq 0) { Write-Warning "No modules found matching '$Name'." return } Write-Host "Found $($modules.Count) modules." -ForegroundColor Green $selected = @() if ($Silent) { Write-Host "Silent mode: Selecting all $($modules.Count) modules." -ForegroundColor Yellow $selected = $modules } else { # Invoke UI selection $selected = Show-ModuleSelection -Modules $modules } if ($selected) { Write-Host "`nProcessing $($selected.Count) selected module(s)..." -ForegroundColor Cyan if ($Install) { $selected | Install-GalleryModuleBatch -AllowPrerelease:$AllowPrerelease } elseif ($Update) { $selected | Update-GalleryModuleBatch -AllowPrerelease:$AllowPrerelease } else { return $selected } } } catch { Write-Warning "Search failed: $_" } } } function Show-ModuleSelection { param( [Parameter(Mandatory = $true)] [array]$Modules ) # Attempt to use Spectre Console if available if (Get-Command Read-SpectreMultiSelection -ErrorAction SilentlyContinue) { $choices = $Modules | ForEach-Object { $desc = if ($_.Description -and $_.Description.Length -gt 60) { $_.Description.Substring(0, 57) + "..." } else { $_.Description } "$($_.Name) ($($_.Version)) - $desc" } $selectedStrings = Read-SpectreMultiSelection -Message "[cyan]Select modules to process (Space to toggle, Enter to confirm)[/]" ` -Choices $choices ` -Color "Green" ` -PageSize 20 if (-not $selectedStrings) { return $null } $results = foreach ($sel in $selectedStrings) { if ($sel -match '^(.+?)\s+\((.+?)\)\s+-') { $mName = $matches[1] $mVer = $matches[2] $Modules | Where-Object { $_.Name -eq $mName -and $_.Version -eq $mVer } | Select-Object -First 1 } } return $results } else { Write-Warning "Spectre Console 'Read-SpectreMultiSelection' not found. Using Out-GridView." return $Modules | Out-GridView -Title "Select Modules" -PassThru } } function Install-GalleryModuleBatch { [CmdletBinding()] param( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] $InputObject, [Parameter()] [switch]$AllowPrerelease ) begin { $items = @() } process { $items += $InputObject } end { if ($items.Count -eq 0) { return } foreach ($mod in $items) { Write-Host "Installing: " -ForegroundColor Magenta -NoNewline Write-Host "$($mod.Name) ($($mod.Version))" -ForegroundColor White try { if (Get-Command Install-PSResource -ErrorAction SilentlyContinue) { # Removed -Force (not supported), using -TrustRepository Install-PSResource -Name $mod.Name -Version $mod.Version -Prerelease:$AllowPrerelease -TrustRepository -Scope CurrentUser -ErrorAction Stop } else { Install-Module -Name $mod.Name -RequiredVersion $mod.Version -AllowPrerelease:$AllowPrerelease -Scope CurrentUser -Force -AllowClobber -ErrorAction Stop } Write-Host "✓ Successfully installed $($mod.Name)" -ForegroundColor Green } catch { Write-Error "✗ Failed to install $($mod.Name): $_" } } } } function Update-GalleryModuleBatch { [CmdletBinding()] param( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] $InputObject, [Parameter()] [switch]$AllowPrerelease ) begin { $items = @() } process { $items += $InputObject } end { if ($items.Count -eq 0) { return } foreach ($mod in $items) { Write-Host "Updating: " -ForegroundColor Magenta -NoNewline Write-Host "$($mod.Name)" -ForegroundColor White try { if (Get-Command Update-PSResource -ErrorAction SilentlyContinue) { Update-PSResource -Name $mod.Name -Prerelease:$AllowPrerelease -TrustRepository -ErrorAction Stop } else { Update-Module -Name $mod.Name -AllowPrerelease:$AllowPrerelease -Force -ErrorAction Stop } Write-Host "✓ Successfully updated $($mod.Name)" -ForegroundColor Green } catch { Write-Error "✗ Failed to update $($mod.Name): $_" } } } } function Get-PSGalleryNew { <# .SYNOPSIS Browse and install newly published PowerShell Gallery modules interactively. .DESCRIPTION Fetches the most recently published modules from the PowerShell Gallery, sorted by publication date (newest first), and presents them in an interactive multi-select menu (Spectre Console or Out-GridView fallback). Selected modules are immediately installed. .PARAMETER Count How many of the most recent modules to fetch. Default: 50. .PARAMETER Tag Optionally filter to modules matching a specific PSGallery tag (e.g. 'Azure'). .PARAMETER AllowPrerelease Include prerelease versions. .PARAMETER Scope Installation scope. Default: CurrentUser. .EXAMPLE Get-PSGalleryNew # Fetches the 50 newest modules and shows an interactive picker. .EXAMPLE Get-PSGalleryNew -Count 100 -Tag 'Azure' # Fetches the 100 newest Azure-tagged modules. .NOTES Author: Matthew Bubb #> [CmdletBinding()] param( [Parameter()] [ValidateRange(1, 500)] [int]$Count = 50, [Parameter()] [string]$Tag, [Parameter()] [switch]$AllowPrerelease, [Parameter()] [ValidateSet('CurrentUser', 'AllUsers')] [string]$Scope = 'CurrentUser' ) Write-Host "`nFetching the $Count newest modules from PSGallery..." -ForegroundColor Cyan $modules = @() try { # Build find params $findParams = @{ Repository = 'PSGallery' ErrorAction = 'Stop' } if ($AllowPrerelease) { $findParams['Prerelease'] = $true } if (Get-Command Find-PSResource -ErrorAction SilentlyContinue) { # PSResourceGet v3 — supports -Tag natively if ($Tag) { $findParams['Tag'] = $Tag } # Fetch more than we need so we can sort by PublishedDate $raw = Find-PSResource @findParams -Name '*' | Sort-Object PublishedDate -Descending | Select-Object -First $Count } else { # PowerShellGet v2 fallback if ($Tag) { $findParams['Tag'] = $Tag } $raw = Find-Module @findParams | Sort-Object PublishedDate -Descending | Select-Object -First $Count } $modules = @($raw) } catch { Write-Error "Failed to query PSGallery: $_" return } if ($modules.Count -eq 0) { Write-Warning "No modules found." return } Write-Host "Found " -ForegroundColor Green -NoNewline Write-Host "$($modules.Count)" -ForegroundColor White -NoNewline Write-Host " new module(s). Select ones to install:`n" -ForegroundColor Green # ── Build display strings ────────────────────────────────────────────────── $choices = [System.Collections.Generic.List[string]]::new() $choiceMap = @{} # display string -> module object foreach ($mod in $modules) { $pubDate = if ($mod.PublishedDate) { $mod.PublishedDate.ToString('yyyy-MM-dd') } else { '????-??-??' } $desc = if ($mod.Description -and $mod.Description.Length -gt 55) { $mod.Description.Substring(0, 52) + '...' } else { $mod.Description } $label = "$($mod.Name) v$($mod.Version) [$pubDate] — $desc" $choices.Add($label) $choiceMap[$label] = $mod } # ── Interactive selection ───────────────────────────────────────────────── $selected = @() if (Get-Command Read-SpectreMultiSelection -ErrorAction SilentlyContinue) { $selectedLabels = Read-SpectreMultiSelection ` -Message "[cyan]Select modules to install[/] [dim](Space = toggle, Enter = confirm)[/]" ` -Choices $choices ` -Color "Green" ` -PageSize 20 if (-not $selectedLabels) { Write-Host "Nothing selected." -ForegroundColor Yellow return } $selected = foreach ($label in $selectedLabels) { $choiceMap[$label] } } else { Write-Warning "Spectre Console not found — falling back to Out-GridView." $gridItems = $modules | Select-Object Name, Version, PublishedDate, Description $gridSelected = $gridItems | Out-GridView -Title "Select modules to install" -PassThru if (-not $gridSelected) { Write-Host "Nothing selected." -ForegroundColor Yellow return } $selected = foreach ($g in $gridSelected) { $modules | Where-Object { $_.Name -eq $g.Name -and $_.Version -eq $g.Version } | Select-Object -First 1 } } if (-not $selected -or @($selected).Count -eq 0) { Write-Host "Nothing selected." -ForegroundColor Yellow return } # ── Install selected ────────────────────────────────────────────────────── Write-Host "`nInstalling $(@($selected).Count) module(s)..." -ForegroundColor Cyan foreach ($mod in $selected) { Write-Host "Installing: " -ForegroundColor Magenta -NoNewline Write-Host "$($mod.Name) v$($mod.Version)" -ForegroundColor White try { if (Get-Command Install-PSResource -ErrorAction SilentlyContinue) { Install-PSResource -Name $mod.Name -Version $mod.Version ` -Prerelease:$AllowPrerelease -TrustRepository -Scope $Scope -ErrorAction Stop } else { Install-Module -Name $mod.Name -RequiredVersion $mod.Version ` -AllowPrerelease:$AllowPrerelease -Scope $Scope -Force -AllowClobber -ErrorAction Stop } Write-Host "✓ $($mod.Name) installed." -ForegroundColor Green } catch { Write-Error "✗ Failed to install $($mod.Name): $_" } } } |