Skip to content

Commit 8083ec1

Browse files
🚀 [Feature]: Version input accepts NuGet version ranges (#98)
The `Version` input now accepts a NuGet version range in addition to an exact version, so a workflow can pin a compatible window (for example `[1.2.0, 2.0.0)`) instead of a single build. Pinning an exact version keeps working exactly as before, and a version that is already installed and satisfies the request is no longer reinstalled on every run. - Fixes #97 ## New: NuGet version ranges for the `Version` input `Version` accepts the same syntax as [`Install-PSResource`](https://learn.microsoft.com/powershell/module/microsoft.powershell.psresourceget/install-psresource): | `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` | A bare version is treated as an *exact* version (not a minimum), so existing pins are unaffected. PSResourceGet resolves a range to the lowest satisfying version. ## Fixed: a satisfying version is reused instead of reinstalled Previously, supplying anything other than an exact version caused the module to be reinstalled on every run: the already-installed check compared the raw input to installed versions with exact string equality, which never matches a range. The check now honors ranges, so an already-installed version that satisfies the request is reused. ## Technical Details - `src/init.ps1`: the already-installed lookup now passes the value to `Get-InstalledPSResource -Name $Name -Version $Version` instead of piping to `Where-Object Version -EQ`. The redundant `Where-Object Prerelease -EQ` filter was removed — prerelease handling is governed by the `-Prerelease` switch passed to `Install-PSResource`. The retry loop (5 attempts, 10s delay) around `Install-PSResource` is unchanged. - `action.yml` / `README.md`: the `Version` input is documented as accepting an exact version or a NuGet version range, with an examples table and a note that a bare version is exact. - Tests (`.github/workflows/TestWorkflow.yml`): added `Version [Exact]`, `Version [Bounded range]`, `Version [Minimum range]`, and `Version [Already installed]` jobs (Linux; version resolution is OS-independent). Written test-first — the already-installed job failed on the unfixed code (two installed versions) and passes after the fix. Because these jobs use `Prerelease: ${{ inputs.Prerelease }}`, they also exercise the prerelease + range combination when run via `Action-Test-Prerelease.yml`. - Implementation plan progress: all tasks in #97 are complete. Backward compatibility: exact-version pins resolve identically; only the unnecessary-reinstall behavior changes.
1 parent 918892d commit 8083ec1

4 files changed

Lines changed: 135 additions & 8 deletions

File tree

.github/workflows/TestWorkflow.yml

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,120 @@ jobs:
7373
ShowInit: true
7474
ShowRateLimit: true
7575

76+
ActionTestVersionExact:
77+
name: Version [Exact]
78+
# Version resolution is OS-independent, so these logic tests run once (on Linux) to keep CI lean.
79+
if: ${{ inputs.runs-on == 'ubuntu-latest' }}
80+
runs-on: ${{ inputs.runs-on }}
81+
steps:
82+
# Need to check out as part of the test, as it's a local action
83+
- name: Checkout repo
84+
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
85+
with:
86+
persist-credentials: false
87+
# A bare version must resolve to that EXACT version (not "minimum inclusive"), which
88+
# guarantees backward compatibility for callers that pin an exact version today.
89+
- name: Action-Test [exact version]
90+
uses: ./
91+
with:
92+
Version: '0.40.0'
93+
Prerelease: ${{ inputs.Prerelease }}
94+
ShowInit: true
95+
ShowRateLimit: true
96+
Script: |
97+
$loaded = (Get-Module -Name GitHub).Version
98+
if ("$loaded" -ne '0.40.0') {
99+
throw "Expected the GitHub module 0.40.0 to be loaded, but found '$loaded'."
100+
}
101+
Write-Host "OK: exact version resolved to $loaded"
102+
103+
ActionTestVersionRange:
104+
name: Version [Bounded range]
105+
if: ${{ inputs.runs-on == 'ubuntu-latest' }}
106+
runs-on: ${{ inputs.runs-on }}
107+
steps:
108+
# Need to check out as part of the test, as it's a local action
109+
- name: Checkout repo
110+
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
111+
with:
112+
persist-credentials: false
113+
# A bounded range must resolve to a version inside the range. PSResourceGet resolves a range to
114+
# the lowest satisfying version: [0.38.0, 0.40.0) -> 0.38.0.
115+
- name: Action-Test [bounded range]
116+
uses: ./
117+
with:
118+
Version: '[0.38.0, 0.40.0)'
119+
Prerelease: ${{ inputs.Prerelease }}
120+
ShowInit: true
121+
ShowRateLimit: true
122+
Script: |
123+
$loaded = (Get-Module -Name GitHub).Version
124+
if ($loaded -lt [version]'0.38.0' -or $loaded -ge [version]'0.40.0') {
125+
throw "Expected '[0.38.0, 0.40.0)' to resolve within [0.38.0, 0.40.0), but found '$loaded'."
126+
}
127+
Write-Host "OK: bounded range resolved to $loaded"
128+
129+
ActionTestVersionMinimum:
130+
name: Version [Minimum range]
131+
if: ${{ inputs.runs-on == 'ubuntu-latest' }}
132+
runs-on: ${{ inputs.runs-on }}
133+
steps:
134+
# Need to check out as part of the test, as it's a local action
135+
- name: Checkout repo
136+
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
137+
with:
138+
persist-credentials: false
139+
# A minimum-inclusive range must resolve to a version >= the lower bound.
140+
- name: Action-Test [minimum range]
141+
uses: ./
142+
with:
143+
Version: '[0.40.0, ]'
144+
Prerelease: ${{ inputs.Prerelease }}
145+
ShowInit: true
146+
ShowRateLimit: true
147+
Script: |
148+
$loaded = (Get-Module -Name GitHub).Version
149+
if ($loaded -lt [version]'0.40.0') {
150+
throw "Expected '[0.40.0, ]' to resolve to >= 0.40.0, but found '$loaded'."
151+
}
152+
Write-Host "OK: minimum range resolved to $loaded"
153+
154+
ActionTestVersionAlreadyInstalled:
155+
name: Version [Already installed]
156+
if: ${{ inputs.runs-on == 'ubuntu-latest' }}
157+
runs-on: ${{ inputs.runs-on }}
158+
steps:
159+
# Need to check out as part of the test, as it's a local action
160+
- name: Checkout repo
161+
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
162+
with:
163+
persist-credentials: false
164+
# Pre-install a version that satisfies the range requested below.
165+
- name: Pre-install GitHub 0.40.0
166+
shell: pwsh
167+
run: |
168+
Install-PSResource -Name GitHub -Version '0.40.0' -Repository PSGallery -TrustRepository -Reinstall
169+
# 0.40.0 already satisfies the requested range, so the action must REUSE it and must not
170+
# install another copy. The exact-string already-installed check never matches a range, so it
171+
# reinstalls the range floor (0.38.0) side-by-side, leaving two installed versions; the
172+
# range-aware check keeps exactly one.
173+
- name: Action-Test [already-installed range]
174+
uses: ./
175+
with:
176+
Version: '[0.38.0, ]'
177+
Prerelease: ${{ inputs.Prerelease }}
178+
ShowInit: true
179+
ShowRateLimit: true
180+
Script: |
181+
$installed = @(Get-InstalledPSResource -Name GitHub)
182+
if ($installed.Count -ne 1) {
183+
throw "Expected exactly 1 installed GitHub version (0.40.0 reused, no extra install), but found $($installed.Count): $(($installed.Version) -join ', ')."
184+
}
185+
if ("$($installed.Version)" -ne '0.40.0') {
186+
throw "Expected the retained version to be 0.40.0, but found '$($installed.Version)'."
187+
}
188+
Write-Host "OK: already-installed satisfying version reused (no reinstall)"
189+
76190
ActionTestWithScript:
77191
name: WithScript
78192
runs-on: ${{ inputs.runs-on }}

README.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ To get started with your own GitHub PowerShell based action, [create a new repos
1818
| `KeyVaultKeyReference` | Log in using a GitHub App, with the App's Client ID and KeyVault Key Reference. | false | |
1919
| `Debug` | Enable debug output for the whole action. | false | `'false'` |
2020
| `Verbose` | Enable verbose output for the whole action. | false | `'false'` |
21-
| `Version` | Specifies the exact version of the GitHub module to install. | false | |
21+
| `Version` | The version or NuGet version range of the module to install. | false | |
2222
| `Prerelease` | Allow prerelease versions if available. | false | `'false'` |
2323
| `ErrorView` | Configure the PowerShell `$ErrorView` variable. You can use full names ('NormalView', 'CategoryView', 'ConciseView', 'DetailedView'). It matches on partials. | false | `'NormalView'` |
2424
| `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
2828
| `WorkingDirectory` | The working directory where the script runs. | false | `'.'` |
2929
| `PreserveCredentials` | Preserve credentials after script execution. If false, disconnects GitHub contexts and CLI using Disconnect-GitHubAccount. | false | `'true'` |
3030

31+
> [!NOTE]
32+
> 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.
33+
34+
| `Version` example | Meaning |
35+
|-------------------|-----------------------------------------|
36+
| `1.2.3` | Exactly `1.2.3` |
37+
| `[1.2.3]` | Exactly `1.2.3` |
38+
| `[1.2.0, ]` | `1.2.0` or newer |
39+
| `(, 2.0.0)` | Any version lower than `2.0.0` |
40+
| `[1.2.0, 2.0.0)` | `1.2.0` up to but not including `2.0.0` |
41+
3142
### Outputs
3243

3344
| Name | Description |

action.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ inputs:
3535
required: false
3636
default: 'false'
3737
Version:
38-
description: Specifies the version of the GitHub module to be installed. The value must be an exact version.
38+
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)').
3939
required: false
4040
Prerelease:
4141
description: Allow prerelease versions if available.

src/init.ps1

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,17 @@ process {
2323
$Version = [string]::IsNullOrEmpty($env:PSMODULE_GITHUB_SCRIPT_INPUT_Version) ? $null : $env:PSMODULE_GITHUB_SCRIPT_INPUT_Version
2424
$Prerelease = $env:PSMODULE_GITHUB_SCRIPT_INPUT_Prerelease -eq 'true'
2525

26-
$alreadyInstalled = Get-InstalledPSResource -Name $Name -ErrorAction SilentlyContinue
26+
$installedParams = @{
27+
Name = $Name
28+
ErrorAction = 'SilentlyContinue'
29+
}
2730
if ($Version) {
31+
# Version accepts an exact version or a NuGet version range. Let PSResourceGet resolve
32+
# range satisfaction instead of comparing the raw value with an exact string match.
2833
Write-Verbose "Filtering by version: $Version"
29-
$alreadyInstalled = $alreadyInstalled | Where-Object Version -EQ $Version
30-
}
31-
if ($Prerelease) {
32-
Write-Verbose 'Filtering by prerelease'
33-
$alreadyInstalled = $alreadyInstalled | Where-Object Prerelease -EQ $Prerelease
34+
$installedParams['Version'] = $Version
3435
}
36+
$alreadyInstalled = Get-InstalledPSResource @installedParams
3537

3638
if ($showInit) {
3739
Write-Output 'Already installed:'

0 commit comments

Comments
 (0)