Skip to content
Merged
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
7 changes: 6 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,12 @@ jobs:
if: runner.os == 'Linux'
run: bats test/install-release.bats

- name: Test Windows release installer
- name: Test Windows release installer (PowerShell 7)
if: runner.os == 'Windows'
shell: pwsh
run: ./test/install-release.ps1

- name: Test Windows release installer (Windows PowerShell 5.1)
if: runner.os == 'Windows'
shell: powershell
run: ./test/install-release.ps1
7 changes: 6 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -124,11 +124,16 @@ jobs:
if: runner.os == 'Linux'
run: bats test/install-release.bats

- name: Test Windows release installer
- name: Test Windows release installer (PowerShell 7)
if: runner.os == 'Windows'
shell: pwsh
run: ./test/install-release.ps1

- name: Test Windows release installer (Windows PowerShell 5.1)
if: runner.os == 'Windows'
shell: powershell
run: ./test/install-release.ps1

release:
name: Build and publish
needs: [validate, test]
Expand Down
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,10 @@ To install a specific release, replace `vX.Y.Z` with its tag and add
`CODEXOMETER_VERSION=vX.Y.Z` beside the bin-directory setting, or download the
script and pass `--version vX.Y.Z`.

On Windows, download and run the PowerShell installer:
On Windows, download and run the PowerShell installer using either built-in
Windows PowerShell 5.1 (`powershell`) or PowerShell 7 (`pwsh`). There is no need
to upgrade PowerShell to install Codexometer. To check your shell version, run
`$PSVersionTable`.

```powershell
$installer = Join-Path ([IO.Path]::GetTempPath()) "install-codexometer.ps1"
Expand Down
37 changes: 32 additions & 5 deletions install-release.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,29 @@ function Fail {
throw "codexometer installer: $Message"
}

function Resolve-ReleaseArchitecture {
param(
[string]$OSArchitecture,
[string]$NativeArchitecture,
[string]$ProcessArchitecture
)

# Prefer the OS, not the shell architecture. WOW64 exposes the native
# architecture separately when a 32-bit shell runs on 64-bit Windows.
$architecture = $OSArchitecture
if ([string]::IsNullOrWhiteSpace($architecture)) {
$architecture = $NativeArchitecture
}
if ([string]::IsNullOrWhiteSpace($architecture)) {
$architecture = $ProcessArchitecture
}
switch ($architecture) {
{ $_ -in @("X64", "AMD64") } { return "amd64" }
"ARM64" { return "arm64" }
default { Fail "unsupported or undetermined architecture: $architecture" }
}
}

$Version = Get-Setting $Version "CODEXOMETER_VERSION" "latest"
$Repository = Get-Setting $Repository "CODEXOMETER_REPOSITORY" "merefield/codexometer"
$GitHubUrl = (Get-Setting $GitHubUrl "CODEXOMETER_GITHUB_URL" "https://github.com").TrimEnd("/")
Expand Down Expand Up @@ -63,12 +86,16 @@ if ($Version -ne "latest" -and $Version -cnotmatch $semanticTagPattern) {
Fail "invalid semantic release tag: $Version"
}

$architecture = [Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString()
switch ($architecture) {
"X64" { $releaseArch = "amd64" }
"Arm64" { $releaseArch = "arm64" }
default { Fail "unsupported architecture: $architecture" }
$osArchitecture = ""
try {
$osArchitecture = [Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString()
} catch {
# Some Windows PowerShell 5.1/.NET Framework environments do not expose
# this property (or its type). Use Windows' architecture variables instead.
}
$releaseArch = Resolve-ReleaseArchitecture $osArchitecture `
([Environment]::GetEnvironmentVariable("PROCESSOR_ARCHITEW6432")) `
([Environment]::GetEnvironmentVariable("PROCESSOR_ARCHITECTURE"))

[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12
$headers = @{ "User-Agent" = "codexometer-release-installer" }
Expand Down
55 changes: 53 additions & 2 deletions test/install-release.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,64 @@ function Invoke-Installer {
if (-not [string]::IsNullOrWhiteSpace($RequestedVersion)) {
$arguments += @("-Version", $RequestedVersion)
}
$output = & $hostExecutable @arguments 2>&1
# Windows PowerShell turns redirected native stderr into ErrorRecords.
# Expected failing child installers must reach the exit-code assertions.
$savedErrorActionPreference = $ErrorActionPreference
try {
$ErrorActionPreference = "Continue"
$output = & $hostExecutable @arguments 2>&1
$status = $LASTEXITCODE
} finally {
$ErrorActionPreference = $savedErrorActionPreference
}
return @{
Status = $LASTEXITCODE
Status = $status
Output = ($output | Out-String).Trim()
}
}

# Load only the architecture helper and its error helper, without running the
# installer. Exercise missing RuntimeInformation data even on modern .NET.
$parseTokens = $null
$parseErrors = $null
$installerAst = [Management.Automation.Language.Parser]::ParseFile($installer, [ref]$parseTokens, [ref]$parseErrors)
if ($parseErrors.Count -ne 0) {
throw "installer contains PowerShell syntax errors"
}
foreach ($name in @("Fail", "Resolve-ReleaseArchitecture")) {
$functionAst = $installerAst.Find({
param($node)
$node -is [Management.Automation.Language.FunctionDefinitionAst] -and $node.Name -eq $name
}, $true)
if ($null -eq $functionAst) { throw "missing installer function: $name" }
. ([scriptblock]::Create($functionAst.Extent.Text))
}
foreach ($case in @(
@{ OS = "X64"; Native = ""; Process = "AMD64"; Want = "amd64" },
@{ OS = "Arm64"; Native = ""; Process = "AMD64"; Want = "arm64" },
@{ OS = ""; Native = ""; Process = "AMD64"; Want = "amd64" },
@{ OS = ""; Native = ""; Process = "ARM64"; Want = "arm64" },
@{ OS = ""; Native = "AMD64"; Process = "x86"; Want = "amd64" },
@{ OS = ""; Native = "ARM64"; Process = "x86"; Want = "arm64" },
@{ OS = ""; Native = "ARM64"; Process = "AMD64"; Want = "arm64" }
)) {
$actual = Resolve-ReleaseArchitecture $case.OS $case.Native $case.Process
if ($actual -ne $case.Want) { throw "architecture mapping failed: $actual, expected $($case.Want)" }
}
foreach ($case in @(
@{ OS = ""; Native = ""; Process = "" },
@{ OS = ""; Native = ""; Process = "x86" },
@{ OS = ""; Native = "IA64"; Process = "x86" },
@{ OS = "Unknown"; Native = ""; Process = "AMD64" }
)) {
$rejected = $false
try { Resolve-ReleaseArchitecture $case.OS $case.Native $case.Process | Out-Null } catch {
if ($_.Exception.Message -notmatch 'unsupported or undetermined architecture') { throw }
$rejected = $true
}
if (-not $rejected) { throw "unsupported architecture was accepted" }
}

try {
New-Item -ItemType Directory -Path $testRoot | Out-Null
New-Item -ItemType Directory -Path $packageDirectory, $releaseDirectory, $latestDirectory -Force | Out-Null
Expand Down
Loading