From efddbfe181f6d9704fb63efda88021686352dd0f Mon Sep 17 00:00:00 2001 From: Friedrich Weinmann Date: Sun, 19 Jan 2025 18:33:57 +0100 Subject: [PATCH 1/2] fixes and finetuning --- PSFramework.NuGet/changelog.md | 8 ++++ .../PowerShellGet/Get-PSFPowerShellGet.ps1 | 2 +- .../Get/Publish-StagingModuleRemote.ps1 | 1 + .../functions/Get/Save-StagingModule.ps1 | 8 ++-- .../internal/moduleScopes/AllUsers.scope.ps1 | 6 ++- .../xml/PSFramework.NuGet.Format.ps1xml | 39 +++++++++++++++++++ 6 files changed, 59 insertions(+), 5 deletions(-) diff --git a/PSFramework.NuGet/changelog.md b/PSFramework.NuGet/changelog.md index 21d94f8..17aa7e0 100644 --- a/PSFramework.NuGet/changelog.md +++ b/PSFramework.NuGet/changelog.md @@ -1,5 +1,13 @@ # Changelog +## ??? + ++ Upd: Updated to include PSResourceGet 1.1.0 ++ Upd: Get-PSFPowerShellGet - improved result display style ++ Fix: Install-PSFModule - returns unexpected object when successfully installing, but not from the first priority repository ++ Fix: Install-PSFModule - progress bar does not show number of deployments in progress. ++ Fix: Scope: AllUsers - fails to install to AllUsers when no module has been installed there yet. + ## 0.9.2 (2025-01-17) + Fix: Get-PSFRepository - writes error when searching for a specific repository that only exists in one PSGet version diff --git a/PSFramework.NuGet/functions/PowerShellGet/Get-PSFPowerShellGet.ps1 b/PSFramework.NuGet/functions/PowerShellGet/Get-PSFPowerShellGet.ps1 index f7b8620..aa75b14 100644 --- a/PSFramework.NuGet/functions/PowerShellGet/Get-PSFPowerShellGet.ps1 +++ b/PSFramework.NuGet/functions/PowerShellGet/Get-PSFPowerShellGet.ps1 @@ -50,7 +50,7 @@ } [PSCustomObject]@{ - PSTypeName = 'PSFramework.NuGet.GetReport' + PSTypeName = 'PSFramework.NuGet.GetReport' ComputerName = $env:COMPUTERNAME V2 = ($modules | Where-Object { $_.Version.Major -lt 3 }) -as [bool] V3 = $modulesV3 -as [bool] diff --git a/PSFramework.NuGet/internal/functions/Get/Publish-StagingModuleRemote.ps1 b/PSFramework.NuGet/internal/functions/Get/Publish-StagingModuleRemote.ps1 index c2e4231..dd04bd5 100644 --- a/PSFramework.NuGet/internal/functions/Get/Publish-StagingModuleRemote.ps1 +++ b/PSFramework.NuGet/internal/functions/Get/Publish-StagingModuleRemote.ps1 @@ -60,6 +60,7 @@ $PSDefaultParameterValues['Write-PSFMessage:ModuleName'] = 'PSFramework.NuGet' $PSDefaultParameterValues['Write-PSFMessage:FunctionName'] = 'Publish-StagingModule' + $__PSF_Workflow.Data.InProgress[$TargetPath.ComputerName] = $true #region Functions function Get-GlobalFailResult { diff --git a/PSFramework.NuGet/internal/functions/Get/Save-StagingModule.ps1 b/PSFramework.NuGet/internal/functions/Get/Save-StagingModule.ps1 index 3336e64..98d5c5e 100644 --- a/PSFramework.NuGet/internal/functions/Get/Save-StagingModule.ps1 +++ b/PSFramework.NuGet/internal/functions/Get/Save-StagingModule.ps1 @@ -104,6 +104,7 @@ if ($Repository.Credential) { $callSpecifics.Credential = $Repository.Credential } $result = [PSCustomObject]@{ + PSTypeName = 'PSFramework.NuGet.DownloadResult' Success = $false Error = $null ModuleName = $Item.Name @@ -203,8 +204,8 @@ Write-PSFMessage -String 'Save-StagingModule.SavingV3.Start' -StringValues $Item.Name, $Item.Version, $Repository.Name, $Repository.Type -Target $Item $callSpecifics = @{ - ErrorAction = 'Stop' - Repository = $Repository.Name + ErrorAction = 'Stop' + Repository = $Repository.Name } if ((Get-Command Save-PSResource).Parameters.Keys -contains 'AcceptLicense') { $callSpecifics.AcceptLicense = $true @@ -214,6 +215,7 @@ if ($SkipDependency) { $callSpecifics.SkipDependencyCheck = $true } $result = [PSCustomObject]@{ + PSTypeName = 'PSFramework.NuGet.DownloadResult' Success = $false Error = $null ModuleName = $Item.Name @@ -282,7 +284,7 @@ } } process { - :item foreach ($installItem in $InstallData) { + $null = :item foreach ($installItem in $InstallData) { $saveResults = foreach ($repository in $Repositories | Set-PSFObjectOrder -Property Priority, '>Type') { $saveResult = switch ($repository.Type) { V2 { Save-StagingModuleV2 -Repository $repository -Item $installItem @common } diff --git a/PSFramework.NuGet/internal/moduleScopes/AllUsers.scope.ps1 b/PSFramework.NuGet/internal/moduleScopes/AllUsers.scope.ps1 index da8651c..a970d58 100644 --- a/PSFramework.NuGet/internal/moduleScopes/AllUsers.scope.ps1 +++ b/PSFramework.NuGet/internal/moduleScopes/AllUsers.scope.ps1 @@ -3,7 +3,11 @@ return "$([Environment]::GetFolderPath("ProgramFiles"))\WindowsPowerShell\Modules" } if ($IsWindows) { - return "$([Environment]::GetFolderPath("ProgramFiles"))\PowerShell\Modules" + $path = "$([Environment]::GetFolderPath("ProgramFiles"))\PowerShell\Modules" + if (-not (Test-Path -Path $path)) { + $null = New-Item -Path $path -ItemType Directory -Force -ErrorAction Ignore + } + return $path } '/usr/local/share/powershell/Modules' } diff --git a/PSFramework.NuGet/xml/PSFramework.NuGet.Format.ps1xml b/PSFramework.NuGet/xml/PSFramework.NuGet.Format.ps1xml index 2d83f83..f630813 100644 --- a/PSFramework.NuGet/xml/PSFramework.NuGet.Format.ps1xml +++ b/PSFramework.NuGet/xml/PSFramework.NuGet.Format.ps1xml @@ -1,6 +1,45 @@  + + + PSFramework.NuGet.GetReport + + PSFramework.NuGet.GetReport + + + + + + + + + + + + + + + ComputerName + + + V2 + + + V3 + + + V2CanInstall + + + V2CanPublish + + + + + + + PSFramework.NuGet.ModuleInfo From 1eb4d3baeebb3579aca34e8a6c47890ef949caf0 Mon Sep 17 00:00:00 2001 From: Friedrich Weinmann Date: Mon, 5 May 2025 08:46:35 +0200 Subject: [PATCH 2/2] updates --- PSFramework.NuGet/PSFramework.NuGet.psd1 | 2 +- PSFramework.NuGet/changelog.md | 6 +- .../internal/configurations/configuration.ps1 | 3 +- .../Get/Publish-StagingModuleLocal.ps1 | 3 +- .../internal/scripts/initialize.ps1 | 13 ++- bootstrap.ps1 | 101 ++++++++++++++++++ 6 files changed, 123 insertions(+), 5 deletions(-) create mode 100644 bootstrap.ps1 diff --git a/PSFramework.NuGet/PSFramework.NuGet.psd1 b/PSFramework.NuGet/PSFramework.NuGet.psd1 index dd2c564..a9db56b 100644 --- a/PSFramework.NuGet/PSFramework.NuGet.psd1 +++ b/PSFramework.NuGet/PSFramework.NuGet.psd1 @@ -3,7 +3,7 @@ RootModule = 'PSFramework.NuGet.psm1' # Version number of this module. - ModuleVersion = '0.9.2' + ModuleVersion = '0.9.11' # ID used to uniquely identify this module GUID = 'ad0f2a25-552f-4dd6-bd8e-5ddced2a5d88' diff --git a/PSFramework.NuGet/changelog.md b/PSFramework.NuGet/changelog.md index 17aa7e0..fca375d 100644 --- a/PSFramework.NuGet/changelog.md +++ b/PSFramework.NuGet/changelog.md @@ -1,11 +1,15 @@ # Changelog -## ??? +## 0.9.11 (2025-05-05) ++ New: Bootstrap script to deploy PSFramework.NuGet to the local computer without requiring Package Management. ++ New: Module automatically deploys the NuGet provider to the user profile on module import, to simplify the PowerShellGet experience + Upd: Updated to include PSResourceGet 1.1.0 + Upd: Get-PSFPowerShellGet - improved result display style ++ Fix: Install-PSFModule - fails to detect already existing version of module and attempts to overwrite + Fix: Install-PSFModule - returns unexpected object when successfully installing, but not from the first priority repository + Fix: Install-PSFModule - progress bar does not show number of deployments in progress. ++ Fix: Save-PSFModule - fails to detect already existing version of module and attempts to overwrite + Fix: Scope: AllUsers - fails to install to AllUsers when no module has been installed there yet. ## 0.9.2 (2025-01-17) diff --git a/PSFramework.NuGet/internal/configurations/configuration.ps1 b/PSFramework.NuGet/internal/configurations/configuration.ps1 index 7ce6c66..64b887d 100644 --- a/PSFramework.NuGet/internal/configurations/configuration.ps1 +++ b/PSFramework.NuGet/internal/configurations/configuration.ps1 @@ -16,4 +16,5 @@ Set-PSFConfig -Module 'PSFramework.NuGet' -Name 'Import.IndividualFiles' -Value Set-PSFConfig -Module 'PSFramework.NuGet' -Name 'Install.AuthenticodeSignature.Check' -Value $false -Initialize -Validation 'bool' -Description 'Whether on installation or download of module its code-signing will be checked first.' Set-PSFConfig -Module 'PSFramework.NuGet' -Name 'Remoting.DefaultConfiguration' -Value 'Microsoft.PowerShell' -Initialize -Validation string -Description 'The PSSessionConfiguration to use when initializing new PS remoting sessions' -Set-PSFConfig -Module 'PSFramework.NuGet' -Name 'Remoting.Throttling' -Value 5 -Initialize -Validation integerpositive -Description 'Up to how many remote computers to deploy to in parallel.' \ No newline at end of file +Set-PSFConfig -Module 'PSFramework.NuGet' -Name 'Remoting.Throttling' -Value 5 -Initialize -Validation integerpositive -Description 'Up to how many remote computers to deploy to in parallel.' +Set-PSFConfig -Module 'PSFramework.NuGet' -Name 'LocalBootstrap' -Value $true -Initialize -Validation bool -Description 'Whether PSGetV2 gets automatically bootstrapped on module import. This happens by default to improve user experience with an unmodified PowerShell that have never had to deal with PSGet. Also to simplify the experience in an offline environment.' \ No newline at end of file diff --git a/PSFramework.NuGet/internal/functions/Get/Publish-StagingModuleLocal.ps1 b/PSFramework.NuGet/internal/functions/Get/Publish-StagingModuleLocal.ps1 index 9e1ce2e..c0192af 100644 --- a/PSFramework.NuGet/internal/functions/Get/Publish-StagingModuleLocal.ps1 +++ b/PSFramework.NuGet/internal/functions/Get/Publish-StagingModuleLocal.ps1 @@ -57,10 +57,11 @@ $publishCommon.Module = $module.Name $publishCommon.Version = $version.Name - $testPath = Join-Path -Path $destination.Path -ChildPath "$($module.Name)/$($version.Name)/$($module.DirectoryName).psd1" + $testPath = Join-Path -Path $destination.Path -ChildPath "$($module.Name)/$($version.Name)/$($module.Name).psd1" $alreadyExists = Test-Path -Path $testPath if ($alreadyExists -and -not $Force) { Write-PSFMessage @msgParam -String 'Publish-StagingModule.Skipping.AlreadyExists' -StringValues $module.Name, $version.Name, $destination.Path + New-PublishResult @publishCommon -Success $true -Message 'Module already deployed' continue } diff --git a/PSFramework.NuGet/internal/scripts/initialize.ps1 b/PSFramework.NuGet/internal/scripts/initialize.ps1 index 506ccd3..0a033b0 100644 --- a/PSFramework.NuGet/internal/scripts/initialize.ps1 +++ b/PSFramework.NuGet/internal/scripts/initialize.ps1 @@ -2,4 +2,15 @@ Search-PSFPowerShellGet # Ensure all configured repositories exist, and all unintended repositories are gone -Update-PSFRepository \ No newline at end of file +Update-PSFRepository + +# Auto-Bootstrap Local GetV2 on Windows +if ( + ($PSVersionTable.PSVersion.Major -lt 5 -or $IsWindows) -and + $env:LOCALAPPDATA -and + (-not (Test-Path "$env:LOCALAPPDATA\PackageManagement\ProviderAssemblies\nuget\2.8.5.208\Microsoft.PackageManagement.NuGetProvider.dll")) -and + (Get-PSFConfigValue -FullName 'PSFramework.NuGet.LocalBootstrap') +) { + $null = New-Item -Path "$env:LOCALAPPDATA\PackageManagement\ProviderAssemblies\nuget\2.8.5.208" -ItemType Directory -Force -ErrorAction SilentlyContinue + Copy-Item -Path "$script:ModuleRoot\bin\Microsoft.PackageManagement.NuGetProvider.dll" -Destination "$env:LOCALAPPDATA\PackageManagement\ProviderAssemblies\nuget\2.8.5.208" -ErrorAction SilentlyContinue +} \ No newline at end of file diff --git a/bootstrap.ps1 b/bootstrap.ps1 new file mode 100644 index 0000000..395d979 --- /dev/null +++ b/bootstrap.ps1 @@ -0,0 +1,101 @@ +<# +.SYNOPSIS + Installs all that is needed to run PSFramework.NuGet without using the PowerShellGet tools. + +.DESCRIPTION + Installs all that is needed to run PSFramework.NuGet without using the PowerShellGet tools. + +.EXAMPLE + PS C:\> .\bootstrap.ps1 + + Installs all that is needed to run PSFramework.NuGet without using the PowerShellGet tools. + +.EXAMPLE + PS C:\> Iwr https://raw.githubusercontent.com/PowershellFrameworkCollective/PSFramework.NuGet/refs/heads/master/bootstrap.ps1 | iex + + This one-liner will download the script into memory and then execute it directly, enabling PowerShell package management on the local computer. +#> +[CmdletBinding()] +param ( + +) + +$ErrorActionPreference = 'Stop' +trap { + Write-Warning "Script failed: $_" + throw $_ +} + +#region Functions +function Find-GalleryModule { + [CmdletBinding()] + param ( + [string[]] + $Name + ) + + foreach ($moduleName in $Name) { + $page = Invoke-WebRequest "https://www.powershellgallery.com/packages/$moduleName" -UseBasicParsing + $versions = $page.Links | Where-Object href -Match "^/packages/$moduleName/\d+(\.\d+){1,3}$" + foreach ($version in $versions) { + $null = $version.href -match '/(\d+(\.\d+){1,3})$' + Add-Member -InputObject $version -MemberType NoteProperty -Name Version -Value ($matches.1 -as [version]) -Force + } + + $latest = $versions | Sort-Object Version -Descending | Select-Object -First 1 + + [PSCustomObject]@{ + Name = $moduleName + Version = $latest.Version + Link = 'https://psg-prod-eastus.azureedge.net/packages/{0}.{1}.nupkg' -f $moduleName.ToLower(), $latest.Version + } + } +} + +function Install-GalleryModule { + [CmdletBinding()] + param ( + $Module + ) + + Write-Host "Installing: $($Module.Name) ($($Module.Version))" + + trap { + Write-Host " Failed: $_" -ForegroundColor Red -BackgroundColor Black + return + } + + # Resolve target path and skip if not needed + $modulePath = $env:PSModulePath -split ';' | Where-Object { $_ -match '\\Documents\\' } | Select-Object -First 1 + if (-not $modulePath) { $modulePath = $env:PSModulePath -split ';' | Select-Object -First 1 } + $moduleRoot = Join-Path -Path $modulePath -ChildPath "$($Module.Name)/$($Module.Version)" + if (Test-Path -Path $moduleRoot) { + Write-Host " $($Module.Name) already installed, skipping" + return + } + + $ProgressPreference = 'SilentlyContinue' + $staging = New-Item -Path $env:TEMP -Name "PSMS-$(Get-Random)" -ItemType Directory + Invoke-WebRequest -Uri $Module.Link -OutFile "$($staging.FullName)\$($Module.Name).zip" -ErrorAction Stop + Expand-Archive -Path "$($staging.FullName)\$($Module.Name).zip" -DestinationPath $staging.FullName -ErrorAction Stop + + # Remove undesired parts + Remove-Item -Path "$($staging.FullName)\$($Module.Name).zip" + Remove-Item -Path "$($staging.FullName)\$($Module.Name).nuspec" + Remove-Item -LiteralPath "$($staging.FullName)\[Content_Types].xml" + Remove-Item -Path "$($staging.FullName)\_rels" -Recurse -Force + Remove-Item -Path "$($staging.FullName)\package" -Recurse -Force + + # Deploy to Documents + $null = New-Item -Path $moduleRoot -ItemType Directory -Force + Move-Item -Path "$($staging.FullName)\*" -Destination $moduleRoot -Force -ErrorAction Stop + Write-Host " Successfully completed" -ForegroundColor Green -BackgroundColor Black + + Remove-Item -Path $staging -ErrorAction SilentlyContinue -Force -Recurse +} +#endregion Functions + +$modules = Find-GalleryModule -Name PSFramework, ConvertToPSD1, PSFramework.NuGet +foreach ($module in $modules) { + Install-GalleryModule -Module $module +} \ No newline at end of file