PSResourceGet.Bootstrap.psm1
#Region '.\prefix.ps1' -1 $script:dscResourceCommonModulePath = Join-Path -Path $PSScriptRoot -ChildPath 'Modules/DscResource.Common' Import-Module -Name $script:dscResourceCommonModulePath $script:localizedData = Get-LocalizedData -DefaultUICulture 'en-US' #EndRegion '.\prefix.ps1' 5 #Region '.\Public\Start-PSResourceGetBootstrap.ps1' -1 <# .SYNOPSIS Bootstraps the Microsoft.PowerShell.PSResourceGet module to the specified location. .DESCRIPTION The command Start-PSResourceGetBootstrap is used to bootstrap the Microsoft.PowerShell.PSResourceGet module. It supports two parameter sets: 'Destination' and 'Scope'. The 'Destination' parameter set allows you to specify a specific location to save the module, while the 'Scope' parameter set saves the module to the appropriate `$env:PSModulePath` location based on the specified scope ('CurrentUser' or 'AllUsers'). .PARAMETER Destination Specifies the destination path where the module should be saved. This parameter is mandatory when using the 'Destination' parameter set. The path must be a valid container. .PARAMETER Scope Specifies the scope for saving the module. This parameter is used when using the 'Scope' parameter set. The valid values are 'CurrentUser' and 'AllUsers'. The default value is 'CurrentUser'. .PARAMETER Version Specifies the version of the Microsoft.PowerShell.PSResourceGet module to download. If not specified, the latest version will be downloaded. .PARAMETER UseCompatibilityModule Indicates whether to use the compatibility module. If this switch parameter is present, the compatibility module will be downloaded. .PARAMETER CompatibilityModuleVersion Specifies the version of the compatibility module to download. If not specified, it will default to a minimum required range that includes previews. .PARAMETER Force Forces the operation without prompting for confirmation. This is useful when running the script in non-interactive mode. .PARAMETER ImportModule Indicates whether to import the module after it has been downloaded. .OUTPUTS None. .EXAMPLE Start-PSResourceGetBootstrap -Destination 'C:\Modules' This example bootstraps the Microsoft.PowerShell.PSResourceGet module, saving it to the specified destination path "C:\Modules". .EXAMPLE Start-PSResourceGetBootstrap -Scope 'AllUsers' This example bootstraps the Microsoft.PowerShell.PSResourceGet module, saving it to the appropriate location based on the 'AllUsers' scope. .EXAMPLE Start-PSResourceGetBootstrap -UseCompatibilityModule This example bootstraps the Microsoft.PowerShell.PSResourceGet module, saving it to the appropriate location based on the default scope ('CurrentUser'). It will also save the compatibility module to the same location. #> function Start-PSResourceGetBootstrap { # TODO: Change impact to 'Medium' when the script is stable. [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High', DefaultParameterSetName = 'Scope')] [OutputType()] param ( [Parameter(Mandatory = $true, ParameterSetName = 'Destination')] [ValidateScript({ Write-Verbose -Message "Destination folder is set to '$($_)'." Test-Path -Path $_ -PathType 'Container' })] [System.String] $Destination, [Parameter(ParameterSetName = 'Scope')] [ValidateSet('CurrentUser', 'AllUsers')] [System.String] $Scope = 'CurrentUser', [Parameter()] [ValidateScript({ # From https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string $_ -match '^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$' -or # Need to support the Nuget range syntax as well. $_ -match '^[\[(][0-9\.\,]*[\])]$' })] [System.String] $Version, [Parameter()] [System.Management.Automation.SwitchParameter] $UseCompatibilityModule, [Parameter()] [ValidateScript({ # From https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string $_ -match '^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$' -or # Need to support the Nuget range syntax as well. $_ -match '^[\[(][0-9\.\,]*[\])]$' })] [System.String] $CompatibilityModuleVersion = '[3.0.22,]', [Parameter()] [System.Management.Automation.SwitchParameter] $ImportModule, [Parameter()] [System.Management.Automation.SwitchParameter] $Force ) if ($Force.IsPresent -and -not $Confirm) { $ConfirmPreference = 'None' } $name = 'Microsoft.PowerShell.PSResourceGet' switch ($PSCmdlet.ParameterSetName) { 'Destination' { # Resolve relative path to absolute path. $Destination = Resolve-Path -Path $Destination -ErrorAction 'Stop' $verboseDescriptionMessage = $script:localizedData.Start_PSResourceGetBootstrap_Destination_ShouldProcessVerboseDescription -f $name, $Destination Write-Debug -Message ($script:localizedData.Start_PSResourceGetBootstrap_Destination_SaveModule -f $Destination) } 'Scope' { $verboseDescriptionMessage = $script:localizedData.Start_PSResourceGetBootstrap_Scope_ShouldProcessVerboseDescription -f $name, $Scope $scopeModulePath = Get-PSModulePath -Scope $Scope if (-not (Test-Path -Path $scopeModulePath)) { # cSpell: ignore SPSRGB $exception = New-Exception -Message ($script:localizedData.Start_PSResourceGetBootstrap_MissingScopePath -f $scopeModulePath, $Scope) $errorRecord = New-ErrorRecord -Exception $exception -ErrorId 'SPSRGB0004' -ErrorCategory 'InvalidOperation' -TargetObject $name $PSCmdlet.ThrowTerminatingError($errorRecord) } $Destination = $scopeModulePath Write-Debug -Message ($script:localizedData.Start_PSResourceGetBootstrap_Scope_SaveModule -f $Scope, $Destination) } } $loadedModule = Get-Module -Name $name if ($loadedModule -and $loadedModule.Path -match [System.Text.RegularExpressions.Regex]::Escape($Destination)) { Write-Verbose -Message ($script:localizedData.Start_PSResourceGetBootstrap_AlreadyInUse -f $name) # Since it is loaded into the session, assume it is downloaded and working. $moduleAvailable = $true } else { $verboseWarningMessage = $script:localizedData.Start_PSResourceGetBootstrap_ShouldProcessVerboseWarning -f $name $captionMessage = $script:localizedData.Start_PSResourceGetBootstrap_ShouldProcessCaption -f $name if ($PSCmdlet.ShouldProcess($verboseDescriptionMessage, $verboseWarningMessage, $captionMessage)) { $moduleAvailable = $false try { if (-not $Version) { # Default to latest version if no version is passed in parameter or specified in configuration. $psResourceGetUri = "https://www.powershellgallery.com/api/v2/package/$name" } else { $psResourceGetUri = "https://www.powershellgallery.com/api/v2/package/$name/$Version" } $invokeWebRequestParameters = @{ # TODO: Should support proxy parameters passed to the command. Uri = $psResourceGetUri OutFile = "$Destination/$name.nupkg" # cSpell: ignore nupkg ErrorAction = 'Stop' } Invoke-WebRequest @invokeWebRequestParameters $moduleAvailable = $true } catch { $exception = New-Exception -ErrorRecord $_ -Message ($script:localizedData.Start_PSResourceGetBootstrap_FailedDownload -f $name) $errorRecord = New-ErrorRecord -Exception $exception -ErrorId 'SPSRGB0001' -ErrorCategory 'InvalidOperation' -TargetObject $name $PSCmdlet.ThrowTerminatingError($errorRecord) } if ($moduleAvailable) { try { # On Windows PowerShell the command Expand-Archive do not like .nupkg as a zip archive extension. $zipFileName = ((Split-Path -Path $invokeWebRequestParameters.OutFile -Leaf) -replace 'nupkg', 'zip') $renameItemParameters = @{ Path = $invokeWebRequestParameters.OutFile NewName = $zipFileName Force = $true } Rename-Item @renameItemParameters } catch { # If the rename fails, we should remove the .nupkg file. Remove-Item -Path $invokeWebRequestParameters.OutFile $exception = New-Exception -ErrorRecord $_ -Message ($script:localizedData.Start_PSResourceGetBootstrap_RenamedFailed -f $invokeWebRequestParameters.OutFile, $invokeWebRequestParameters.NewName) $errorRecord = New-ErrorRecord -Exception $exception -ErrorId 'SPSRGB0002' -ErrorCategory 'InvalidOperation' -TargetObject $invokeWebRequestParameters.OutFile $PSCmdlet.ThrowTerminatingError($errorRecord) } try { $zipArchivePath = Join-Path -Path (Split-Path -Path $invokeWebRequestParameters.OutFile -Parent) -ChildPath $zipFileName $expandArchiveParameters = @{ Path = $zipArchivePath DestinationPath = "$Destination/$name" Force = $true } Expand-Archive @expandArchiveParameters } catch { $exception = New-Exception -ErrorRecord $_ -Message ($script:localizedData.Start_PSResourceGetBootstrap_ExpandFailed -f $zipArchivePath) $errorRecord = New-ErrorRecord -Exception $exception -ErrorId 'SPSRGB0003' -ErrorCategory 'InvalidOperation' -TargetObject $zipArchivePath $PSCmdlet.ThrowTerminatingError($errorRecord) } finally { # When the expand succeeds, we should remove the .zip file. Remove-Item -Path $zipArchivePath } if ($ImportModule.IsPresent) { Import-Module -Name $expandArchiveParameters.DestinationPath -Force } } } else { if ((Test-Path -Path (Join-Path -Path $Destination -ChildPath $name))) { # Since it is available in the destination, assume it is downloaded and can work. $moduleAvailable = $true Write-Debug -Message 'Did not bootstrap, but module is available in the destination.' } } } if ($moduleAvailable -and $UseCompatibilityModule.IsPresent) { $name = 'PowerShellGet' $loadedModule = Get-Module -Name $name if ($loadedModule -and $loadedModule.Path -match [System.Text.RegularExpressions.Regex]::Escape($Destination)) { Write-Verbose -Message ($script:localizedData.Start_PSResourceGetBootstrap_AlreadyInUse -f $name) } else { $verboseDescriptionMessage = $script:localizedData.Start_PSResourceGetBootstrap_CompatibilityModule_ShouldProcessVerboseDescription -f $name, $Destination $verboseWarningMessage = $script:localizedData.Start_PSResourceGetBootstrap_CompatibilityModule_ShouldProcessVerboseWarning -f $name $captionMessage = $script:localizedData.Start_PSResourceGetBootstrap_CompatibilityModule_ShouldProcessCaption -f $name if ($PSCmdlet.ShouldProcess($verboseDescriptionMessage, $verboseWarningMessage, $captionMessage)) { $savePowerShellGetParameters = @{ Name = $name Path = $Destination Repository = 'PSGallery' TrustRepository = $true # If not specified, default to a minimum required range that includes previews. Version = $CompatibilityModuleVersion # TODO: Should probably be a switch parameter when there is a full release out. Prerelease = $true } Save-PSResource @savePowerShellGetParameters if ($ImportModule.IsPresent) { Import-Module -Name "$Destination/$name" } } } } } #EndRegion '.\Public\Start-PSResourceGetBootstrap.ps1' 316 |