Skip to content

Commit 09d87d3

Browse files
🌟 [Major]: Introducing Install-PowerShell action (#1)
## Description This pull request introduces the `Install-PowerShell` GitHub Action, simplifying installation of specific versions of PowerShell on GitHub runners. ### Features: - Cross‑platform installer – Runs on all GitHub‑hosted runners (Ubuntu, macOS, Windows). Detects the OS at runtime and executes the appropriate installation routine. - Version selector – Installs either a specific PowerShell Core version (e.g. 7.4.1) or the latest stable release when Version: latest is supplied (default). - Smart skip logic – Checks the currently installed PowerShell version and skips installation when the requested version is already present, saving time and CI minutes. - Native package managers first - Linux (Debian/Ubuntu): Uses APT, falling back to direct .deb download if the exact version isn’t in the repo. - macOS: Prefers Homebrew Cask; falls back to the official .pkg installer (ARM64/x64 aware). - Windows: Downloads the official MSI and installs silently with msiexec. ### Metadata Updates: * **Action Metadata Update**: Updated `action.yml` to reflect the new functionality, including a description, branding changes, and streamlined inputs. Removed unnecessary inputs like `Debug` and `Verbose`. `action.yml` ## Type of change <!-- Use the check-boxes [x] on the options that are relevant. --> - [ ] 📖 [Docs] - [ ] 🪲 [Fix] - [ ] 🩹 [Patch] - [ ] ⚠️ [Security fix] - [ ] 🚀 [Feature] - [x] 🌟 [Breaking change] ## Checklist <!-- Use the check-boxes [x] on the options that are relevant. --> - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas
1 parent e8f11bb commit 09d87d3

File tree

6 files changed

+287
-79
lines changed

6 files changed

+287
-79
lines changed

.github/linters/.jscpd.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
"consoleFull"
55
],
66
"ignore": [
7-
"**/tests/**"
7+
"**/tests/**",
8+
"**/action.yml"
89
],
910
"absolute": true
1011
}

.github/workflows/Action-Test.yml

+40-5
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,50 @@ permissions:
1818

1919
jobs:
2020
ActionTestBasic:
21-
name: Action-Test - [Basic]
22-
runs-on: ubuntu-latest
21+
strategy:
22+
fail-fast: false
23+
matrix:
24+
os: [ubuntu-latest, windows-latest, macOS-latest]
25+
version: ['latest', '7.4.7', '7.5.0']
26+
runs-on: ${{ matrix.os }}
27+
name: '${{ matrix.os }} - [${{ matrix.version }}]'
2328
steps:
24-
# Need to check out as part of the test, as its a local action
2529
- name: Checkout repo
2630
uses: actions/checkout@v4
2731

