From 4a23a7eadaaf4a01c7c601b410df7fdfbf63ec40 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Tue, 17 Feb 2026 10:45:25 +0100 Subject: [PATCH 01/13] =?UTF-8?q?=F0=9F=9A=80=20[Feature]:=20Support=20ins?= =?UTF-8?q?talling=20prerelease=20PowerShell=20versions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Update action input description to document prerelease version support - Fix Windows downgrade detection to handle prerelease version strings by stripping the prerelease suffix before casting to [version] - Fix Windows uninstall registry filter to match Preview display names when uninstalling a preview build - Add prerelease version (7.6.0-preview.6) to test workflow matrix - Update test verification to detect preview installs on Windows (preview builds install to a separate directory) - Update README with prerelease documentation and usage example Closes #15 --- .github/workflows/Action-Test.yml | 19 ++++++++++++++++--- README.md | 15 +++++++++++++-- action.yml | 18 +++++++++++++----- 3 files changed, 42 insertions(+), 10 deletions(-) diff --git a/.github/workflows/Action-Test.yml b/.github/workflows/Action-Test.yml index b0238f2..8b76407 100644 --- a/.github/workflows/Action-Test.yml +++ b/.github/workflows/Action-Test.yml @@ -22,7 +22,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, windows-latest, macOS-latest] - version: ['latest', '7.4.7', '7.5.0'] + version: ['latest', '7.4.7', '7.5.0', '7.6.0-preview.6'] runs-on: ${{ matrix.os }} name: '${{ matrix.os }} - [${{ matrix.version }}]' steps: @@ -59,8 +59,21 @@ jobs: Write-Host "Resolved 'latest' → $requested" } - # Actual version installed by the action - $installed = ($PSVersionTable.PSVersion).ToString() + # For prerelease versions on Windows, launch the installed pwsh to get the version + # because the current shell may not be the version we just installed. + $isPrerelease = $requested -match '-' + if ($IsWindows -and $isPrerelease) { + $majorVersion = ($requested -split '[\.-]')[0] + $installDir = "$majorVersion-preview" + $pwshPath = "$env:ProgramFiles\PowerShell\$installDir\pwsh.exe" + if (Test-Path $pwshPath) { + $installed = (& $pwshPath -NoLogo -NoProfile -Command '$PSVersionTable.PSVersion.ToString()') + } else { + $installed = ($PSVersionTable.PSVersion).ToString() + } + } else { + $installed = ($PSVersionTable.PSVersion).ToString() + } Write-Host "Installed PowerShell version: $installed" Write-Host "Expected PowerShell version: $requested" diff --git a/README.md b/README.md index 2447d9a..722045e 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ A cross‑platform GitHub Action that installs a specific **PowerShell Core** version—or the latest stable release—on any GitHub‑hosted runner (Linux, macOS, or Windows). The action automatically skips installation when the requested version is already present. +Prerelease versions (e.g. `7.6.0-preview.6`, `7.5.0-rc.1`) are also supported. ## Usage @@ -25,11 +26,20 @@ jobs: Write-Host "Using PowerShell $($PSVersionTable.PSVersion)" ``` +### Installing a prerelease version + +```yaml + - name: Install PowerShell Preview + uses: PSModule/install-powershell@v1 + with: + Version: 7.6.0-preview.6 +``` + ## Inputs | Input | Required | Default | Description | | ----- | -------- | ------- | ----------- | -| `Version` | `false` | `latest` | Desired PowerShell Core version (e.g. `7.4.1`). Use `latest` to install the newest stable release. | +| `Version` | `false` | `latest` | Desired PowerShell Core version (e.g. `7.4.1`, `7.6.0-preview.6`). Use `latest` to install the newest stable release. Prerelease versions are supported. | ## Secrets @@ -43,7 +53,8 @@ This action does **not** generate any outputs. * **Version resolution** If `Version` is set to `latest` (case‑insensitive), the action queries the GitHub API for the newest stable release tag in the - `PowerShell/PowerShell` repository and substitutes that version. + `PowerShell/PowerShell` repository and substitutes that version. Prerelease version strings (e.g. `7.6.0-preview.6`) are passed + through directly. * **Skip logic** Before installing, the action checks the current runner to see whether the requested version is already available diff --git a/action.yml b/action.yml index b79cc2d..02fea8d 100644 --- a/action.yml +++ b/action.yml @@ -11,8 +11,9 @@ branding: inputs: Version: description: | - PowerShell version to install (e.g. `7.4.1`). + PowerShell version to install (e.g. `7.4.1` or `7.6.0-preview.6`). Defaults to install the latest stable release. + Prerelease versions are supported (e.g. `7.6.0-preview.6`, `7.5.0-rc.1`). required: false default: 'latest' @@ -198,14 +199,20 @@ runs: } # Downgrade detection + # Strip prerelease suffix for [version] comparison (e.g. '7.6.0-preview.6' → '7.6.0') $isDowngrade = $false if ($detected -and $detected -ne $env:REQUESTED_VERSION) { try { - $detectedVersion = [version]$detected - $requestedVersion = [version]$env:REQUESTED_VERSION + $detectedBase = ($detected -split '-')[0] + $requestedBase = ($env:REQUESTED_VERSION -split '-')[0] + $detectedVersion = [version]$detectedBase + $requestedVersion = [version]$requestedBase if ($detectedVersion -gt $requestedVersion) { Write-Host "Downgrade detected: $detected → $($env:REQUESTED_VERSION)" $isDowngrade = $true + } elseif ($detectedVersion -eq $requestedVersion -and $detected -ne $env:REQUESTED_VERSION) { + # Same base version but different prerelease label, treat as a reinstall + Write-Host "Version change detected (same base, different label): $detected → $($env:REQUESTED_VERSION)" } else { Write-Host "Upgrade detected: $detected → $($env:REQUESTED_VERSION)" } @@ -224,13 +231,14 @@ runs: 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*' ) + $isDetectedPreview = $detected -match '-preview|\-rc' $pwshEntries = Get-ItemProperty -Path $regPaths -ErrorAction SilentlyContinue | Where-Object { $_.Publisher -eq 'Microsoft Corporation' -and $_.DisplayName -like 'PowerShell 7*' -and - $_.DisplayName -notlike '*Preview*' -and + $(if ($isDetectedPreview) { $_.DisplayName -like '*Preview*' } else { $_.DisplayName -notlike '*Preview*' }) -and $_.DisplayVersion -and - $_.DisplayVersion.StartsWith($detected) + $_.DisplayVersion.StartsWith(($detected -split '-')[0]) } $targetEntry = $pwshEntries | Select-Object -First 1 From eb32a654d6c3b581c7c85e0c1163d09df8c24cce Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Tue, 17 Feb 2026 11:08:42 +0100 Subject: [PATCH 02/13] Add Prerelease input for latest prerelease resolution - Add 'Prerelease' boolean input (default: false), similar to Install-PSResource's -Prerelease switch - When Prerelease is true and Version is 'latest', resolve to the latest prerelease from the PowerShell/PowerShell GitHub releases API - Explicit prerelease version strings (e.g. 7.6.0-preview.6) continue to work directly without the Prerelease flag - Add prerelease resolution to all 3 platform steps (Linux bash, macOS bash, Windows PowerShell 5.1) - Update test workflow with 'prerelease' matrix entry - Update README with Prerelease input docs and latest prerelease example --- .github/workflows/Action-Test.yml | 18 +++++- README.md | 16 +++++- action.yml | 93 ++++++++++++++++++++++--------- 3 files changed, 95 insertions(+), 32 deletions(-) diff --git a/.github/workflows/Action-Test.yml b/.github/workflows/Action-Test.yml index 8b76407..0478566 100644 --- a/.github/workflows/Action-Test.yml +++ b/.github/workflows/Action-Test.yml @@ -22,7 +22,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, windows-latest, macOS-latest] - version: ['latest', '7.4.7', '7.5.0', '7.6.0-preview.6'] + version: ['latest', 'prerelease', '7.4.7', '7.5.0', '7.6.0-preview.6'] runs-on: ${{ matrix.os }} name: '${{ matrix.os }} - [${{ matrix.version }}]' steps: @@ -34,7 +34,8 @@ jobs: - name: Action-Test uses: ./ with: - Version: ${{ matrix.version }} + Version: ${{ matrix.version == 'prerelease' && 'latest' || matrix.version }} + Prerelease: ${{ matrix.version == 'prerelease' && 'true' || 'false' }} - name: Verify installed version shell: pwsh @@ -44,8 +45,19 @@ jobs: # Requested version that came from the matrix $requested = '${{ matrix.version }}' + # When 'prerelease' → resolve to latest prerelease + if ($requested.Trim().ToLower() -eq 'prerelease') { + $releases = Invoke-RestMethod -Uri 'https://api.github.com/repos/PowerShell/PowerShell/releases' ` + -Headers @{ + 'Accept' = 'application/vnd.github+json' + 'Authorization' = "Bearer $($env:GITHUB_TOKEN)" + 'X-GitHub-Api-Version' = '2022-11-28' + } + $requested = ($releases | Where-Object { $_.prerelease -eq $true } | Select-Object -First 1).tag_name.TrimStart('v') + Write-Host "Resolved 'prerelease' → $requested" + } # When empty / 'null' / 'latest' → resolve to latest stable release - if ([string]::IsNullOrWhiteSpace($requested) -or + elseif ([string]::IsNullOrWhiteSpace($requested) -or $requested.Trim().ToLower() -in @('latest','null')) { $requested = ( diff --git a/README.md b/README.md index 722045e..106f70a 100644 --- a/README.md +++ b/README.md @@ -35,11 +35,21 @@ jobs: Version: 7.6.0-preview.6 ``` +### Installing the latest prerelease + +```yaml + - name: Install latest PowerShell Preview + uses: PSModule/install-powershell@v1 + with: + Prerelease: true +``` + ## Inputs | Input | Required | Default | Description | | ----- | -------- | ------- | ----------- | -| `Version` | `false` | `latest` | Desired PowerShell Core version (e.g. `7.4.1`, `7.6.0-preview.6`). Use `latest` to install the newest stable release. Prerelease versions are supported. | +| `Version` | `false` | `latest` | Desired PowerShell Core version (e.g. `7.4.1`, `7.6.0-preview.6`). Use `latest` to install the newest stable release (or newest prerelease when `Prerelease` is `true`). | +| `Prerelease` | `false` | `false` | Install a prerelease version. When `true` and `Version` is `latest`, resolves to the latest prerelease. Similar to `-Prerelease` on `Install-PSResource`. | ## Secrets @@ -53,8 +63,8 @@ This action does **not** generate any outputs. * **Version resolution** If `Version` is set to `latest` (case‑insensitive), the action queries the GitHub API for the newest stable release tag in the - `PowerShell/PowerShell` repository and substitutes that version. Prerelease version strings (e.g. `7.6.0-preview.6`) are passed - through directly. + `PowerShell/PowerShell` repository and substitutes that version. When `Prerelease` is `true`, it queries for the latest prerelease + instead. Explicit prerelease version strings (e.g. `7.6.0-preview.6`) are passed through directly. * **Skip logic** Before installing, the action checks the current runner to see whether the requested version is already available diff --git a/action.yml b/action.yml index 02fea8d..2e55499 100644 --- a/action.yml +++ b/action.yml @@ -16,6 +16,13 @@ inputs: Prerelease versions are supported (e.g. `7.6.0-preview.6`, `7.5.0-rc.1`). required: false default: 'latest' + Prerelease: + description: | + Install a prerelease version of PowerShell. + When `true` and `Version` is `latest`, installs the latest prerelease. + Similar to the `-Prerelease` switch on `Install-PSResource`. + required: false + default: 'false' runs: using: composite @@ -26,24 +33,38 @@ runs: working-directory: ${{ github.action_path }} env: REQUESTED_VERSION: ${{ inputs.Version }} + PRERELEASE: ${{ inputs.Prerelease }} GITHUB_TOKEN: ${{ github.token }} run: | # Install-PowerShell set -e echo "Requested version: [$REQUESTED_VERSION]" + echo "Prerelease: [$PRERELEASE]" # Only resolve to latest version if explicitly set to 'latest' (case-insensitive) case "${REQUESTED_VERSION:-}" in [Ll][Aa][Tt][Ee][Ss][Tt]) - REQUESTED_VERSION=$( - curl -s -f \ - -H "Accept: application/vnd.github+json" \ - -H "Authorization: Bearer $GITHUB_TOKEN" \ - -H "X-GitHub-Api-Version: 2022-11-28" \ - https://api.github.com/repos/PowerShell/PowerShell/releases/latest | - jq -r '.tag_name' | sed 's/^v//' - ) - echo "Latest stable PowerShell release detected: $REQUESTED_VERSION" + if [[ "$PRERELEASE" == "true" ]]; then + REQUESTED_VERSION=$( + curl -s -f \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer $GITHUB_TOKEN" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + https://api.github.com/repos/PowerShell/PowerShell/releases | + jq -r '[.[] | select(.prerelease == true)] | .[0].tag_name' | sed 's/^v//' + ) + echo "Latest prerelease PowerShell version detected: $REQUESTED_VERSION" + else + REQUESTED_VERSION=$( + curl -s -f \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer $GITHUB_TOKEN" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + https://api.github.com/repos/PowerShell/PowerShell/releases/latest | + jq -r '.tag_name' | sed 's/^v//' + ) + echo "Latest stable PowerShell release detected: $REQUESTED_VERSION" + fi ;; "") echo "Error: Version input is required (or use 'latest')" @@ -100,24 +121,38 @@ runs: working-directory: ${{ github.action_path }} env: REQUESTED_VERSION: ${{ inputs.Version }} + PRERELEASE: ${{ inputs.Prerelease }} GITHUB_TOKEN: ${{ github.token }} run: | # Install-PowerShell set -e echo "Requested version: [$REQUESTED_VERSION]" + echo "Prerelease: [$PRERELEASE]" # Only resolve to latest version if explicitly set to 'latest' (case-insensitive) case "${REQUESTED_VERSION:-}" in [Ll][Aa][Tt][Ee][Ss][Tt]) - REQUESTED_VERSION=$( - curl -s -f \ - -H "Accept: application/vnd.github+json" \ - -H "Authorization: Bearer $GITHUB_TOKEN" \ - -H "X-GitHub-Api-Version: 2022-11-28" \ - https://api.github.com/repos/PowerShell/PowerShell/releases/latest | - jq -r '.tag_name' | sed 's/^v//' - ) - echo "Latest stable PowerShell release detected: $REQUESTED_VERSION" + if [[ "$PRERELEASE" == "true" ]]; then + REQUESTED_VERSION=$( + curl -s -f \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer $GITHUB_TOKEN" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + https://api.github.com/repos/PowerShell/PowerShell/releases | + jq -r '[.[] | select(.prerelease == true)] | .[0].tag_name' | sed 's/^v//' + ) + echo "Latest prerelease PowerShell version detected: $REQUESTED_VERSION" + else + REQUESTED_VERSION=$( + curl -s -f \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer $GITHUB_TOKEN" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + https://api.github.com/repos/PowerShell/PowerShell/releases/latest | + jq -r '.tag_name' | sed 's/^v//' + ) + echo "Latest stable PowerShell release detected: $REQUESTED_VERSION" + fi ;; "") echo "Error: Version input is required (or use 'latest')" @@ -161,23 +196,29 @@ runs: working-directory: ${{ github.action_path }} env: REQUESTED_VERSION: ${{ inputs.Version }} + PRERELEASE: ${{ inputs.Prerelease }} GITHUB_TOKEN: ${{ github.token }} run: | # Install-PowerShell Write-Host "Requested version: [$env:REQUESTED_VERSION]" + Write-Host "Prerelease: [$env:PRERELEASE]" # Resolve 'latest' → concrete version $req = $env:REQUESTED_VERSION if ($req -and $req.Trim().ToLower() -eq 'latest') { - $latest = ( - Invoke-RestMethod -Uri 'https://api.github.com/repos/PowerShell/PowerShell/releases/latest' ` - -Headers @{ - 'Accept' = 'application/vnd.github+json' - 'Authorization' = "Bearer $($env:GITHUB_TOKEN)" + $headers = @{ + 'Accept' = 'application/vnd.github+json' + 'Authorization' = "Bearer $($env:GITHUB_TOKEN)" 'X-GitHub-Api-Version' = '2022-11-28' - } - ).tag_name.TrimStart('v') - Write-Host "Latest stable PowerShell release detected: $latest" + } + if ($env:PRERELEASE -eq 'true') { + $releases = Invoke-RestMethod -Uri 'https://api.github.com/repos/PowerShell/PowerShell/releases' -Headers $headers + $latest = ($releases | Where-Object { $_.prerelease -eq $true } | Select-Object -First 1).tag_name.TrimStart('v') + Write-Host "Latest prerelease PowerShell version detected: $latest" + } else { + $latest = (Invoke-RestMethod -Uri 'https://api.github.com/repos/PowerShell/PowerShell/releases/latest' -Headers $headers).tag_name.TrimStart('v') + Write-Host "Latest stable PowerShell release detected: $latest" + } $env:REQUESTED_VERSION = $latest } elseif ([string]::IsNullOrWhiteSpace($req)) { Write-Host "Error: Version input is required (or use 'latest')" From 8688e56143a80f4d0800fe4c6b56e5f4640eebce Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Tue, 17 Feb 2026 11:14:07 +0100 Subject: [PATCH 03/13] fix: Always verify via subprocess on Windows and prepend install dir to GITHUB_PATH After MSI install on Windows, the running shell may still resolve the pre-installed pwsh from PATH. Two fixes: 1. action.yml: Append the install directory to GITHUB_PATH so that subsequent 'shell: pwsh' steps in the consumer's workflow resolve to the version we just installed (critical for preview builds whose 7-preview dir is not on the runner's default PATH, but also defensive for stable upgrades). 2. Action-Test.yml: Verification step now always launches pwsh from the known install directory on Windows (both stable and prerelease), instead of relying on $PSVersionTable from the current shell. --- .github/workflows/Action-Test.yml | 13 ++++++++----- action.yml | 16 ++++++++++++++++ 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/.github/workflows/Action-Test.yml b/.github/workflows/Action-Test.yml index 0478566..67b9701 100644 --- a/.github/workflows/Action-Test.yml +++ b/.github/workflows/Action-Test.yml @@ -71,16 +71,19 @@ jobs: Write-Host "Resolved 'latest' → $requested" } - # For prerelease versions on Windows, launch the installed pwsh to get the version - # because the current shell may not be the version we just installed. - $isPrerelease = $requested -match '-' - if ($IsWindows -and $isPrerelease) { + # On Windows, always verify by launching pwsh from the known install directory. + # This avoids relying on PATH resolution, which may still point to the pre-installed + # version if the runner's environment hasn't refreshed after the MSI install. + if ($IsWindows) { + $isPrerelease = $requested -match '-' $majorVersion = ($requested -split '[\.-]')[0] - $installDir = "$majorVersion-preview" + $installDir = if ($isPrerelease) { "$majorVersion-preview" } else { $majorVersion } $pwshPath = "$env:ProgramFiles\PowerShell\$installDir\pwsh.exe" + Write-Host "Windows: verifying via subprocess at $pwshPath" if (Test-Path $pwshPath) { $installed = (& $pwshPath -NoLogo -NoProfile -Command '$PSVersionTable.PSVersion.ToString()') } else { + Write-Host "Warning: Expected pwsh not found at $pwshPath, falling back to `$PSVersionTable" $installed = ($PSVersionTable.PSVersion).ToString() } } else { diff --git a/action.yml b/action.yml index 2e55499..1681da6 100644 --- a/action.yml +++ b/action.yml @@ -332,3 +332,19 @@ runs: } Write-Host "Installation complete. PowerShell [$($env:REQUESTED_VERSION)] is now available." + + # Add the install directory to GITHUB_PATH so subsequent `shell: pwsh` steps + # resolve to the version we just installed — even for preview builds whose + # install directory (7-preview) is not on the runner's default PATH. + $isPrerelease = $env:REQUESTED_VERSION -match '-' + $installDir = if ($isPrerelease) { + "$env:ProgramFiles\PowerShell\7-preview" + } else { + "$env:ProgramFiles\PowerShell\7" + } + if (Test-Path $installDir) { + Write-Host "Adding install directory to GITHUB_PATH: $installDir" + Add-Content -Path $env:GITHUB_PATH -Value $installDir + } else { + Write-Host "Warning: Expected install directory not found: $installDir" + } From e29a085572f6267d54b07eb83a7d8fa112428508 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Tue, 17 Feb 2026 11:35:46 +0100 Subject: [PATCH 04/13] fix: address all Copilot review comments on PR #16 - Add null/empty checks for prerelease resolution on all 3 platforms (Linux jq // empty + bash validation, macOS same, Windows null check) - Fix prerelease downgrade detection: force uninstall when same base version but different prerelease labels (MSI can't handle in-place) - Fix inconsistent regex escaping: -preview|-rc (not -preview|\-rc) - Fix StartsWith over-matching in registry filter: use regex with delimiter-aware boundary instead of string prefix match - Extract major version dynamically for install path (not hardcoded 7) - Add GITHUB_PATH updates for Linux/macOS preview builds - Add null check in test workflow prerelease resolution --- .github/workflows/Action-Test.yml | 6 +++- action.yml | 56 ++++++++++++++++++++++++++----- 2 files changed, 52 insertions(+), 10 deletions(-) diff --git a/.github/workflows/Action-Test.yml b/.github/workflows/Action-Test.yml index 67b9701..0aa09b5 100644 --- a/.github/workflows/Action-Test.yml +++ b/.github/workflows/Action-Test.yml @@ -53,7 +53,11 @@ jobs: 'Authorization' = "Bearer $($env:GITHUB_TOKEN)" 'X-GitHub-Api-Version' = '2022-11-28' } - $requested = ($releases | Where-Object { $_.prerelease -eq $true } | Select-Object -First 1).tag_name.TrimStart('v') + $latestPrerelease = $releases | Where-Object { $_.prerelease -eq $true } | Select-Object -First 1 + if (-not $latestPrerelease) { + throw "No prerelease releases found for PowerShell/PowerShell." + } + $requested = $latestPrerelease.tag_name.TrimStart('v') Write-Host "Resolved 'prerelease' → $requested" } # When empty / 'null' / 'latest' → resolve to latest stable release diff --git a/action.yml b/action.yml index 1681da6..67f65b2 100644 --- a/action.yml +++ b/action.yml @@ -51,8 +51,12 @@ runs: -H "Authorization: Bearer $GITHUB_TOKEN" \ -H "X-GitHub-Api-Version: 2022-11-28" \ https://api.github.com/repos/PowerShell/PowerShell/releases | - jq -r '[.[] | select(.prerelease == true)] | .[0].tag_name' | sed 's/^v//' + jq -r '[.[] | select(.prerelease == true)] | (.[0].tag_name // empty)' | sed 's/^v//' ) + if [[ -z "$REQUESTED_VERSION" ]]; then + echo "Error: No prerelease PowerShell releases found when resolving latest prerelease." + exit 1 + fi echo "Latest prerelease PowerShell version detected: $REQUESTED_VERSION" else REQUESTED_VERSION=$( @@ -115,6 +119,17 @@ runs: fi echo "Installation complete. PowerShell [$REQUESTED_VERSION] is now available." + # For prerelease builds, add the install directory to GITHUB_PATH so subsequent + # `shell: pwsh` steps resolve to the version we just installed. + if [[ "$REQUESTED_VERSION" == *-* ]]; then + MAJOR_VERSION=$(echo "$REQUESTED_VERSION" | cut -d'.' -f1) + INSTALL_DIR="/opt/microsoft/powershell/${MAJOR_VERSION}-preview" + if [[ -d "$INSTALL_DIR" ]]; then + echo "Adding install directory to GITHUB_PATH: $INSTALL_DIR" + echo "$INSTALL_DIR" >> "$GITHUB_PATH" + fi + fi + - name: Install PowerShell (macOS) if: runner.os == 'macOS' shell: bash @@ -139,8 +154,12 @@ runs: -H "Authorization: Bearer $GITHUB_TOKEN" \ -H "X-GitHub-Api-Version: 2022-11-28" \ https://api.github.com/repos/PowerShell/PowerShell/releases | - jq -r '[.[] | select(.prerelease == true)] | .[0].tag_name' | sed 's/^v//' + jq -r '[.[] | select(.prerelease == true)] | (.[0].tag_name // empty)' | sed 's/^v//' ) + if [[ -z "$REQUESTED_VERSION" ]]; then + echo "Error: No prerelease PowerShell releases found when resolving latest prerelease." + exit 1 + fi echo "Latest prerelease PowerShell version detected: $REQUESTED_VERSION" else REQUESTED_VERSION=$( @@ -190,6 +209,17 @@ runs: sudo installer -pkg "$PKG_NAME" -target / echo "Installation complete. PowerShell [$REQUESTED_VERSION] is now available." + # For prerelease builds, add the install directory to GITHUB_PATH so subsequent + # `shell: pwsh` steps resolve to the version we just installed. + if [[ "$REQUESTED_VERSION" == *-* ]]; then + MAJOR_VERSION=$(echo "$REQUESTED_VERSION" | cut -d'.' -f1) + INSTALL_DIR="/usr/local/microsoft/powershell/${MAJOR_VERSION}-preview" + if [[ -d "$INSTALL_DIR" ]]; then + echo "Adding install directory to GITHUB_PATH: $INSTALL_DIR" + echo "$INSTALL_DIR" >> "$GITHUB_PATH" + fi + fi + - name: Install PowerShell (Windows) if: runner.os == 'Windows' shell: powershell @@ -213,7 +243,12 @@ runs: } if ($env:PRERELEASE -eq 'true') { $releases = Invoke-RestMethod -Uri 'https://api.github.com/repos/PowerShell/PowerShell/releases' -Headers $headers - $latest = ($releases | Where-Object { $_.prerelease -eq $true } | Select-Object -First 1).tag_name.TrimStart('v') + $latestRelease = $releases | Where-Object { $_.prerelease -eq $true } | Select-Object -First 1 + if (-not $latestRelease) { + Write-Host "Error: No prerelease PowerShell releases are available from GitHub." + exit 1 + } + $latest = $latestRelease.tag_name.TrimStart('v') Write-Host "Latest prerelease PowerShell version detected: $latest" } else { $latest = (Invoke-RestMethod -Uri 'https://api.github.com/repos/PowerShell/PowerShell/releases/latest' -Headers $headers).tag_name.TrimStart('v') @@ -252,8 +287,10 @@ runs: Write-Host "Downgrade detected: $detected → $($env:REQUESTED_VERSION)" $isDowngrade = $true } elseif ($detectedVersion -eq $requestedVersion -and $detected -ne $env:REQUESTED_VERSION) { - # Same base version but different prerelease label, treat as a reinstall - Write-Host "Version change detected (same base, different label): $detected → $($env:REQUESTED_VERSION)" + # Same base version but different prerelease label — MSI installers cannot + # handle cross-prerelease changes in-place, so force uninstall first. + Write-Host "Prerelease version change detected (same base, different label): $detected → $($env:REQUESTED_VERSION)" + $isDowngrade = $true } else { Write-Host "Upgrade detected: $detected → $($env:REQUESTED_VERSION)" } @@ -272,14 +309,14 @@ runs: 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*' ) - $isDetectedPreview = $detected -match '-preview|\-rc' + $isDetectedPreview = $detected -match '-preview|-rc' $pwshEntries = Get-ItemProperty -Path $regPaths -ErrorAction SilentlyContinue | Where-Object { $_.Publisher -eq 'Microsoft Corporation' -and $_.DisplayName -like 'PowerShell 7*' -and $(if ($isDetectedPreview) { $_.DisplayName -like '*Preview*' } else { $_.DisplayName -notlike '*Preview*' }) -and $_.DisplayVersion -and - $_.DisplayVersion.StartsWith(($detected -split '-')[0]) + ($_.DisplayVersion -match "^$([regex]::Escape(($detected -split '-')[0]))([.\-]|$)" -or $_.DisplayVersion -eq ($detected -split '-')[0]) } $targetEntry = $pwshEntries | Select-Object -First 1 @@ -337,10 +374,11 @@ runs: # resolve to the version we just installed — even for preview builds whose # install directory (7-preview) is not on the runner's default PATH. $isPrerelease = $env:REQUESTED_VERSION -match '-' + $majorVersion = ($env:REQUESTED_VERSION -split '[.\-]')[0] $installDir = if ($isPrerelease) { - "$env:ProgramFiles\PowerShell\7-preview" + "$env:ProgramFiles\PowerShell\$majorVersion-preview" } else { - "$env:ProgramFiles\PowerShell\7" + "$env:ProgramFiles\PowerShell\$majorVersion" } if (Test-Path $installDir) { Write-Host "Adding install directory to GITHUB_PATH: $installDir" From e12999820ac952869b6bcdfe3b0bbd033aeea08d Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Tue, 17 Feb 2026 11:41:26 +0100 Subject: [PATCH 05/13] fix: suppress zizmor github-env false positives The GITHUB_PATH writes add known hardcoded install directories (not user-controlled input), so the github-env audit is a false positive. Add inline zizmor: ignore[github-env] comments on all three platform run: blocks. --- action.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/action.yml b/action.yml index 67f65b2..21444b1 100644 --- a/action.yml +++ b/action.yml @@ -35,7 +35,7 @@ runs: REQUESTED_VERSION: ${{ inputs.Version }} PRERELEASE: ${{ inputs.Prerelease }} GITHUB_TOKEN: ${{ github.token }} - run: | + run: | # zizmor: ignore[github-env] # Install-PowerShell set -e echo "Requested version: [$REQUESTED_VERSION]" @@ -138,7 +138,7 @@ runs: REQUESTED_VERSION: ${{ inputs.Version }} PRERELEASE: ${{ inputs.Prerelease }} GITHUB_TOKEN: ${{ github.token }} - run: | + run: | # zizmor: ignore[github-env] # Install-PowerShell set -e echo "Requested version: [$REQUESTED_VERSION]" @@ -228,7 +228,7 @@ runs: REQUESTED_VERSION: ${{ inputs.Version }} PRERELEASE: ${{ inputs.Prerelease }} GITHUB_TOKEN: ${{ github.token }} - run: | + run: | # zizmor: ignore[github-env] # Install-PowerShell Write-Host "Requested version: [$env:REQUESTED_VERSION]" Write-Host "Prerelease: [$env:PRERELEASE]" From 7ebb00cedb04e28924764fb79b12e821b1401db1 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Tue, 17 Feb 2026 11:59:54 +0100 Subject: [PATCH 06/13] docs: add reason to zizmor ignore comments --- action.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/action.yml b/action.yml index 21444b1..28e9545 100644 --- a/action.yml +++ b/action.yml @@ -35,7 +35,7 @@ runs: REQUESTED_VERSION: ${{ inputs.Version }} PRERELEASE: ${{ inputs.Prerelease }} GITHUB_TOKEN: ${{ github.token }} - run: | # zizmor: ignore[github-env] + run: | # zizmor: ignore[github-env] GITHUB_PATH writes use hardcoded install dirs, not user input # Install-PowerShell set -e echo "Requested version: [$REQUESTED_VERSION]" @@ -138,7 +138,7 @@ runs: REQUESTED_VERSION: ${{ inputs.Version }} PRERELEASE: ${{ inputs.Prerelease }} GITHUB_TOKEN: ${{ github.token }} - run: | # zizmor: ignore[github-env] + run: | # zizmor: ignore[github-env] GITHUB_PATH writes use hardcoded install dirs, not user input # Install-PowerShell set -e echo "Requested version: [$REQUESTED_VERSION]" @@ -228,7 +228,7 @@ runs: REQUESTED_VERSION: ${{ inputs.Version }} PRERELEASE: ${{ inputs.Prerelease }} GITHUB_TOKEN: ${{ github.token }} - run: | # zizmor: ignore[github-env] + run: | # zizmor: ignore[github-env] GITHUB_PATH writes use hardcoded install dirs, not user input # Install-PowerShell Write-Host "Requested version: [$env:REQUESTED_VERSION]" Write-Host "Prerelease: [$env:PRERELEASE]" From 07d6c2a500955295c2ade0c82d1bc08f4ca42431 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Tue, 17 Feb 2026 12:10:03 +0100 Subject: [PATCH 07/13] fix: add error handling for stable release resolution --- action.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/action.yml b/action.yml index 28e9545..9028137 100644 --- a/action.yml +++ b/action.yml @@ -67,6 +67,10 @@ runs: https://api.github.com/repos/PowerShell/PowerShell/releases/latest | jq -r '.tag_name' | sed 's/^v//' ) + if [[ -z "$REQUESTED_VERSION" ]]; then + echo "Error: Failed to resolve latest stable PowerShell release from GitHub." + exit 1 + fi echo "Latest stable PowerShell release detected: $REQUESTED_VERSION" fi ;; @@ -170,6 +174,10 @@ runs: https://api.github.com/repos/PowerShell/PowerShell/releases/latest | jq -r '.tag_name' | sed 's/^v//' ) + if [[ -z "$REQUESTED_VERSION" ]]; then + echo "Error: Failed to resolve latest stable PowerShell release from GitHub." + exit 1 + fi echo "Latest stable PowerShell release detected: $REQUESTED_VERSION" fi ;; @@ -252,6 +260,10 @@ runs: Write-Host "Latest prerelease PowerShell version detected: $latest" } else { $latest = (Invoke-RestMethod -Uri 'https://api.github.com/repos/PowerShell/PowerShell/releases/latest' -Headers $headers).tag_name.TrimStart('v') + if (-not $latest) { + Write-Host "Error: Failed to resolve latest stable PowerShell release from GitHub." + exit 1 + } Write-Host "Latest stable PowerShell release detected: $latest" } $env:REQUESTED_VERSION = $latest From d948b87b9a6f75f9429f55ede4a3e87853ee922f Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Tue, 17 Feb 2026 12:15:05 +0100 Subject: [PATCH 08/13] Fix YAML multiline run formatting --- action.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/action.yml b/action.yml index 9028137..7d4359a 100644 --- a/action.yml +++ b/action.yml @@ -35,7 +35,8 @@ runs: REQUESTED_VERSION: ${{ inputs.Version }} PRERELEASE: ${{ inputs.Prerelease }} GITHUB_TOKEN: ${{ github.token }} - run: | # zizmor: ignore[github-env] GITHUB_PATH writes use hardcoded install dirs, not user input + run: + | # zizmor: ignore[github-env] GITHUB_PATH writes use hardcoded install dirs, not user input # Install-PowerShell set -e echo "Requested version: [$REQUESTED_VERSION]" @@ -142,7 +143,8 @@ runs: REQUESTED_VERSION: ${{ inputs.Version }} PRERELEASE: ${{ inputs.Prerelease }} GITHUB_TOKEN: ${{ github.token }} - run: | # zizmor: ignore[github-env] GITHUB_PATH writes use hardcoded install dirs, not user input + run: + | # zizmor: ignore[github-env] GITHUB_PATH writes use hardcoded install dirs, not user input # Install-PowerShell set -e echo "Requested version: [$REQUESTED_VERSION]" @@ -236,7 +238,8 @@ runs: REQUESTED_VERSION: ${{ inputs.Version }} PRERELEASE: ${{ inputs.Prerelease }} GITHUB_TOKEN: ${{ github.token }} - run: | # zizmor: ignore[github-env] GITHUB_PATH writes use hardcoded install dirs, not user input + run: + | # zizmor: ignore[github-env] GITHUB_PATH writes use hardcoded install dirs, not user input # Install-PowerShell Write-Host "Requested version: [$env:REQUESTED_VERSION]" Write-Host "Prerelease: [$env:PRERELEASE]" From ad4035f772dfb573c30f2c9df90b7a3adb8af67b Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Tue, 17 Feb 2026 12:25:29 +0100 Subject: [PATCH 09/13] Harden GITHUB_PATH writes and prerelease API pagination - Validate MAJOR_VERSION is digits-only before writing to GITHUB_PATH (Linux, macOS, Windows) - Add per_page=100 to /releases API calls when resolving latest prerelease (Linux, macOS, Windows) --- action.yml | 52 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/action.yml b/action.yml index 7d4359a..387eddf 100644 --- a/action.yml +++ b/action.yml @@ -51,7 +51,7 @@ runs: -H "Accept: application/vnd.github+json" \ -H "Authorization: Bearer $GITHUB_TOKEN" \ -H "X-GitHub-Api-Version: 2022-11-28" \ - https://api.github.com/repos/PowerShell/PowerShell/releases | + 'https://api.github.com/repos/PowerShell/PowerShell/releases?per_page=100' | jq -r '[.[] | select(.prerelease == true)] | (.[0].tag_name // empty)' | sed 's/^v//' ) if [[ -z "$REQUESTED_VERSION" ]]; then @@ -128,10 +128,14 @@ runs: # `shell: pwsh` steps resolve to the version we just installed. if [[ "$REQUESTED_VERSION" == *-* ]]; then MAJOR_VERSION=$(echo "$REQUESTED_VERSION" | cut -d'.' -f1) - INSTALL_DIR="/opt/microsoft/powershell/${MAJOR_VERSION}-preview" - if [[ -d "$INSTALL_DIR" ]]; then - echo "Adding install directory to GITHUB_PATH: $INSTALL_DIR" - echo "$INSTALL_DIR" >> "$GITHUB_PATH" + if [[ "$MAJOR_VERSION" =~ ^[0-9]+$ ]]; then + INSTALL_DIR="/opt/microsoft/powershell/${MAJOR_VERSION}-preview" + if [[ -d "$INSTALL_DIR" ]]; then + echo "Adding install directory to GITHUB_PATH: $INSTALL_DIR" + echo "$INSTALL_DIR" >> "$GITHUB_PATH" + fi + else + echo "Warning: Computed MAJOR_VERSION ('$MAJOR_VERSION') is invalid; skipping GITHUB_PATH update." >&2 fi fi @@ -159,7 +163,7 @@ runs: -H "Accept: application/vnd.github+json" \ -H "Authorization: Bearer $GITHUB_TOKEN" \ -H "X-GitHub-Api-Version: 2022-11-28" \ - https://api.github.com/repos/PowerShell/PowerShell/releases | + 'https://api.github.com/repos/PowerShell/PowerShell/releases?per_page=100' | jq -r '[.[] | select(.prerelease == true)] | (.[0].tag_name // empty)' | sed 's/^v//' ) if [[ -z "$REQUESTED_VERSION" ]]; then @@ -223,10 +227,14 @@ runs: # `shell: pwsh` steps resolve to the version we just installed. if [[ "$REQUESTED_VERSION" == *-* ]]; then MAJOR_VERSION=$(echo "$REQUESTED_VERSION" | cut -d'.' -f1) - INSTALL_DIR="/usr/local/microsoft/powershell/${MAJOR_VERSION}-preview" - if [[ -d "$INSTALL_DIR" ]]; then - echo "Adding install directory to GITHUB_PATH: $INSTALL_DIR" - echo "$INSTALL_DIR" >> "$GITHUB_PATH" + if [[ "$MAJOR_VERSION" =~ ^[0-9]+$ ]]; then + INSTALL_DIR="/usr/local/microsoft/powershell/${MAJOR_VERSION}-preview" + if [[ -d "$INSTALL_DIR" ]]; then + echo "Adding install directory to GITHUB_PATH: $INSTALL_DIR" + echo "$INSTALL_DIR" >> "$GITHUB_PATH" + fi + else + echo "Warning: Computed MAJOR_VERSION ('$MAJOR_VERSION') is invalid; skipping GITHUB_PATH update." >&2 fi fi @@ -253,7 +261,7 @@ runs: 'X-GitHub-Api-Version' = '2022-11-28' } if ($env:PRERELEASE -eq 'true') { - $releases = Invoke-RestMethod -Uri 'https://api.github.com/repos/PowerShell/PowerShell/releases' -Headers $headers + $releases = Invoke-RestMethod -Uri 'https://api.github.com/repos/PowerShell/PowerShell/releases?per_page=100' -Headers $headers $latestRelease = $releases | Where-Object { $_.prerelease -eq $true } | Select-Object -First 1 if (-not $latestRelease) { Write-Host "Error: No prerelease PowerShell releases are available from GitHub." @@ -390,14 +398,18 @@ runs: # install directory (7-preview) is not on the runner's default PATH. $isPrerelease = $env:REQUESTED_VERSION -match '-' $majorVersion = ($env:REQUESTED_VERSION -split '[.\-]')[0] - $installDir = if ($isPrerelease) { - "$env:ProgramFiles\PowerShell\$majorVersion-preview" - } else { - "$env:ProgramFiles\PowerShell\$majorVersion" - } - if (Test-Path $installDir) { - Write-Host "Adding install directory to GITHUB_PATH: $installDir" - Add-Content -Path $env:GITHUB_PATH -Value $installDir + if ($majorVersion -match '^\d+$') { + $installDir = if ($isPrerelease) { + "$env:ProgramFiles\PowerShell\$majorVersion-preview" + } else { + "$env:ProgramFiles\PowerShell\$majorVersion" + } + if (Test-Path $installDir) { + Write-Host "Adding install directory to GITHUB_PATH: $installDir" + Add-Content -Path $env:GITHUB_PATH -Value $installDir + } else { + Write-Host "Warning: Expected install directory not found: $installDir" + } } else { - Write-Host "Warning: Expected install directory not found: $installDir" + Write-Host "Warning: Computed major version ('$majorVersion') is invalid; skipping GITHUB_PATH update." } From 0e4900839ca0cfb9640145fa8708c5aa6b7aadb9 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Tue, 17 Feb 2026 12:33:07 +0100 Subject: [PATCH 10/13] Use older prerelease (7.4.0-preview.5) in test matrix --- .github/workflows/Action-Test.yml | 2 +- action.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/Action-Test.yml b/.github/workflows/Action-Test.yml index 0aa09b5..95d66c2 100644 --- a/.github/workflows/Action-Test.yml +++ b/.github/workflows/Action-Test.yml @@ -22,7 +22,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, windows-latest, macOS-latest] - version: ['latest', 'prerelease', '7.4.7', '7.5.0', '7.6.0-preview.6'] + version: ['latest', 'prerelease', '7.4.7', '7.5.0', '7.4.0-preview.5'] runs-on: ${{ matrix.os }} name: '${{ matrix.os }} - [${{ matrix.version }}]' steps: diff --git a/action.yml b/action.yml index 387eddf..3ee6de1 100644 --- a/action.yml +++ b/action.yml @@ -11,9 +11,9 @@ branding: inputs: Version: description: | - PowerShell version to install (e.g. `7.4.1` or `7.6.0-preview.6`). + PowerShell version to install (e.g. `7.4.1` or `7.4.0-preview.5`). Defaults to install the latest stable release. - Prerelease versions are supported (e.g. `7.6.0-preview.6`, `7.5.0-rc.1`). + Prerelease versions are supported (e.g. `7.4.0-preview.5`, `7.5.0-rc.1`). required: false default: 'latest' Prerelease: From 19640bee4c875976f34c84d79a91e7de6db478d7 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Tue, 17 Feb 2026 12:38:28 +0100 Subject: [PATCH 11/13] Use GitHub Releases API to resolve Linux package asset URLs The PowerShell release naming convention changed over time (e.g. powershell-preview_ vs powershell_ prefix for .deb packages). Instead of hardcoding the package filename pattern, query the GitHub Releases API for the actual asset URLs. This handles all historical and future naming conventions correctly. --- action.yml | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/action.yml b/action.yml index 3ee6de1..c4e241d 100644 --- a/action.yml +++ b/action.yml @@ -96,11 +96,30 @@ runs: # Determine Linux distribution type ARCH=$(dpkg --print-architecture 2>/dev/null || rpm --eval '%{_arch}' 2>/dev/null || echo "x86_64") + # Query GitHub Releases API for the actual asset URLs to handle naming + # convention differences across releases (e.g. powershell-preview_ vs powershell_). + RELEASE_JSON=$( + curl -s -f \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer $GITHUB_TOKEN" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "https://api.github.com/repos/PowerShell/PowerShell/releases/tags/v${REQUESTED_VERSION}" + ) + if [[ -z "$RELEASE_JSON" ]]; then + echo "Error: Failed to fetch release info for v${REQUESTED_VERSION} from GitHub." + exit 1 + fi + if command -v apt-get >/dev/null || command -v dpkg >/dev/null; then # Debian/Ubuntu based echo "Detected Debian/Ubuntu based system..." - DEB_NAME="powershell_${REQUESTED_VERSION}-1.deb_${ARCH}.deb" - URL="https://github.com/PowerShell/PowerShell/releases/download/v${REQUESTED_VERSION}/${DEB_NAME}" + URL=$(echo "$RELEASE_JSON" | jq -r --arg arch "$ARCH" \ + '[.assets[] | select(.name | test("^powershell.*\\.deb_" + $arch + "\\.deb$"))] | .[0].browser_download_url // empty') + if [[ -z "$URL" ]]; then + echo "Error: No .deb package found for architecture '$ARCH' in release v${REQUESTED_VERSION}." + exit 1 + fi + DEB_NAME=$(basename "$URL") echo "Downloading from: $URL" wget -q "$URL" -O "$DEB_NAME" echo "Starting installation of PowerShell [$REQUESTED_VERSION]..." @@ -108,12 +127,13 @@ runs: elif command -v rpm >/dev/null; then # RHEL/Fedora/CentOS based echo "Detected RHEL/Fedora/CentOS based system..." - if [[ "$ARCH" == "aarch64" ]]; then - RPM_NAME="powershell-${REQUESTED_VERSION}-1.rh.${ARCH}.rpm" - else - RPM_NAME="powershell-${REQUESTED_VERSION}-1.rh.x86_64.rpm" + URL=$(echo "$RELEASE_JSON" | jq -r --arg arch "$ARCH" \ + '[.assets[] | select(.name | test("^powershell.*\\.rh\\." + (if $arch == "aarch64" then $arch else "x86_64" end) + "\\.rpm$"))] | .[0].browser_download_url // empty') + if [[ -z "$URL" ]]; then + echo "Error: No .rpm package found for architecture '$ARCH' in release v${REQUESTED_VERSION}." + exit 1 fi - URL="https://github.com/PowerShell/PowerShell/releases/download/v${REQUESTED_VERSION}/${RPM_NAME}" + RPM_NAME=$(basename "$URL") echo "Downloading from: $URL" wget -q "$URL" -O "$RPM_NAME" echo "Starting installation of PowerShell [$REQUESTED_VERSION]..." From 6302f0ed23e83b7a680b748b1655c677682bdd82 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Tue, 17 Feb 2026 12:42:11 +0100 Subject: [PATCH 12/13] fix: Remove existing PowerShell packages before install and use specific package name filters - Use specific jq filters (powershell_ for stable, powershell-preview_ for prerelease) instead of broad ^powershell.* regex that could match powershell-lts - Remove existing conflicting PowerShell packages (powershell, powershell-lts, powershell-preview) before dpkg/rpm install to avoid /usr/bin/pwsh conflicts - Add post-install verification to fail early if the installed version does not match the requested version - Apply same fixes for RPM-based distributions --- action.yml | 44 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/action.yml b/action.yml index c4e241d..87e2fe1 100644 --- a/action.yml +++ b/action.yml @@ -110,11 +110,24 @@ runs: exit 1 fi + # Determine if the requested version is a prerelease (contains a hyphen, e.g. 7.6.0-preview.6) + IS_PRERELEASE=false + if [[ "$REQUESTED_VERSION" == *-* ]]; then + IS_PRERELEASE=true + fi + if command -v apt-get >/dev/null || command -v dpkg >/dev/null; then # Debian/Ubuntu based echo "Detected Debian/Ubuntu based system..." - URL=$(echo "$RELEASE_JSON" | jq -r --arg arch "$ARCH" \ - '[.assets[] | select(.name | test("^powershell.*\\.deb_" + $arch + "\\.deb$"))] | .[0].browser_download_url // empty') + if [[ "$IS_PRERELEASE" == "true" ]]; then + # For prerelease versions, select the powershell-preview package + URL=$(echo "$RELEASE_JSON" | jq -r --arg arch "$ARCH" \ + '[.assets[] | select(.name | test("^powershell-preview_.*\\.deb_" + $arch + "\\.deb$"))] | .[0].browser_download_url // empty') + else + # For stable versions, select the powershell package (not powershell-lts or powershell-preview) + URL=$(echo "$RELEASE_JSON" | jq -r --arg arch "$ARCH" \ + '[.assets[] | select(.name | test("^powershell_.*\\.deb_" + $arch + "\\.deb$"))] | .[0].browser_download_url // empty') + fi if [[ -z "$URL" ]]; then echo "Error: No .deb package found for architecture '$ARCH' in release v${REQUESTED_VERSION}." exit 1 @@ -122,13 +135,24 @@ runs: DEB_NAME=$(basename "$URL") echo "Downloading from: $URL" wget -q "$URL" -O "$DEB_NAME" + + # Remove all existing PowerShell packages to avoid dpkg conflicts + # (powershell, powershell-lts, and powershell-preview all provide /usr/bin/pwsh) + echo "Removing existing PowerShell packages to avoid conflicts..." + sudo dpkg --remove powershell powershell-lts powershell-preview 2>/dev/null || true + echo "Starting installation of PowerShell [$REQUESTED_VERSION]..." sudo dpkg -i "$DEB_NAME" || sudo apt-get -f install -y elif command -v rpm >/dev/null; then # RHEL/Fedora/CentOS based echo "Detected RHEL/Fedora/CentOS based system..." - URL=$(echo "$RELEASE_JSON" | jq -r --arg arch "$ARCH" \ - '[.assets[] | select(.name | test("^powershell.*\\.rh\\." + (if $arch == "aarch64" then $arch else "x86_64" end) + "\\.rpm$"))] | .[0].browser_download_url // empty') + if [[ "$IS_PRERELEASE" == "true" ]]; then + URL=$(echo "$RELEASE_JSON" | jq -r --arg arch "$ARCH" \ + '[.assets[] | select(.name | test("^powershell-preview.*\\.rh\\." + (if $arch == "aarch64" then $arch else "x86_64" end) + "\\.rpm$"))] | .[0].browser_download_url // empty') + else + URL=$(echo "$RELEASE_JSON" | jq -r --arg arch "$ARCH" \ + '[.assets[] | select(.name | test("^powershell-[0-9].*\\.rh\\." + (if $arch == "aarch64" then $arch else "x86_64" end) + "\\.rpm$"))] | .[0].browser_download_url // empty') + fi if [[ -z "$URL" ]]; then echo "Error: No .rpm package found for architecture '$ARCH' in release v${REQUESTED_VERSION}." exit 1 @@ -136,12 +160,24 @@ runs: RPM_NAME=$(basename "$URL") echo "Downloading from: $URL" wget -q "$URL" -O "$RPM_NAME" + + # Remove existing PowerShell packages to avoid conflicts + echo "Removing existing PowerShell packages to avoid conflicts..." + sudo rpm -e powershell powershell-preview 2>/dev/null || true + echo "Starting installation of PowerShell [$REQUESTED_VERSION]..." sudo rpm -i "$RPM_NAME" || sudo yum install -y "$RPM_NAME" else echo "Unsupported Linux distribution. Cannot determine package format." exit 1 fi + + # Verify installation succeeded + INSTALLED_VERSION=$(pwsh -NoLogo -NoProfile -Command '$PSVersionTable.PSVersion.ToString()' 2>/dev/null || true) + if [[ "$INSTALLED_VERSION" != "$REQUESTED_VERSION" ]]; then + echo "Error: Installation verification failed. Expected $REQUESTED_VERSION but got ${INSTALLED_VERSION:-nothing}." + exit 1 + fi echo "Installation complete. PowerShell [$REQUESTED_VERSION] is now available." # For prerelease builds, add the install directory to GITHUB_PATH so subsequent From 2b522c0eb4f283fce9769e9fe41fa371f51415de Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Tue, 17 Feb 2026 12:46:51 +0100 Subject: [PATCH 13/13] fix: Handle varying prerelease asset names and fix preview verification - Prerelease .deb naming changed between releases: Older (7.4.x): powershell-preview_7.4.0-preview.5-1.deb_amd64.deb Newer (7.6.x): powershell_7.6.0-preview.6-1.deb_amd64.deb Now tries powershell-preview_ first, falls back to powershell_ - Same fallback logic applied for .rpm assets - Fix verification failure for preview installs: preview builds install to /opt/microsoft/powershell/-preview/ which is not on PATH after removing the old stable package. Now adds install dir to PATH before running verification. - Simplified GITHUB_PATH update using the IS_PRERELEASE flag already computed --- action.yml | 44 ++++++++++++++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/action.yml b/action.yml index 87e2fe1..c6dd2d3 100644 --- a/action.yml +++ b/action.yml @@ -120,9 +120,16 @@ runs: # Debian/Ubuntu based echo "Detected Debian/Ubuntu based system..." if [[ "$IS_PRERELEASE" == "true" ]]; then - # For prerelease versions, select the powershell-preview package + # Prerelease .deb naming varies across releases: + # Older: powershell-preview_7.4.0-preview.5-1.deb_amd64.deb + # Newer: powershell_7.6.0-preview.6-1.deb_amd64.deb + # Try powershell-preview_ first, then fall back to powershell_ URL=$(echo "$RELEASE_JSON" | jq -r --arg arch "$ARCH" \ '[.assets[] | select(.name | test("^powershell-preview_.*\\.deb_" + $arch + "\\.deb$"))] | .[0].browser_download_url // empty') + if [[ -z "$URL" ]]; then + URL=$(echo "$RELEASE_JSON" | jq -r --arg arch "$ARCH" \ + '[.assets[] | select(.name | test("^powershell_.*\\.deb_" + $arch + "\\.deb$"))] | .[0].browser_download_url // empty') + fi else # For stable versions, select the powershell package (not powershell-lts or powershell-preview) URL=$(echo "$RELEASE_JSON" | jq -r --arg arch "$ARCH" \ @@ -147,8 +154,16 @@ runs: # RHEL/Fedora/CentOS based echo "Detected RHEL/Fedora/CentOS based system..." if [[ "$IS_PRERELEASE" == "true" ]]; then + # Prerelease .rpm naming varies across releases: + # Older: powershell-preview-7.4.0_preview.5-1.rh.x86_64.rpm + # Newer: powershell-7.6.0_preview.6-1.rh.x86_64.rpm + # Try powershell-preview first, then fall back to powershell- URL=$(echo "$RELEASE_JSON" | jq -r --arg arch "$ARCH" \ '[.assets[] | select(.name | test("^powershell-preview.*\\.rh\\." + (if $arch == "aarch64" then $arch else "x86_64" end) + "\\.rpm$"))] | .[0].browser_download_url // empty') + if [[ -z "$URL" ]]; then + URL=$(echo "$RELEASE_JSON" | jq -r --arg arch "$ARCH" \ + '[.assets[] | select(.name | test("^powershell-[0-9].*\\.rh\\." + (if $arch == "aarch64" then $arch else "x86_64" end) + "\\.rpm$"))] | .[0].browser_download_url // empty') + fi else URL=$(echo "$RELEASE_JSON" | jq -r --arg arch "$ARCH" \ '[.assets[] | select(.name | test("^powershell-[0-9].*\\.rh\\." + (if $arch == "aarch64" then $arch else "x86_64" end) + "\\.rpm$"))] | .[0].browser_download_url // empty') @@ -172,6 +187,19 @@ runs: exit 1 fi + # Determine the install directory and add to PATH before verification. + # Preview builds install to /opt/microsoft/powershell/-preview/ + # which is not on the default PATH after removing the old powershell package. + MAJOR_VERSION=$(echo "$REQUESTED_VERSION" | cut -d'.' -f1) + if [[ "$IS_PRERELEASE" == "true" ]]; then + INSTALL_DIR="/opt/microsoft/powershell/${MAJOR_VERSION}-preview" + else + INSTALL_DIR="/opt/microsoft/powershell/${MAJOR_VERSION}" + fi + if [[ -d "$INSTALL_DIR" ]]; then + export PATH="$INSTALL_DIR:$PATH" + fi + # Verify installation succeeded INSTALLED_VERSION=$(pwsh -NoLogo -NoProfile -Command '$PSVersionTable.PSVersion.ToString()' 2>/dev/null || true) if [[ "$INSTALLED_VERSION" != "$REQUESTED_VERSION" ]]; then @@ -182,17 +210,9 @@ runs: # For prerelease builds, add the install directory to GITHUB_PATH so subsequent # `shell: pwsh` steps resolve to the version we just installed. - if [[ "$REQUESTED_VERSION" == *-* ]]; then - MAJOR_VERSION=$(echo "$REQUESTED_VERSION" | cut -d'.' -f1) - if [[ "$MAJOR_VERSION" =~ ^[0-9]+$ ]]; then - INSTALL_DIR="/opt/microsoft/powershell/${MAJOR_VERSION}-preview" - if [[ -d "$INSTALL_DIR" ]]; then - echo "Adding install directory to GITHUB_PATH: $INSTALL_DIR" - echo "$INSTALL_DIR" >> "$GITHUB_PATH" - fi - else - echo "Warning: Computed MAJOR_VERSION ('$MAJOR_VERSION') is invalid; skipping GITHUB_PATH update." >&2 - fi + if [[ "$IS_PRERELEASE" == "true" && -d "$INSTALL_DIR" ]]; then + echo "Adding install directory to GITHUB_PATH: $INSTALL_DIR" + echo "$INSTALL_DIR" >> "$GITHUB_PATH" fi - name: Install PowerShell (macOS)