Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 37 additions & 5 deletions .github/workflows/Action-Test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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', 'prerelease', '7.4.7', '7.5.0', '7.6.0-preview.6']
runs-on: ${{ matrix.os }}
name: '${{ matrix.os }} - [${{ matrix.version }}]'
steps:
Expand All @@ -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
Expand All @@ -44,8 +45,23 @@ 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'
}
$latestPrerelease = $releases | Where-Object { $_.prerelease -eq $true } | Select-Object -First 1
Comment thread
MariusStorhaug marked this conversation as resolved.
if (-not $latestPrerelease) {
throw "No prerelease releases found for PowerShell/PowerShell."
}
$requested = $latestPrerelease.tag_name.TrimStart('v')
Write-Host "Resolved 'prerelease' β†’ $requested"
Comment thread
MariusStorhaug marked this conversation as resolved.
}
# 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 = (
Expand All @@ -59,8 +75,24 @@ jobs:
Write-Host "Resolved 'latest' β†’ $requested"
}

# Actual version installed by the action
$installed = ($PSVersionTable.PSVersion).ToString()
# 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 = 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 {
$installed = ($PSVersionTable.PSVersion).ToString()
}
Write-Host "Installed PowerShell version: $installed"
Write-Host "Expected PowerShell version: $requested"

Comment thread
MariusStorhaug marked this conversation as resolved.
Expand Down
25 changes: 23 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -25,11 +26,30 @@ 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
```

### 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`). 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 (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

Expand All @@ -43,7 +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.
`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
Expand Down
183 changes: 149 additions & 34 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,18 @@ 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'
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
Expand All @@ -25,24 +33,46 @@ runs:
working-directory: ${{ github.action_path }}
env:
REQUESTED_VERSION: ${{ inputs.Version }}
PRERELEASE: ${{ inputs.Prerelease }}
GITHUB_TOKEN: ${{ github.token }}
run: |
run: | # zizmor: ignore[github-env] GITHUB_PATH writes use hardcoded install dirs, not user input
# 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 // empty)' | sed 's/^v//'
)
Comment thread
MariusStorhaug marked this conversation as resolved.
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=$(
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//'
)
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"
Comment thread
MariusStorhaug marked this conversation as resolved.
fi
Comment thread
MariusStorhaug marked this conversation as resolved.
;;
"")
echo "Error: Version input is required (or use 'latest')"
Expand Down Expand Up @@ -93,30 +123,63 @@ runs:
fi
Comment thread
MariusStorhaug marked this conversation as resolved.
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"
Comment thread
MariusStorhaug marked this conversation as resolved.
Outdated
fi
fi

- name: Install PowerShell (macOS)
if: runner.os == 'macOS'
shell: bash
working-directory: ${{ github.action_path }}
env:
REQUESTED_VERSION: ${{ inputs.Version }}
PRERELEASE: ${{ inputs.Prerelease }}
GITHUB_TOKEN: ${{ github.token }}
run: |
run: | # zizmor: ignore[github-env] GITHUB_PATH writes use hardcoded install dirs, not user input
# 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 // empty)' | sed 's/^v//'
)
Comment thread
MariusStorhaug marked this conversation as resolved.
Comment thread
MariusStorhaug marked this conversation as resolved.
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=$(
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//'
)
Comment thread
MariusStorhaug marked this conversation as resolved.
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"
Comment thread
MariusStorhaug marked this conversation as resolved.
fi
;;
"")
echo "Error: Version input is required (or use 'latest')"
Expand Down Expand Up @@ -154,29 +217,55 @@ 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"
Comment thread
MariusStorhaug marked this conversation as resolved.
Outdated
fi
fi

- name: Install PowerShell (Windows)
if: runner.os == 'Windows'
shell: powershell
working-directory: ${{ github.action_path }}
env:
REQUESTED_VERSION: ${{ inputs.Version }}
PRERELEASE: ${{ inputs.Prerelease }}
GITHUB_TOKEN: ${{ github.token }}
run: |
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]"

# 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
$latestRelease = $releases | Where-Object { $_.prerelease -eq $true } | Select-Object -First 1
Comment thread
MariusStorhaug marked this conversation as resolved.
Outdated
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"
Comment thread
MariusStorhaug marked this conversation as resolved.
} 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"
Comment thread
MariusStorhaug marked this conversation as resolved.
Comment thread
MariusStorhaug marked this conversation as resolved.
}
$env:REQUESTED_VERSION = $latest
} elseif ([string]::IsNullOrWhiteSpace($req)) {
Write-Host "Error: Version input is required (or use 'latest')"
Expand All @@ -198,14 +287,22 @@ 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 β€” 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)"
}
Expand All @@ -224,13 +321,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 -match "^$([regex]::Escape(($detected -split '-')[0]))([.\-]|$)" -or $_.DisplayVersion -eq ($detected -split '-')[0])
}

$targetEntry = $pwshEntries | Select-Object -First 1
Expand Down Expand Up @@ -283,3 +381,20 @@ 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 '-'
$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
Comment thread
MariusStorhaug marked this conversation as resolved.
Outdated
} else {
Write-Host "Warning: Expected install directory not found: $installDir"
}
Comment thread
MariusStorhaug marked this conversation as resolved.