From 3117518b6d49ddd79fdeb70eb95568c0f05503f0 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 11 Jul 2026 19:23:43 +0200 Subject: [PATCH 1/5] Import the selected GitHub module version and remove other loaded versions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit init.ps1 now resolves the exact installed version satisfying the Version input, removes any already-loaded GitHub module versions from the session, and imports the resolved version with -RequiredVersion -Global — so the user script uses the selected version instead of whatever PowerShell would auto-load from PSModulePath. --- src/init.ps1 | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/init.ps1 b/src/init.ps1 index a937815..f587ae0 100644 --- a/src/init.ps1 +++ b/src/init.ps1 @@ -67,15 +67,31 @@ process { } } + # Resolve the exact installed version that satisfies the request (newest match), so the loaded + # module is deterministic instead of whatever PowerShell would auto-load from PSModulePath. + $resolveParams = @{ + Name = $Name + ErrorAction = 'SilentlyContinue' + } + if ($Version) { + $resolveParams['Version'] = $Version + } + $resolved = Get-InstalledPSResource @resolveParams | Sort-Object Version -Descending | Select-Object -First 1 + if (-not $resolved) { + throw "No installed '$Name' version satisfies the requested version '$Version'." + } + $alreadyImported = Get-Module -Name $Name if ($showInit) { Write-Output 'Already imported:' $alreadyImported | Format-List | Out-String } - if (-not $alreadyImported) { - Write-Verbose "Importing module: $Name" - Import-Module -Name $Name - } + # Remove any already-loaded versions so only the chosen version remains loaded, then import that + # exact version into the global session state so every subsequent command (info.ps1, the user + # script, clean.ps1) uses the selected version. + Remove-Module -Name $Name -Force -ErrorAction SilentlyContinue + Write-Verbose "Importing module: $Name $($resolved.Version)" + Import-Module -Name $Name -RequiredVersion $resolved.Version -Force -Global $providedToken = -not [string]::IsNullOrEmpty($env:PSMODULE_GITHUB_SCRIPT_INPUT_Token) $providedClientID = -not [string]::IsNullOrEmpty($env:PSMODULE_GITHUB_SCRIPT_INPUT_ClientID) From ba5bfaf355daab50436abf06db712b2b03700a7b Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 12 Jul 2026 11:59:55 +0200 Subject: [PATCH 2/5] Address review: filter prereleases when not requested and remove all loaded instances Per PR review: resolution now excludes prerelease versions when Prerelease is false and adds a stable-wins tie-breaker (so a Prerelease:false run never imports an installed prerelease, and stable wins at equal base version); and 'Get-Module -Name \ -All | Remove-Module' removes every loaded instance before import. --- src/init.ps1 | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/init.ps1 b/src/init.ps1 index f587ae0..e12ca7c 100644 --- a/src/init.ps1 +++ b/src/init.ps1 @@ -68,7 +68,9 @@ process { } # Resolve the exact installed version that satisfies the request (newest match), so the loaded - # module is deterministic instead of whatever PowerShell would auto-load from PSModulePath. + # module is deterministic instead of whatever PowerShell would auto-load from PSModulePath. When + # prerelease versions are not requested, never resolve to one; when a stable and a prerelease share + # a base version, the stable one wins. $resolveParams = @{ Name = $Name ErrorAction = 'SilentlyContinue' @@ -76,7 +78,14 @@ process { if ($Version) { $resolveParams['Version'] = $Version } - $resolved = Get-InstalledPSResource @resolveParams | Sort-Object Version -Descending | Select-Object -First 1 + $candidates = @(Get-InstalledPSResource @resolveParams) + if (-not $Prerelease) { + $candidates = @($candidates | Where-Object { -not $_.IsPrerelease }) + } + $resolved = $candidates | Sort-Object -Property @( + @{ Expression = 'Version'; Descending = $true } + @{ Expression = 'IsPrerelease'; Descending = $false } + ) | Select-Object -First 1 if (-not $resolved) { throw "No installed '$Name' version satisfies the requested version '$Version'." } @@ -86,10 +95,10 @@ process { Write-Output 'Already imported:' $alreadyImported | Format-List | Out-String } - # Remove any already-loaded versions so only the chosen version remains loaded, then import that - # exact version into the global session state so every subsequent command (info.ps1, the user - # script, clean.ps1) uses the selected version. - Remove-Module -Name $Name -Force -ErrorAction SilentlyContinue + # Remove every already-loaded instance (all versions, including nested) so only the chosen version + # remains loaded, then import that exact version into the global session state so every subsequent + # command (info.ps1, the user script, clean.ps1) uses the selected version. + Get-Module -Name $Name -All | Remove-Module -Force -ErrorAction SilentlyContinue Write-Verbose "Importing module: $Name $($resolved.Version)" Import-Module -Name $Name -RequiredVersion $resolved.Version -Force -Global From e76e997b8454a4440a512a013f8ce7d167facc73 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 12 Jul 2026 12:20:51 +0200 Subject: [PATCH 3/5] Address review: use Prerelease string, stop on import failure, clearer error Filter/sort on the 'Prerelease' string instead of IsPrerelease; add -ErrorAction Stop to the final Import-Module so a failed load stops the run; and render 'latest' in the throw message when no Version was requested (matching the moduleStatus display). --- src/init.ps1 | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/init.ps1 b/src/init.ps1 index e12ca7c..db26f19 100644 --- a/src/init.ps1 +++ b/src/init.ps1 @@ -70,7 +70,8 @@ process { # Resolve the exact installed version that satisfies the request (newest match), so the loaded # module is deterministic instead of whatever PowerShell would auto-load from PSModulePath. When # prerelease versions are not requested, never resolve to one; when a stable and a prerelease share - # a base version, the stable one wins. + # a base version, the stable one wins. PSResourceGet exposes prerelease state as the 'Prerelease' + # string (empty for stable). $resolveParams = @{ Name = $Name ErrorAction = 'SilentlyContinue' @@ -80,14 +81,15 @@ process { } $candidates = @(Get-InstalledPSResource @resolveParams) if (-not $Prerelease) { - $candidates = @($candidates | Where-Object { -not $_.IsPrerelease }) + $candidates = @($candidates | Where-Object { [string]::IsNullOrWhiteSpace($_.Prerelease) }) } $resolved = $candidates | Sort-Object -Property @( @{ Expression = 'Version'; Descending = $true } - @{ Expression = 'IsPrerelease'; Descending = $false } + @{ Expression = { -not [string]::IsNullOrWhiteSpace($_.Prerelease) }; Descending = $false } ) | Select-Object -First 1 if (-not $resolved) { - throw "No installed '$Name' version satisfies the requested version '$Version'." + $requested = [string]::IsNullOrWhiteSpace($Version) ? 'latest' : $Version + throw "No installed '$Name' version satisfies the requested version '$requested'." } $alreadyImported = Get-Module -Name $Name @@ -100,7 +102,7 @@ process { # command (info.ps1, the user script, clean.ps1) uses the selected version. Get-Module -Name $Name -All | Remove-Module -Force -ErrorAction SilentlyContinue Write-Verbose "Importing module: $Name $($resolved.Version)" - Import-Module -Name $Name -RequiredVersion $resolved.Version -Force -Global + Import-Module -Name $Name -RequiredVersion $resolved.Version -Force -Global -ErrorAction Stop $providedToken = -not [string]::IsNullOrEmpty($env:PSMODULE_GITHUB_SCRIPT_INPUT_Token) $providedClientID = -not [string]::IsNullOrEmpty($env:PSMODULE_GITHUB_SCRIPT_INPUT_ClientID) From a6d0a871877b2a69f70d58e448970c646f3e09a1 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 12 Jul 2026 12:30:53 +0200 Subject: [PATCH 4/5] Address review: do not treat an installed prerelease as satisfying a stable request When Prerelease is false, exclude prereleases from the already-installed check so a machine with only prerelease builds installed still installs a stable version instead of failing resolution. --- src/init.ps1 | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/init.ps1 b/src/init.ps1 index db26f19..653998f 100644 --- a/src/init.ps1 +++ b/src/init.ps1 @@ -33,7 +33,12 @@ process { Write-Verbose "Filtering by version: $Version" $installedParams['Version'] = $Version } - $alreadyInstalled = Get-InstalledPSResource @installedParams + $alreadyInstalled = @(Get-InstalledPSResource @installedParams) + if (-not $Prerelease) { + # A prerelease already on disk must not count as "already installed" for a stable request, + # otherwise installation is skipped and resolution later fails with only a prerelease present. + $alreadyInstalled = @($alreadyInstalled | Where-Object { [string]::IsNullOrWhiteSpace($_.Prerelease) }) + } if ($showInit) { Write-Output 'Already installed:' From 5ad3bebb252fb327eb5487d69228124a30bc8232 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 12 Jul 2026 12:35:59 +0200 Subject: [PATCH 5/5] Address review: keep already-installed status semantics when nothing matches Normalize the already-installed set to \ when the (prerelease-filtered) query returns nothing, so the '-not' install guard and the '\ -ne' moduleStatus check both retain their original meaning instead of an empty array reading as 'installed'. --- src/init.ps1 | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/init.ps1 b/src/init.ps1 index 653998f..02d9e73 100644 --- a/src/init.ps1 +++ b/src/init.ps1 @@ -39,6 +39,11 @@ process { # otherwise installation is skipped and resolution later fails with only a prerelease present. $alreadyInstalled = @($alreadyInstalled | Where-Object { [string]::IsNullOrWhiteSpace($_.Prerelease) }) } + # Normalize to $null when nothing matches so the '-not' install guard and the '$null -ne' status + # check below both keep their original semantics (an empty array is not $null). + if ($alreadyInstalled.Count -eq 0) { + $alreadyInstalled = $null + } if ($showInit) { Write-Output 'Already installed:'