Skip to content
Closed
Show file tree
Hide file tree
Changes from all 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
27 changes: 23 additions & 4 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', 'preview', '7.4.7', '7.5.0']
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 == 'preview' && 'latest' || matrix.version }}
Preview: ${{ matrix.version == 'preview' && 'true' || 'false' }}

- name: Verify installed version
shell: pwsh
Expand All @@ -44,8 +45,19 @@ jobs:
# Requested version that came from the matrix
$requested = '${{ matrix.version }}'

# When 'preview' → resolve to latest preview release
if ($requested.Trim().ToLower() -eq 'preview') {
$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 'preview' → $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 = (
Expand All @@ -60,7 +72,14 @@ jobs:
}

# Actual version installed by the action
$installed = ($PSVersionTable.PSVersion).ToString()
if ($IsWindows) {
$majorVersion = ($requested -split '[\.-]')[0]
$installDir = if ($requested -match 'preview') { "$majorVersion-preview" } else { $majorVersion }
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Windows verification treats a prerelease install as preview only when the version string contains preview. GitHub prereleases can also be -rc.N (or other labels), which would be installed into the preview side-by-side location but this logic would look in the stable directory. Consider detecting prerelease more generally (e.g., any SemVer pre-release -...) rather than matching only preview.

Suggested change
$installDir = if ($requested -match 'preview') { "$majorVersion-preview" } else { $majorVersion }
# Treat any SemVer prerelease (e.g., "-preview.1", "-rc.1") as a preview install
$isPreRelease = $requested -match '^\d+(?:\.\d+){1,2}-'
$installDir = if ($isPreRelease) { "$majorVersion-preview" } else { $majorVersion }

Copilot uses AI. Check for mistakes.
$installed = (& "$env:ProgramFiles\PowerShell\$installDir\pwsh.exe" -NoLogo -NoProfile -Command '$PSVersionTable.PSVersion.ToString()')
} else {
$installed = (pwsh -NoLogo -NoProfile -Command '$PSVersionTable.PSVersion.ToString()')
}
Comment on lines +75 to +81
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Windows branch verifies the installed version by invoking a hard-coded install path instead of the pwsh on PATH. This means the workflow may pass even if the action doesn’t make the requested version the default pwsh (which is what users rely on via shell: pwsh). Consider asserting what pwsh resolves to (or have the action export/update PATH and verify that behavior).

Copilot uses AI. Check for mistakes.

Write-Host "Installed PowerShell version: $installed"
Write-Host "Expected PowerShell version: $requested"

Expand Down
23 changes: 19 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
# Install-PowerShell

A cross‑platform GitHub Action that installs a specific **PowerShell Core** version—or the latest stable release—on any GitHub‑hosted runner
A cross‑platform GitHub Action that installs a specific **PowerShell Core** version—or the latest stable or preview release—on any GitHub‑hosted runner
(Linux, macOS, or Windows). The action automatically skips installation when the requested version is already present.

## Usage

Add the action to a job in your workflow file:

```yaml
jobs:
build:
Expand All @@ -25,11 +24,27 @@ jobs:
Write-Host "Using PowerShell $($PSVersionTable.PSVersion)"
```

### Installing preview builds

```yaml
- name: Install PowerShell Preview
uses: PSModule/install-powershell@v1
with:
Version: latest
Preview: true

- name: Run a PowerShell script
shell: pwsh
run: |
Write-Host "Using PowerShell $($PSVersionTable.PSVersion)"
```

## 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.1`). Use `latest` to install the newest stable release, or the newest preview release when `Preview` is `true`. |
| `Preview` | `false` | `false` | Install PowerShell preview build instead of stable. When `true` and `Version` is `latest`, installs the latest preview release. |

## Secrets

Expand All @@ -43,7 +58,7 @@ 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 `Preview` is `true`, it queries for the latest preview release instead.

* **Skip logic**
Before installing, the action checks the current runner to see whether the requested version is already available
Expand Down
108 changes: 80 additions & 28 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,17 @@ branding:
inputs:
Version:
description: |
PowerShell version to install (e.g. `7.4.1`).
PowerShell version to install (e.g. `7.4.1` or `7.5.0-preview.1`).
Defaults to install the latest stable release.
Use 'latest' for latest stable, or latest preview if Preview is true.
required: false
default: 'latest'
Preview:
description: |
Install PowerShell preview build instead of stable.
When true and Version is 'latest', installs the latest preview release.
required: false
default: 'false'

runs:
using: composite
Expand All @@ -25,24 +32,39 @@ runs:
working-directory: ${{ github.action_path }}
env:
REQUESTED_VERSION: ${{ inputs.Version }}
PREVIEW: ${{ inputs.Preview }}
GITHUB_TOKEN: ${{ github.token }}
run: |
# Install-PowerShell
set -e
echo "Requested version: [$REQUESTED_VERSION]"
echo "Preview mode: [$PREVIEW]"

# 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 [[ "$PREVIEW" == "true" ]]; then
echo "Fetching latest preview release..."
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//'
)
Comment on lines +54 to +55
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The preview-version resolution uses jq ... | .[0].tag_name without guarding for cases where no prerelease is returned (or when the newest prerelease isn’t first / is a draft). If this yields null, the action will proceed with REQUESTED_VERSION=null and fail later with a confusing 404. Consider filtering out drafts, sorting by published_at, and explicitly erroring when no prerelease is found.

