diff --git a/.github/linters/.jscpd.json b/.github/linters/.jscpd.json index 23970e8..6100343 100644 --- a/.github/linters/.jscpd.json +++ b/.github/linters/.jscpd.json @@ -4,7 +4,8 @@ "consoleFull" ], "ignore": [ - "**/tests/**" + "**/tests/**", + "**/action.yml" ], "absolute": true } diff --git a/.github/workflows/Action-Test.yml b/.github/workflows/Action-Test.yml index 405a723..25565a8 100644 --- a/.github/workflows/Action-Test.yml +++ b/.github/workflows/Action-Test.yml @@ -18,15 +18,50 @@ permissions: jobs: ActionTestBasic: - name: Action-Test - [Basic] - runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macOS-latest] + version: ['latest', '7.4.7', '7.5.0'] + runs-on: ${{ matrix.os }} + name: '${{ matrix.os }} - [${{ matrix.version }}]' steps: - # Need to check out as part of the test, as its a local action - name: Checkout repo uses: actions/checkout@v4 - name: Action-Test uses: ./ with: - working-directory: ./tests - subject: PSModule + Version: ${{ matrix.version }} + + - name: Verify installed version + shell: pwsh + env: + GITHUB_TOKEN: ${{ github.token }} + run: | + # Requested version that came from the matrix + $requested = '${{ matrix.version }}' + + # When empty / 'null' / 'latest' → resolve to latest stable release + if ([string]::IsNullOrWhiteSpace($requested) -or + $requested.Trim().ToLower() -in @('latest','null')) { + + $requested = ( + 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 "Resolved 'latest' → $requested" + } + + # Actual version installed by the action + $installed = ($PSVersionTable.PSVersion).ToString() + Write-Host "Installed PowerShell version: $installed" + Write-Host "Expected PowerShell version: $requested" + + if ($installed -ne $requested) { + throw "Failed: expected $requested but got $installed" + } diff --git a/README.md b/README.md index d560186..1d42759 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,61 @@ -# Template-Action +# Install-PowerShell -A template repository for GitHub Actions +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. ## Usage -### Inputs +Add the action to a job in your workflow file: -### Secrets - -### Outputs +```yaml +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 -### Example + - name: Install PowerShell + uses: PSModule/install-powershell@v1 + with: + Version: 7.5.0 -```yaml -Example here + - 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. | + +## Secrets + +This action does **not** require any secrets. + +## Outputs + +This action does **not** generate any outputs. + +## How it works + +* **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. + +* **Skip logic** + Before installing, the action checks the current runner to see whether the requested version is already available + (`pwsh -Command $($PSVersionTable.PSVersion)`). If it matches, the step ends immediately. + +* **Platform‑specific installers** + + | Runner OS | Install strategy | + | --------- | ---------------- | + | **Linux** (Debian/Ubuntu‑based) | Uses APT if available; otherwise downloads the `.deb` asset directly from the release page and installs with `dpkg`. | + | **macOS** | Prefers Homebrew Cask (`brew install --cask powershell`) and falls back to downloading the `.pkg` installer. ARCH detection (`arm64` / `x64`) is automatic. | + | **Windows** | Downloads the corresponding `.msi` package and installs silently with `msiexec`. | + +* **Error handling** + 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). diff --git a/action.yml b/action.yml index 1fca22c..8d48b2d 100644 --- a/action.yml +++ b/action.yml @@ -1,48 +1,203 @@ -name: {{ NAME }} -description: {{ DESCRIPTION }} +name: Install PowerShell +description: | + Install a specific version —or the latest stable version— of PowerShell Core + on any GitHub runner (Linux, macOS, Windows). + Skips the install if the requested version is already present. author: PSModule branding: - icon: upload-cloud - color: white + icon: terminal + color: purple inputs: - subject: - description: The subject to greet - required: false - default: World - Debug: - description: Enable debug output. - required: false - default: 'false' - Verbose: - description: Enable verbose output. - required: false - default: 'false' Version: - description: Specifies the version of the GitHub module to be installed. The value must be an exact version. - required: false - Prerelease: - description: Allow prerelease versions if available. + description: | + PowerShell version to install (e.g. `7.4.1`). + Defaults to install the latest stable release. required: false - default: 'false' - WorkingDirectory: - description: The working directory where the script will run from. - required: false - default: ${{ github.workspace }} + default: 'latest' runs: using: composite steps: - - name: {{ NAME }} - uses: PSModule/GitHub-Script@v1 + - name: Install PowerShell (Linux) + if: runner.os == 'Linux' + shell: bash + env: + REQUESTED_VERSION: ${{ inputs.Version }} + GITHUB_TOKEN: ${{ github.token }} + run: | + set -e + + echo "Requested version: [$REQUESTED_VERSION]" + + # 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" + ;; + "") + echo "Error: Version input is required (or use 'latest')" + exit 1 + ;; + esac + + DETECTED_VERSION=$(pwsh -NoLogo -NoProfile -Command '$PSVersionTable.PSVersion.ToString()' 2>/dev/null || true) + if [[ "$DETECTED_VERSION" == "$REQUESTED_VERSION" ]]; then + echo "PowerShell $DETECTED_VERSION already installed. Skipping." + exit 0 + fi + + if command -v apt-get >/dev/null; then + echo "Using APT package manager (Debian/Ubuntu)..." + + if ! grep -q "packages.microsoft.com" /etc/apt/sources.list /etc/apt/sources.list.d/* 2>/dev/null; then + wget -qO- https://packages.microsoft.com/keys/microsoft.asc \ + | sudo tee /etc/apt/trusted.gpg.d/microsoft.asc > /dev/null + DIST_CODENAME=$(lsb_release -cs) + sudo add-apt-repository \ + "deb [arch=$(dpkg --print-architecture)] https://packages.microsoft.com/ubuntu/$DIST_CODENAME/prod $DIST_CODENAME main" + fi + + sudo apt-get update -y + EXACT_PKG=$(apt-cache madison powershell | awk '{print $3}' \ + | grep -E "^${REQUESTED_VERSION}(-|$)" | head -n1 || true) + + if [[ -n "$EXACT_PKG" ]]; then + sudo apt-get install -y powershell=$EXACT_PKG + else + ARCH=$(dpkg --print-architecture) + DEB_NAME="powershell_${REQUESTED_VERSION}-1.deb_${ARCH}.deb" + URL="https://github.com/PowerShell/PowerShell/releases/download/v${REQUESTED_VERSION}/${DEB_NAME}" + wget -q "$URL" -O "$DEB_NAME" + sudo dpkg -i "$DEB_NAME" || sudo apt-get -f install -y + fi + else + echo "Unsupported Linux distribution (no apt-get)." + exit 1 + fi + + - name: Install PowerShell (macOS) + if: runner.os == 'macOS' + shell: bash env: - {{ ORG }}_{{ NAME }}_INPUT_subject: ${{ inputs.subject }} - with: - Debug: ${{ inputs.Debug }} - Prerelease: ${{ inputs.Prerelease }} - Verbose: ${{ inputs.Verbose }} - Version: ${{ inputs.Version }} - WorkingDirectory: ${{ inputs.WorkingDirectory }} - Script: | - # {{ NAME }} - ${{ github.action_path }}/scripts/main.ps1 + REQUESTED_VERSION: ${{ inputs.Version }} + GITHUB_TOKEN: ${{ github.token }} + run: | + set -e + + echo "Requested version: [$REQUESTED_VERSION]" + + # Convert to lowercase for comparison (macOS-compatible method) + REQ_VER_LOWER=$(echo "$REQUESTED_VERSION" | tr '[:upper:]' '[:lower:]') + + # Only resolve to latest version if explicitly set to 'latest' + if [[ "$REQ_VER_LOWER" == "latest" ]]; 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/latest | + jq -r '.tag_name' | sed 's/^v//' + ) + echo "Latest stable PowerShell release detected: $REQUESTED_VERSION" + fi + + # Validate REQUESTED_VERSION is not empty + if [[ -z "${REQUESTED_VERSION}" ]]; then + echo "Error: Could not determine a valid PowerShell version." + exit 1 + fi + + DETECTED_VERSION=$(pwsh -NoLogo -NoProfile -Command '$PSVersionTable.PSVersion.ToString()' 2>/dev/null || true) + if [[ "$DETECTED_VERSION" == "$REQUESTED_VERSION" ]]; then + echo "PowerShell $DETECTED_VERSION already installed. Skipping." + exit 0 + fi + + # Try Homebrew first (simplified approach) + if command -v brew >/dev/null; then + echo "Using Homebrew package manager..." + # Homebrew handles 'latest' automatically when no version is specified + if [[ "$REQ_VER_LOWER" == "latest" ]]; then + brew install --cask powershell + else + # Try specific version formula first + if brew install --cask "powershell@$REQUESTED_VERSION" 2>/dev/null; then + echo "Successfully installed via Homebrew" + else + # Fall back to generic formula if versioned one doesn't exist + echo "Version-specific formula not found, installing latest via Homebrew" + brew install --cask powershell + fi + fi + else + # Fall back to direct download + echo "Homebrew not available, downloading directly..." + ARCH=$(uname -m) + case "$ARCH" in + "arm64") PKG_NAME="powershell-${REQUESTED_VERSION}-osx-arm64.pkg" ;; + *) PKG_NAME="powershell-${REQUESTED_VERSION}-osx-x64.pkg" ;; + esac + + URL="https://github.com/PowerShell/PowerShell/releases/download/v${REQUESTED_VERSION}/${PKG_NAME}" + echo "Downloading from: $URL" + if ! curl -sSL "$URL" -o "$PKG_NAME"; then + echo "Error: Failed to download PowerShell package" + exit 1 + fi + sudo installer -pkg "$PKG_NAME" -target / + fi + + - name: Install PowerShell (Windows) + if: runner.os == 'Windows' + shell: powershell + env: + REQUESTED_VERSION: ${{ inputs.Version }} + GITHUB_TOKEN: ${{ github.token }} + run: | + Write-Host "Requested version: [$REQUESTED_VERSION]" + # Only resolve to latest version if explicitly set to 'latest' (case-insensitive) + $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" + $env:REQUESTED_VERSION = $latest + } elseif ([string]::IsNullOrWhiteSpace($req)) { + Write-Host "Error: Version input is required (or use 'latest')" + exit 1 + } + + try { + $detected = (pwsh -NoLogo -NoProfile -Command '$PSVersionTable.PSVersion.ToString()') + } catch { + $detected = $null + } + + Write-Host "Detected PowerShell version: $detected" + Write-Host "Requested PowerShell version: $env:REQUESTED_VERSION" + + if ($detected -eq $env:REQUESTED_VERSION) { + Write-Host "PowerShell $detected already installed. Skipping." + exit 0 + } + + $msi = "PowerShell-$($env:REQUESTED_VERSION)-win-x64.msi" + $url = "https://github.com/PowerShell/PowerShell/releases/download/v$($env:REQUESTED_VERSION)/$msi" + Invoke-WebRequest -Uri $url -OutFile $msi -UseBasicParsing + Start-Process msiexec.exe -ArgumentList '/i', $msi, '/quiet', '/norestart' -Wait diff --git a/scripts/main.ps1 b/scripts/main.ps1 deleted file mode 100644 index cfdee7c..0000000 --- a/scripts/main.ps1 +++ /dev/null @@ -1,24 +0,0 @@ -#Requires -Modules GitHub - -[CmdletBinding()] -param( - [Parameter()] - [string] $Subject = $env:__INPUT_subject -) - -begin { - $scriptName = $MyInvocation.MyCommand.Name - Write-Debug "[$scriptName] - Start" -} - -process { - try { - Write-Output "Hello, $Subject!" - } catch { - throw $_ - } -} - -end { - Write-Debug "[$scriptName] - End" -} diff --git a/tests/README.md b/tests/README.md deleted file mode 100644 index 43816d3..0000000 --- a/tests/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Placeholder for tests - -Location for tests of the action.