diff --git a/.github/workflows/TestWorkflow.yml b/.github/workflows/TestWorkflow.yml index 20735c1..49d4a41 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 it's 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 it's a local action + - name: Checkout repo + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false + # 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: + Version: '[0.38.0, 0.40.0)' + Prerelease: ${{ inputs.Prerelease }} + ShowInit: true + ShowRateLimit: true + Script: | + $loaded = (Get-Module -Name GitHub).Version + 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" + + 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 it's 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 it's 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 + # 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.38.0, ]' + Prerelease: ${{ inputs.Prerelease }} + ShowInit: true + ShowRateLimit: true + Script: | + $installed = @(Get-InstalledPSResource -Name GitHub) + if ($installed.Count -ne 1) { + throw "Expected exactly 1 installed GitHub version (0.40.0 reused, no extra install), but found $($installed.Count): $(($installed.Version) -join ', ')." + } + 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 (no reinstall)" + ActionTestWithScript: name: WithScript runs-on: ${{ inputs.runs-on }} 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. 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:'