Public/PSResourceHelpers.ps1
|
function Get-InstalledPSResourceNameInPath { [CmdletBinding()] param( [Parameter(Mandatory)] [string]$Path ) if (-not (Test-Path -LiteralPath $Path -PathType Container)) { return } $resolvedPath = (Resolve-Path -LiteralPath $Path).ProviderPath Get-ChildItem -LiteralPath $resolvedPath -Directory -ErrorAction SilentlyContinue | ForEach-Object { $resourceName = $_.Name $resourceRoot = $_.FullName $directManifest = Join-Path $resourceRoot "$resourceName.psd1" if (Test-Path -LiteralPath $directManifest -PathType Leaf) { $resourceName } else { $versionedManifest = Get-ChildItem -LiteralPath $resourceRoot -Directory -ErrorAction SilentlyContinue | Where-Object { Test-Path -LiteralPath (Join-Path $_.FullName "$resourceName.psd1") -PathType Leaf } | Select-Object -First 1 if ($versionedManifest) { $resourceName } } } | Sort-Object -Unique } function Get-InstalledPSResourceVersionInPath { # Returns the highest installed [version] for a named resource in the given # module root, or $null when no recognisable layout is found. [CmdletBinding()] param( [Parameter(Mandatory)] [string]$Path, [Parameter(Mandatory)] [string]$Name ) $moduleRoot = Join-Path $Path $Name if (-not (Test-Path -LiteralPath $moduleRoot -PathType Container)) { return $null } # Versioned layout: <Root>/<Name>/<Version>/<Name>.psd1 — version is the directory name. $versions = @( Get-ChildItem -LiteralPath $moduleRoot -Directory -ErrorAction SilentlyContinue | Where-Object { Test-Path -LiteralPath (Join-Path $_.FullName "$Name.psd1") -PathType Leaf } | ForEach-Object { try { [version]$_.Name } catch { $null } } | Where-Object { $null -ne $_ } ) if ($versions.Count -gt 0) { return ($versions | Sort-Object -Descending)[0] } # Direct layout: <Root>/<Name>/<Name>.psd1 — read version from manifest. $directManifest = Join-Path $moduleRoot "$Name.psd1" if (Test-Path -LiteralPath $directManifest -PathType Leaf) { try { return [version](Import-PowerShellDataFile -LiteralPath $directManifest -ErrorAction Stop).ModuleVersion } catch { Write-Verbose "Could not read module version from '$directManifest': $_" } } return $null } function Update-InstalledPSResource { <# .SYNOPSIS Update PowerShell resources deployed to a custom module path via Save-PSResource. .DESCRIPTION Enumerates modules discovered under the supplied path — as deployed by Save-PSResource — determines each module's highest installed version from the on-disk layout (<Path>/<Name>/<Version>/<Name>.psd1 for versioned layouts, or <Path>/<Name>/<Name>.psd1 for direct layouts), compares it to the latest version available in the given repository, and saves a newer version alongside the existing ones using Save-PSResource with the same root path. No Install-PSResource tracking or PSModulePath manipulation is required; this approach works correctly for modules deployed with Save-PSResource to any arbitrary directory. No-op when the path does not exist, when no modules are found, or when all discovered modules are already at the latest version. A repository lookup failure for one module is reported as a non-terminating warning; processing continues for the remaining modules. .PARAMETER Path The module root directory to scan for installed PowerShell resources. This should be the same root directory that was passed to Save-PSResource when the modules were deployed. .PARAMETER Repository PSResourceGet repository to query for available updates. Defaults to 'PSGallery'. .EXAMPLE Update-InstalledPSResource -Path (Join-Path $HOME 'PowerShellModules') Checks PSGallery for newer versions of every module found under the custom module path and saves updates in place. .EXAMPLE Update-InstalledPSResource -Path $env:PSModulePath.Split([IO.Path]::PathSeparator)[0] -WhatIf Shows which resources would be updated without making any changes. #> [CmdletBinding(SupportsShouldProcess)] param( [Parameter(Mandatory, Position = 0)] [ValidateNotNullOrEmpty()] [string]$Path, [Parameter()] [string]$Repository = 'PSGallery' ) process { if (-not (Test-Path -LiteralPath $Path -PathType Container)) { Write-Verbose "Module path '$Path' does not exist. Nothing to update." return } $resolvedPath = (Resolve-Path -LiteralPath $Path).ProviderPath $resourceNames = @(Get-InstalledPSResourceNameInPath -Path $resolvedPath) foreach ($resourceName in $resourceNames) { $installedVersion = Get-InstalledPSResourceVersionInPath -Path $resolvedPath -Name $resourceName if ($null -eq $installedVersion) { Write-Verbose "Could not determine installed version of '$resourceName'; skipping." continue } $latestRemote = $null try { $latestRemote = @(Find-PSResource -Name $resourceName -Repository $Repository -ErrorAction Stop) | Sort-Object { [version]$_.Version } -Descending | Select-Object -First 1 } catch { Write-Warning "Could not look up '$resourceName' in repository '$Repository': $_" continue } if ($null -eq $latestRemote) { Write-Verbose "'$resourceName' was not found in repository '$Repository'; skipping." continue } $remoteVersion = [version]$latestRemote.Version.ToString() if ($installedVersion -ge $remoteVersion) { Write-Verbose "'$resourceName' $installedVersion is already current (latest: $remoteVersion)." continue } if ($PSCmdlet.ShouldProcess($resourceName, "Update from $installedVersion to $remoteVersion in '$resolvedPath'")) { Save-PSResource -Name $resourceName -Version $remoteVersion.ToString() -Path $resolvedPath ` -Repository $Repository -TrustRepository -IncludeXml -AcceptLicense ` -SkipDependencyCheck -ErrorAction Stop } } } } |