2832
- name: Action-Test
2933
uses: ./
3034
with:
31-
working-directory: ./tests
32-
subject: PSModule
35+
Version: ${{ matrix.version }}
36+
37+
- name: Verify installed version
38+
shell: pwsh
39+
env:
40+
GITHUB_TOKEN: ${{ github.token }}
41+
run: |
42+
# Requested version that came from the matrix
43+
$requested = '${{ matrix.version }}'
44+
45+
# When empty / 'null' / 'latest' → resolve to latest stable release
46+
if ([string]::IsNullOrWhiteSpace($requested) -or
47+
$requested.Trim().ToLower() -in @('latest','null')) {
48+
49+
$requested = (
50+
Invoke-RestMethod -Uri 'https://api.github.com/repos/PowerShell/PowerShell/releases/latest' `
51+
-Headers @{
52+
'Accept' = 'application/vnd.github+json'
53+
'Authorization' = "Bearer $($env:GITHUB_TOKEN)"
54+
'X-GitHub-Api-Version' = '2022-11-28'
55+
}
56+
).tag_name.TrimStart('v')
57+
Write-Host "Resolved 'latest' → $requested"
58+
}
59+
60+
# Actual version installed by the action
61+
$installed = ($PSVersionTable.PSVersion).ToString()
62+
Write-Host "Installed PowerShell version: $installed"
63+
Write-Host "Expected PowerShell version: $requested"
64+
65+
if ($installed -ne $requested) {
66+
throw "Failed: expected $requested but got $installed"
67+
}

README.md

+53-9
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,61 @@
1-
# Template-Action
1+
# Install-PowerShell
22

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

56
## Usage
67

7-
### Inputs
8+
Add the action to a job in your workflow file:
89

9-
### Secrets
10-
11-
### Outputs
10+
```yaml
11+
jobs:
12+
build:
13+
runs-on: ubuntu-latest
14+
steps:
15+
- uses: actions/checkout@v4
1216

13-
### Example
17+
- name: Install PowerShell
18+
uses: PSModule/install-powershell@v1
19+
with:
20+
Version: 7.5.0
1421

15-
```yaml
16-
Example here
22+
- name: Run a PowerShell script
23+
shell: pwsh
24+
run: |
25+
Write-Host "Using PowerShell $($PSVersionTable.PSVersion)"
1726
```
27+
28+
## Inputs
29+
30+
| Input | Required | Default | Description |
31+
| ------- | -------- | ------- | ----------- |
32+
| `Version` | `false` | `latest` | Desired PowerShell Core version (e.g. `7.4.1`). Use `latest` to install the newest stable release. |
33+
34+
## Secrets
35+
36+
This action does **not** require any secrets.
37+
38+
## Outputs
39+
40+
This action does **not** generate any outputs.
41+
42+
## How it works
43+
44+
* **Version resolution**
45+
If `Version` is set to `latest` (case‑insensitive), the action queries the GitHub API for the newest stable release tag in the
46+
`PowerShell/PowerShell` repository and substitutes that version.
47+
48+
* **Skip logic**
49+
Before installing, the action checks the current runner to see whether the requested version is already available
50+
(`pwsh -Command $($PSVersionTable.PSVersion)`). If it matches, the step ends immediately.
51+
52+
* **Platform‑specific installers**
53+
54+
| Runner OS | Install strategy |
55+
| --------- | ---------------- |
56+
| **Linux** (Debian/Ubuntu‑based) | Uses APT if available; otherwise downloads the `.deb` asset directly from the release page and installs with `dpkg`. |
57+
| **macOS** | Prefers Homebrew Cask (`brew install --cask powershell`) and falls back to downloading the `.pkg` installer. ARCH detection (`arm64` / `x64`) is automatic. |
58+
| **Windows** | Downloads the corresponding `.msi` package and installs silently with `msiexec`. |
59+
60+
* **Error handling**
61+
The step fails with a clear error message if the requested version cannot be resolved or if the operating‑system distribution is unsupported (e.g., non‑APT Linux distros).

action.yml

+192-37
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,203 @@
1-
name: {{ NAME }}
2-
description: {{ DESCRIPTION }}
1+
name: Install PowerShell
2+
description: |
3+
Install a specific version —or the latest stable version— of PowerShell Core
4+
on any GitHub runner (Linux, macOS, Windows).
5+
Skips the install if the requested version is already present.
36
author: PSModule
47
branding:
5-
icon: upload-cloud
6-
color: white
8+
icon: terminal
9+
color: purple
710

811
inputs:
9-
subject:
10-
description: The subject to greet
11-
required: false
12-
default: World
13-
Debug:
14-
description: Enable debug output.
15-
required: false
16-
default: 'false'
17-
Verbose:
18-
description: Enable verbose output.
19-
required: false
20-
default: 'false'
2112
Version:
22-
description: Specifies the version of the GitHub module to be installed. The value must be an exact version.
23-
required: false
24-
Prerelease:
25-
description: Allow prerelease versions if available.
13+
description: |
14+
PowerShell version to install (e.g. `7.4.1`).
15+
Defaults to install the latest stable release.
2616
required: false
27-
default: 'false'
28-
WorkingDirectory:
29-
description: The working directory where the script will run from.
30-
required: false
31-
default: ${{ github.workspace }}
17+
default: 'latest'
3218

3319
runs:
3420
using: composite
3521
steps:
36-
- name: {{ NAME }}
37-
uses: PSModule/GitHub-Script@v1
22+
- name: Install PowerShell (Linux)
23+
if: runner.os == 'Linux'
24+
shell: bash
25+
env:
26+
REQUESTED_VERSION: ${{ inputs.Version }}
27+
GITHUB_TOKEN: ${{ github.token }}
28+
run: |
29+
set -e
30+
31+
echo "Requested version: [$REQUESTED_VERSION]"
32+
33+
# Only resolve to latest version if explicitly set to 'latest' (case-insensitive)
34+
case "${REQUESTED_VERSION:-}" in
35+
[Ll][Aa][Tt][Ee][Ss][Tt])
36+
REQUESTED_VERSION=$(
37+
curl -s -f \
38+
-H "Accept: application/vnd.github+json" \
39+
-H "Authorization: Bearer $GITHUB_TOKEN" \
40+
-H "X-GitHub-Api-Version: 2022-11-28" \
41+
https://api.github.com/repos/PowerShell/PowerShell/releases/latest |
42+
jq -r '.tag_name' | sed 's/^v//'
43+
)
44+
echo "Latest stable PowerShell release detected: $REQUESTED_VERSION"
45+
;;
46+
"")
47+
echo "Error: Version input is required (or use 'latest')"
48+
exit 1
49+
;;
50+
esac
51+
52+
DETECTED_VERSION=$(pwsh -NoLogo -NoProfile -Command '$PSVersionTable.PSVersion.ToString()' 2>/dev/null || true)
53+
if [[ "$DETECTED_VERSION" == "$REQUESTED_VERSION" ]]; then
54+
echo "PowerShell $DETECTED_VERSION already installed. Skipping."
55+
exit 0
56+
fi
57+
58+
if command -v apt-get >/dev/null; then
59+
echo "Using APT package manager (Debian/Ubuntu)..."
60+
61+
if ! grep -q "packages.microsoft.com" /etc/apt/sources.list /etc/apt/sources.list.d/* 2>/dev/null; then
62+
wget -qO- https://packages.microsoft.com/keys/microsoft.asc \
63+
| sudo tee /etc/apt/trusted.gpg.d/microsoft.asc > /dev/null
64+
DIST_CODENAME=$(lsb_release -cs)
65+
sudo add-apt-repository \
66+
"deb [arch=$(dpkg --print-architecture)] https://packages.microsoft.com/ubuntu/$DIST_CODENAME/prod $DIST_CODENAME main"
67+
fi
68+
69+
sudo apt-get update -y
70+
EXACT_PKG=$(apt-cache madison powershell | awk '{print $3}' \
71+
| grep -E "^${REQUESTED_VERSION}(-|$)" | head -n1 || true)
72+
73+
if [[ -n "$EXACT_PKG" ]]; then
74+
sudo apt-get install -y powershell=$EXACT_PKG
75+
else
76+
ARCH=$(dpkg --print-architecture)
77+
DEB_NAME="powershell_${REQUESTED_VERSION}-1.deb_${ARCH}.deb"
78+
URL="https://github.com/PowerShell/PowerShell/releases/download/v${REQUESTED_VERSION}/${DEB_NAME}"
79+
wget -q "$URL" -O "$DEB_NAME"
80+
sudo dpkg -i "$DEB_NAME" || sudo apt-get -f install -y
81+
fi
82+
else
83+
echo "Unsupported Linux distribution (no apt-get)."
84+
exit 1
85+
fi
86+
87+
- name: Install PowerShell (macOS)
88+
if: runner.os == 'macOS'
89+
shell: bash
3890
env:
39-
{{ ORG }}_{{ NAME }}_INPUT_subject: ${{ inputs.subject }}
40-
with:
41-
Debug: ${{ inputs.Debug }}
42-
Prerelease: ${{ inputs.Prerelease }}
43-
Verbose: ${{ inputs.Verbose }}
44-
Version: ${{ inputs.Version }}
45-
WorkingDirectory: ${{ inputs.WorkingDirectory }}
46-
Script: |
47-
# {{ NAME }}
48-
${{ github.action_path }}/scripts/main.ps1
91+
REQUESTED_VERSION: ${{ inputs.Version }}
92+
GITHUB_TOKEN: ${{ github.token }}
93+
run: |
94+
set -e
95+
96+
echo "Requested version: [$REQUESTED_VERSION]"
97+
98+
# Convert to lowercase for comparison (macOS-compatible method)
99+
REQ_VER_LOWER=$(echo "$REQUESTED_VERSION" | tr '[:upper:]' '[:lower:]')
100+
101+
# Only resolve to latest version if explicitly set to 'latest'
102+
if [[ "$REQ_VER_LOWER" == "latest" ]]; then
103+
REQUESTED_VERSION=$(
104+
curl -s -f \
105+
-H "Accept: application/vnd.github+json" \
106+
-H "Authorization: Bearer $GITHUB_TOKEN" \
107+
-H "X-GitHub-Api-Version: 2022-11-28" \
108+
https://api.github.com/repos/PowerShell/PowerShell/releases/latest |
109+
jq -r '.tag_name' | sed 's/^v//'
110+
)
111+
echo "Latest stable PowerShell release detected: $REQUESTED_VERSION"
112+
fi
113+
114+
# Validate REQUESTED_VERSION is not empty
115+
if [[ -z "${REQUESTED_VERSION}" ]]; then
116+
echo "Error: Could not determine a valid PowerShell version."
117+
exit 1
118+
fi
119+
120+
DETECTED_VERSION=$(pwsh -NoLogo -NoProfile -Command '$PSVersionTable.PSVersion.ToString()' 2>/dev/null || true)
121+
if [[ "$DETECTED_VERSION" == "$REQUESTED_VERSION" ]]; then
122+
echo "PowerShell $DETECTED_VERSION already installed. Skipping."
123+
exit 0
124+
fi
125+
126+
# Try Homebrew first (simplified approach)
127+
if command -v brew >/dev/null; then
128+
echo "Using Homebrew package manager..."
129+
# Homebrew handles 'latest' automatically when no version is specified
130+
if [[ "$REQ_VER_LOWER" == "latest" ]]; then
131+
brew install --cask powershell
132+
else
133+
# Try specific version formula first
134+
if brew install --cask "powershell@$REQUESTED_VERSION" 2>/dev/null; then
135+
echo "Successfully installed via Homebrew"
136+
else
137+
# Fall back to generic formula if versioned one doesn't exist
138+
echo "Version-specific formula not found, installing latest via Homebrew"
139+
brew install --cask powershell
140+
fi
141+
fi
142+
else
143+
# Fall back to direct download
144+
echo "Homebrew not available, downloading directly..."
145+
ARCH=$(uname -m)
146+
case "$ARCH" in
147+
"arm64") PKG_NAME="powershell-${REQUESTED_VERSION}-osx-arm64.pkg" ;;
148+
*) PKG_NAME="powershell-${REQUESTED_VERSION}-osx-x64.pkg" ;;
149+
esac
150+
151+
URL="https://github.com/PowerShell/PowerShell/releases/download/v${REQUESTED_VERSION}/${PKG_NAME}"
152+
echo "Downloading from: $URL"
153+
if ! curl -sSL "$URL" -o "$PKG_NAME"; then
154+
echo "Error: Failed to download PowerShell package"
155+
exit 1
156+
fi
157+
sudo installer -pkg "$PKG_NAME" -target /
158+
fi
159+
160+
- name: Install PowerShell (Windows)
161+
if: runner.os == 'Windows'
162+
shell: powershell
163+
env:
164+
REQUESTED_VERSION: ${{ inputs.Version }}
165+
GITHUB_TOKEN: ${{ github.token }}
166+
run: |
167+
Write-Host "Requested version: [$REQUESTED_VERSION]"
168+
# Only resolve to latest version if explicitly set to 'latest' (case-insensitive)
169+
$req = $env:REQUESTED_VERSION
170+
if ($req -and $req.Trim().ToLower() -eq 'latest') {
171+
$latest = (
172+
Invoke-RestMethod -Uri 'https://api.github.com/repos/PowerShell/PowerShell/releases/latest' `
173+
-Headers @{
174+
'Accept' = 'application/vnd.github+json'
175+
'Authorization' = "Bearer $($env:GITHUB_TOKEN)"
176+
'X-GitHub-Api-Version' = '2022-11-28'
177+
}
178+
).tag_name.TrimStart('v')
179+
Write-Host "Latest stable PowerShell release detected: $latest"
180+
$env:REQUESTED_VERSION = $latest
181+
} elseif ([string]::IsNullOrWhiteSpace($req)) {
182+
Write-Host "Error: Version input is required (or use 'latest')"
183+
exit 1
184+
}
185+
186+
try {
187+
$detected = (pwsh -NoLogo -NoProfile -Command '$PSVersionTable.PSVersion.ToString()')
188+
} catch {
189+
$detected = $null
190+
}
191+
192+
Write-Host "Detected PowerShell version: $detected"
193+
Write-Host "Requested PowerShell version: $env:REQUESTED_VERSION"
194+
195+
if ($detected -eq $env:REQUESTED_VERSION) {
196+
Write-Host "PowerShell $detected already installed. Skipping."
197+
exit 0
198+
}
199+
200+
$msi = "PowerShell-$($env:REQUESTED_VERSION)-win-x64.msi"
201+
$url = "https://github.com/PowerShell/PowerShell/releases/download/v$($env:REQUESTED_VERSION)/$msi"
202+
Invoke-WebRequest -Uri $url -OutFile $msi -UseBasicParsing
203+
Start-Process msiexec.exe -ArgumentList '/i', $msi, '/quiet', '/norestart' -Wait

0 commit comments

Comments
 (0)