Suggested change
jq -r '[.[] | select(.prerelease == true)] | .[0].tag_name' | sed 's/^v//'
)
jq -r '[.[] | select(.prerelease == true and .draft != true)] | sort_by(.published_at) | last | .tag_name // empty' | sed 's/^v//'
)
if [[ -z "$REQUESTED_VERSION" ]]; then
echo "Error: No preview releases found for PowerShell. Cannot resolve 'latest' preview version."
exit 1
fi

Copilot uses AI. Check for mistakes.
echo "Latest preview PowerShell release 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')"
Expand Down Expand Up @@ -99,24 +121,39 @@ runs:
working-directory: ${{ github.action_path }}
env:
REQUESTED_VERSION: ${{ inputs.Version }}
PREVIEW: ${{ inputs.Preview }}
GITHUB_TOKEN: ${{ github.token }}
run: |
# Install-PowerShell
set -e
echo "Requested version: [$REQUESTED_VERSION]"
echo "Preview mode: [$PREVIEW]"

# 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 [[ "$PREVIEW" == "true" ]]; then
echo "Fetching latest preview release..."
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//'
)
Comment on lines +143 to +144
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue as Linux: preview resolution via /releases + jq ... .[0].tag_name can produce null (no prereleases) or pick the wrong prerelease if ordering/drafts change. Add filtering/sorting and a clear error when no preview release is found.

Suggested change
jq -r '[.[] | select(.prerelease == true)] | .[0].tag_name' | sed 's/^v//'
)
jq -r '[.[] | select(.prerelease == true and .draft == false)] | sort_by(.created_at) | reverse | .[0].tag_name' | sed 's/^v//'
)
if [[ -z "$REQUESTED_VERSION" || "$REQUESTED_VERSION" == "null" ]]; then
echo "Error: No preview PowerShell release found when resolving 'latest' preview."
exit 1
fi

Copilot uses AI. Check for mistakes.
echo "Latest preview PowerShell release 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')"
Expand Down Expand Up @@ -160,23 +197,38 @@ runs:
working-directory: ${{ github.action_path }}
env:
REQUESTED_VERSION: ${{ inputs.Version }}
PREVIEW: ${{ inputs.Preview }}
GITHUB_TOKEN: ${{ github.token }}
run: |
# Install-PowerShell
Write-Host "Requested version: [$env:REQUESTED_VERSION]"
Write-Host "Preview mode: [$env:PREVIEW]"

# 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)"
'X-GitHub-Api-Version' = '2022-11-28'
}
).tag_name.TrimStart('v')
Write-Host "Latest stable PowerShell release detected: $latest"
if ($env:PREVIEW -eq 'true') {
Write-Host "Fetching latest preview release..."
$releases = Invoke-RestMethod -Uri 'https://api.github.com/repos/PowerShell/PowerShell/releases' `
Comment on lines 204 to +212
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On Windows, preview/prerelease builds are typically installed side-by-side (e.g., under a *-preview directory) rather than replacing the stable install. This action still relies on pwsh from PATH for detection and for user steps (shell: pwsh), so it may continue using the stable pwsh even after installing a preview. Consider explicitly updating PATH (or exposing the installed pwsh path as an output) when Preview is enabled so subsequent steps reliably use the requested build.

Copilot uses AI. Check for mistakes.
-Headers @{
'Accept' = 'application/vnd.github+json'
'Authorization' = "Bearer $($env:GITHUB_TOKEN)"
'X-GitHub-Api-Version' = '2022-11-28'
}
$latestPreview = $releases | Where-Object { $_.prerelease -eq $true } | Select-Object -First 1
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If GitHub returns no prerelease items, $latestPreview will be $null and $latestPreview.tag_name.TrimStart('v') will throw. Add an explicit null-check and fail with a clear error message (and consider excluding draft releases and sorting by published_at).

Suggested change
$latestPreview = $releases | Where-Object { $_.prerelease -eq $true } | Select-Object -First 1
$latestPreview = $releases `
| Where-Object { $_.prerelease -eq $true -and -not $_.draft } `
| Sort-Object -Property published_at -Descending `
| Select-Object -First 1
if (-not $latestPreview -or -not $latestPreview.tag_name) {
Write-Error "No non-draft preview releases found for PowerShell. Cannot resolve 'latest' preview version."
exit 1
}

Copilot uses AI. Check for mistakes.
$latest = $latestPreview.tag_name.TrimStart('v')
Write-Host "Latest preview PowerShell release detected: $latest"
} else {
$latest = (
Invoke-RestMethod -Uri 'https://api.github.com/repos/PowerShell/PowerShell/releases/latest' `
-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"
}
$env:REQUESTED_VERSION = $latest
} elseif ([string]::IsNullOrWhiteSpace($req)) {
Write-Host "Error: Version input is required (or use 'latest')"
Expand Down