From f0cf99dc8c79c52a65c77aa2ae07d7f0c7f6ab54 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Thu, 9 Jul 2026 01:20:03 +0200 Subject: [PATCH 1/6] Add version-input tests for exact, range, and already-installed cases --- .github/workflows/TestWorkflow.yml | 114 +++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) diff --git a/.github/workflows/TestWorkflow.yml b/.github/workflows/TestWorkflow.yml index 20735c1..4459f52 100644 --- a/.github/workflows/TestWorkflow.yml +++ b/.github/workflows/TestWorkflow.yml @@ -73,6 +73,120 @@ jobs: ShowInit: true ShowRateLimit: true + ActionTestVersionExact: + name: Version [Exact] + # Version resolution is OS-independent, so these logic tests run once (on Linux) to keep CI lean. + if: ${{ inputs.runs-on == 'ubuntu-latest' }} + runs-on: ${{ inputs.runs-on }} + steps: + # Need to check out as part of the test, as its a local action + - name: Checkout repo + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false + # A bare version must resolve to that EXACT version (not "minimum inclusive"), which + # guarantees backward compatibility for callers that pin an exact version today. + - name: Action-Test [exact version] + uses: ./ + with: + Version: '0.40.0' + Prerelease: ${{ inputs.Prerelease }} + ShowInit: true + ShowRateLimit: true + Script: | + $loaded = (Get-Module -Name GitHub).Version + if ("$loaded" -ne '0.40.0') { + throw "Expected the GitHub module 0.40.0 to be loaded, but found '$loaded'." + } + Write-Host "OK: exact version resolved to $loaded" + + ActionTestVersionRange: + name: Version [Bounded range] + if: ${{ inputs.runs-on == 'ubuntu-latest' }} + runs-on: ${{ inputs.runs-on }} + steps: + # Need to check out as part of the test, as its a local action + - name: Checkout repo + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false + # A bounded range must resolve to the highest satisfying version: [0.38.0, 0.40.0) -> 0.39.0. + - name: Action-Test [bounded range] + uses: ./ + with: + Version: '[0.38.0, 0.40.0)' + Prerelease: ${{ inputs.Prerelease }} + ShowInit: true + ShowRateLimit: true + Script: | + $loaded = (Get-Module -Name GitHub).Version + if ($loaded -ne [version]'0.39.0') { + throw "Expected '[0.38.0, 0.40.0)' to resolve to 0.39.0, but found '$loaded'." + } + Write-Host "OK: bounded range resolved to $loaded" + + ActionTestVersionMinimum: + name: Version [Minimum range] + if: ${{ inputs.runs-on == 'ubuntu-latest' }} + runs-on: ${{ inputs.runs-on }} + steps: + # Need to check out as part of the test, as its a local action + - name: Checkout repo + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false + # A minimum-inclusive range must resolve to a version >= the lower bound. + - name: Action-Test [minimum range] + uses: ./ + with: + Version: '[0.40.0, ]' + Prerelease: ${{ inputs.Prerelease }} + ShowInit: true + ShowRateLimit: true + Script: | + $loaded = (Get-Module -Name GitHub).Version + if ($loaded -lt [version]'0.40.0') { + throw "Expected '[0.40.0, ]' to resolve to >= 0.40.0, but found '$loaded'." + } + Write-Host "OK: minimum range resolved to $loaded" + + ActionTestVersionAlreadyInstalled: + name: Version [Already installed] + if: ${{ inputs.runs-on == 'ubuntu-latest' }} + runs-on: ${{ inputs.runs-on }} + steps: + # Need to check out as part of the test, as its a local action + - name: Checkout repo + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false + # Pre-install a version that satisfies the range requested below. + - name: Pre-install GitHub 0.40.0 + shell: pwsh + run: | + Install-PSResource -Name GitHub -Version '0.40.0' -Repository PSGallery -TrustRepository -Reinstall + # When an installed version already satisfies the requested range, the action must reuse it + # and must not install another copy. The exact-string already-installed check never matches a + # range, so it reinstalls the latest (leaving two installed versions); the range-aware check + # keeps exactly one. + - name: Action-Test [already-installed range] + uses: ./ + with: + Version: '[0.40.0, ]' + Prerelease: ${{ inputs.Prerelease }} + ShowInit: true + ShowRateLimit: true + Script: | + $installed = @(Get-InstalledPSResource -Name GitHub) + $loaded = (Get-Module -Name GitHub).Version + if ($installed.Count -ne 1) { + throw "Expected exactly 1 installed GitHub version (satisfying version reused, no reinstall), but found $($installed.Count): $(($installed.Version) -join ', ')." + } + if ("$loaded" -ne '0.40.0') { + throw "Expected the already-installed 0.40.0 to be reused, but found '$loaded'." + } + Write-Host "OK: already-installed satisfying version reused ($loaded)" + ActionTestWithScript: name: WithScript runs-on: ${{ inputs.runs-on }} From a7d2bccf33fb137a6a92ffbeb87dc8e5d69ff001 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Thu, 9 Jul 2026 01:24:35 +0200 Subject: [PATCH 2/6] Align version tests with PSResourceGet lowest-in-range resolution --- .github/workflows/TestWorkflow.yml | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/.github/workflows/TestWorkflow.yml b/.github/workflows/TestWorkflow.yml index 4459f52..e532d60 100644 --- a/.github/workflows/TestWorkflow.yml +++ b/.github/workflows/TestWorkflow.yml @@ -120,8 +120,8 @@ jobs: ShowRateLimit: true Script: | $loaded = (Get-Module -Name GitHub).Version - if ($loaded -ne [version]'0.39.0') { - throw "Expected '[0.38.0, 0.40.0)' to resolve to 0.39.0, but found '$loaded'." + if ($loaded -lt [version]'0.38.0' -or $loaded -ge [version]'0.40.0') { + throw "Expected '[0.38.0, 0.40.0)' to resolve within [0.38.0, 0.40.0), but found '$loaded'." } Write-Host "OK: bounded range resolved to $loaded" @@ -165,27 +165,26 @@ jobs: shell: pwsh run: | Install-PSResource -Name GitHub -Version '0.40.0' -Repository PSGallery -TrustRepository -Reinstall - # When an installed version already satisfies the requested range, the action must reuse it - # and must not install another copy. The exact-string already-installed check never matches a - # range, so it reinstalls the latest (leaving two installed versions); the range-aware check - # keeps exactly one. + # 0.40.0 already satisfies the requested range, so the action must REUSE it and must not + # install another copy. The exact-string already-installed check never matches a range, so it + # reinstalls the range floor (0.38.0) side-by-side, leaving two installed versions; the + # range-aware check keeps exactly one. - name: Action-Test [already-installed range] uses: ./ with: - Version: '[0.40.0, ]' + Version: '[0.38.0, ]' Prerelease: ${{ inputs.Prerelease }} ShowInit: true ShowRateLimit: true Script: | $installed = @(Get-InstalledPSResource -Name GitHub) - $loaded = (Get-Module -Name GitHub).Version if ($installed.Count -ne 1) { - throw "Expected exactly 1 installed GitHub version (satisfying version reused, no reinstall), but found $($installed.Count): $(($installed.Version) -join ', ')." + throw "Expected exactly 1 installed GitHub version (0.40.0 reused, no extra install), but found $($installed.Count): $(($installed.Version) -join ', ')." } - if ("$loaded" -ne '0.40.0') { - throw "Expected the already-installed 0.40.0 to be reused, but found '$loaded'." + if ("$($installed.Version)" -ne '0.40.0') { + throw "Expected the retained version to be 0.40.0, but found '$($installed.Version)'." } - Write-Host "OK: already-installed satisfying version reused ($loaded)" + Write-Host "OK: already-installed satisfying version reused (no reinstall)" ActionTestWithScript: name: WithScript From ecde6a7a7a53ecc085b415c6e8b6c48e05b299d6 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Thu, 9 Jul 2026 01:26:48 +0200 Subject: [PATCH 3/6] Detect already-installed versions using NuGet range matching --- src/init.ps1 | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/init.ps1 b/src/init.ps1 index 76663fd..a937815 100644 --- a/src/init.ps1 +++ b/src/init.ps1 @@ -23,15 +23,17 @@ process { $Version = [string]::IsNullOrEmpty($env:PSMODULE_GITHUB_SCRIPT_INPUT_Version) ? $null : $env:PSMODULE_GITHUB_SCRIPT_INPUT_Version $Prerelease = $env:PSMODULE_GITHUB_SCRIPT_INPUT_Prerelease -eq 'true' - $alreadyInstalled = Get-InstalledPSResource -Name $Name -ErrorAction SilentlyContinue + $installedParams = @{ + Name = $Name + ErrorAction = 'SilentlyContinue' + } if ($Version) { + # Version accepts an exact version or a NuGet version range. Let PSResourceGet resolve + # range satisfaction instead of comparing the raw value with an exact string match. Write-Verbose "Filtering by version: $Version" - $alreadyInstalled = $alreadyInstalled | Where-Object Version -EQ $Version - } - if ($Prerelease) { - Write-Verbose 'Filtering by prerelease' - $alreadyInstalled = $alreadyInstalled | Where-Object Prerelease -EQ $Prerelease + $installedParams['Version'] = $Version } + $alreadyInstalled = Get-InstalledPSResource @installedParams if ($showInit) { Write-Output 'Already installed:' From e974072c99fab85b2e53200edbe9a3165262a1a8 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Thu, 9 Jul 2026 01:33:50 +0200 Subject: [PATCH 4/6] Document NuGet version range support for the Version input --- README.md | 13 ++++++++++++- action.yml | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0739b01..30ee6b7 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ To get started with your own GitHub PowerShell based action, [create a new repos | `KeyVaultKeyReference` | Log in using a GitHub App, with the App's Client ID and KeyVault Key Reference. | false | | | `Debug` | Enable debug output for the whole action. | false | `'false'` | | `Verbose` | Enable verbose output for the whole action. | false | `'false'` | -| `Version` | Specifies the exact version of the GitHub module to install. | false | | +| `Version` | The version or NuGet version range of the module to install. | false | | | `Prerelease` | Allow prerelease versions if available. | false | `'false'` | | `ErrorView` | Configure the PowerShell `$ErrorView` variable. You can use full names ('NormalView', 'CategoryView', 'ConciseView', 'DetailedView'). It matches on partials. | false | `'NormalView'` | | `ShowInfo` | Show information about the environment. | false | `'true'` | @@ -28,6 +28,17 @@ To get started with your own GitHub PowerShell based action, [create a new repos | `WorkingDirectory` | The working directory where the script runs. | false | `'.'` | | `PreserveCredentials` | Preserve credentials after script execution. If false, disconnects GitHub contexts and CLI using Disconnect-GitHubAccount. | false | `'true'` | +> [!NOTE] +> The `Version` input accepts an exact version or a [NuGet version range](https://learn.microsoft.com/nuget/concepts/package-versioning#version-ranges), the same syntax used by [`Install-PSResource`](https://learn.microsoft.com/powershell/module/microsoft.powershell.psresourceget/install-psresource). A bare version such as `1.2.3` is treated as an *exact* version rather than a minimum, so pinning a single version keeps working unchanged. A version that is already installed and satisfies the request is reused instead of being reinstalled. + +| `Version` example | Meaning | +|-------------------|-----------------------------------------| +| `1.2.3` | Exactly `1.2.3` | +| `[1.2.3]` | Exactly `1.2.3` | +| `[1.2.0, ]` | `1.2.0` or newer | +| `(, 2.0.0)` | Any version lower than `2.0.0` | +| `[1.2.0, 2.0.0)` | `1.2.0` up to but not including `2.0.0` | + ### Outputs | Name | Description | diff --git a/action.yml b/action.yml index 9733fc6..6077126 100644 --- a/action.yml +++ b/action.yml @@ -35,7 +35,7 @@ inputs: required: false default: 'false' Version: - description: Specifies the version of the GitHub module to be installed. The value must be an exact version. + description: Specifies the version of the GitHub module to install. Accepts an exact version or a NuGet version range (for example '[1.2.0, 2.0.0)'). required: false Prerelease: description: Allow prerelease versions if available. From 201fdb590a73a2623ecc213babcf969b37bf3986 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Thu, 9 Jul 2026 01:45:39 +0200 Subject: [PATCH 5/6] Correct bounded-range test comment to match lowest-in-range resolution --- .github/workflows/TestWorkflow.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/TestWorkflow.yml b/.github/workflows/TestWorkflow.yml index e532d60..8ea01cf 100644 --- a/.github/workflows/TestWorkflow.yml +++ b/.github/workflows/TestWorkflow.yml @@ -110,7 +110,8 @@ jobs: uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - # A bounded range must resolve to the highest satisfying version: [0.38.0, 0.40.0) -> 0.39.0. + # A bounded range must resolve to a version inside the range. PSResourceGet resolves a range to + # the lowest satisfying version: [0.38.0, 0.40.0) -> 0.38.0. - name: Action-Test [bounded range] uses: ./ with: From 349952c20a548087f4927fb568e6aca542d2cb36 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Thu, 9 Jul 2026 01:50:58 +0200 Subject: [PATCH 6/6] Fix grammar in version job checkout comments --- .github/workflows/TestWorkflow.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/TestWorkflow.yml b/.github/workflows/TestWorkflow.yml index 8ea01cf..49d4a41 100644 --- a/.github/workflows/TestWorkflow.yml +++ b/.github/workflows/TestWorkflow.yml @@ -79,7 +79,7 @@ jobs: if: ${{ inputs.runs-on == 'ubuntu-latest' }} runs-on: ${{ inputs.runs-on }} steps: - # Need to check out as part of the test, as its a local action + # Need to check out as part of the test, as it's a local action - name: Checkout repo uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: @@ -105,7 +105,7 @@ jobs: if: ${{ inputs.runs-on == 'ubuntu-latest' }} runs-on: ${{ inputs.runs-on }} steps: - # Need to check out as part of the test, as its a local action + # Need to check out as part of the test, as it's a local action - name: Checkout repo uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: @@ -131,7 +131,7 @@ jobs: if: ${{ inputs.runs-on == 'ubuntu-latest' }} runs-on: ${{ inputs.runs-on }} steps: - # Need to check out as part of the test, as its a local action + # Need to check out as part of the test, as it's a local action - name: Checkout repo uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: @@ -156,7 +156,7 @@ jobs: if: ${{ inputs.runs-on == 'ubuntu-latest' }} runs-on: ${{ inputs.runs-on }} steps: - # Need to check out as part of the test, as its a local action + # Need to check out as part of the test, as it's a local action - name: Checkout repo uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: