Skip to content

Commit f3a395f

Browse files
prabhakk-mwclaude
andcommitted
Add PowerShell install script for Windows (inspired by uv)
One-liner install experience for Windows users: powershell -ExecutionPolicy ByPass -c "irm .../install.ps1 | iex" Features: arch detection via .NET RuntimeInformation, auto PATH management via registry, proxy support, GitHub Actions CI awareness, and VERSION/INSTALL_DIR/MWI_NO_MODIFY_PATH env var overrides. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 5daa1cd commit f3a395f

2 files changed

Lines changed: 292 additions & 2 deletions

File tree

‎docs/installation.md‎

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55
- **MATLAB** installed on the target machine (R2020b or later)
66
- **Linux:** Xvfb (recommended for headless environments)
77

8-
## Install Script (Linux / macOS)
8+
## Install Script
99

10-
The easiest way to install. The script detects your OS and architecture, fetches the latest release from GitHub, and installs the binary to `~/.local/bin`:
10+
The easiest way to install. The script detects your OS and architecture, fetches the latest release from GitHub, and installs the binary to `~/.local/bin`.
11+
12+
### Linux / macOS
1113

1214
```bash
1315
curl -fsSL https://raw.githubusercontent.com/prabhakk-mw/matlab-proxy-go/main/install.sh | sh
@@ -28,6 +30,27 @@ curl -fsSL https://raw.githubusercontent.com/prabhakk-mw/matlab-proxy-go/main/in
2830

2931
The script uses `sudo` only if the install directory is not writable by the current user.
3032

33+
### Windows (PowerShell)
34+
35+
```powershell
36+
powershell -ExecutionPolicy ByPass -c "irm https://raw.githubusercontent.com/prabhakk-mw/matlab-proxy-go/main/install.ps1 | iex"
37+
```
38+
39+
Customize with environment variables:
40+
41+
```powershell
42+
# Install a specific version
43+
$env:VERSION = "0.5.1"; irm https://raw.githubusercontent.com/prabhakk-mw/matlab-proxy-go/main/install.ps1 | iex
44+
45+
# Install to a custom directory
46+
$env:INSTALL_DIR = "C:\Tools"; irm https://raw.githubusercontent.com/prabhakk-mw/matlab-proxy-go/main/install.ps1 | iex
47+
48+
# Don't modify PATH
49+
$env:MWI_NO_MODIFY_PATH = "1"; irm https://raw.githubusercontent.com/prabhakk-mw/matlab-proxy-go/main/install.ps1 | iex
50+
```
51+
52+
The script automatically adds the install directory to your user-level `PATH` via the Windows registry. Restart your shell or run the printed command to pick up the change immediately.
53+
3154
## Download a Pre-Built Binary
3255

3356
Pre-built binaries for Linux, macOS, and Windows are available on the [GitHub Releases](https://github.com/prabhakk-mw/matlab-proxy-go/releases) page.

‎install.ps1‎

Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
# Copyright 2026 The MathWorks, Inc.
2+
#
3+
# Install script for matlab-proxy on Windows.
4+
# Downloads the latest release binary from GitHub and installs it.
5+
#
6+
# Usage:
7+
# powershell -ExecutionPolicy ByPass -c "irm https://raw.githubusercontent.com/prabhakk-mw/matlab-proxy-go/main/install.ps1 | iex"
8+
#
9+
# Options (via environment variables):
10+
# VERSION Install a specific version (e.g. 0.5.1)
11+
# INSTALL_DIR Install to a custom directory
12+
# MWI_NO_MODIFY_PATH=1 Don't add the install directory to PATH
13+
14+
<#
15+
.SYNOPSIS
16+
Installer for matlab-proxy.
17+
18+
.DESCRIPTION
19+
This script detects your platform architecture and downloads the appropriate
20+
matlab-proxy binary from GitHub releases, then installs it to the first of:
21+
22+
$env:INSTALL_DIR (if set)
23+
$env:XDG_BIN_HOME
24+
$HOME\.local\bin
25+
26+
It will then add that directory to PATH by editing the user-level
27+
Environment registry key.
28+
29+
.PARAMETER NoModifyPath
30+
Don't add the install directory to PATH
31+
32+
.PARAMETER Help
33+
Print help
34+
#>
35+
36+
param (
37+
[Parameter(HelpMessage = "Don't add the install directory to PATH")]
38+
[switch]$NoModifyPath,
39+
[Parameter(HelpMessage = "Print Help")]
40+
[switch]$Help
41+
)
42+
43+
$app_name = 'matlab-proxy'
44+
$repo = 'prabhakk-mw/matlab-proxy-go'
45+
46+
if ($env:MWI_NO_MODIFY_PATH) {
47+
$NoModifyPath = $true
48+
}
49+
50+
function Install-Binary($install_args) {
51+
if ($Help) {
52+
Get-Help $PSCommandPath -Detailed
53+
Exit
54+
}
55+
56+
Initialize-Environment
57+
58+
$arch = Get-Arch
59+
$version = Get-Version
60+
61+
$tag = "v$version"
62+
$artifact = "$app_name-$tag-windows-$arch.zip"
63+
$url = "https://github.com/$repo/releases/download/$tag/$artifact"
64+
65+
Write-Information "Installing $app_name $version (windows/$arch)..."
66+
Write-Information " From: $url"
67+
68+
# Download and extract
69+
$tmp = New-Temp-Dir
70+
$zip_path = Join-Path $tmp $artifact
71+
72+
try {
73+
$wc = New-Object Net.WebClient
74+
$proxy = Get-WebProxyFromEnvironment
75+
if ($null -ne $proxy) {
76+
$wc.Proxy = $proxy
77+
}
78+
$wc.DownloadFile($url, $zip_path)
79+
} catch {
80+
throw "ERROR: failed to download $url`n$_"
81+
}
82+
83+
Expand-Archive -Path $zip_path -DestinationPath $tmp
84+
85+
# Determine install directory
86+
$dest_dir = $null
87+
if ($env:INSTALL_DIR) {
88+
$dest_dir = $env:INSTALL_DIR
89+
}
90+
if (-not $dest_dir) {
91+
$dest_dir = if ($env:XDG_BIN_HOME) { $env:XDG_BIN_HOME } else { $null }
92+
}
93+
if (-not $dest_dir) {
94+
$dest_dir = if ($HOME) { Join-Path $HOME ".local\bin" } else { $null }
95+
}
96+
if (-not $dest_dir) {
97+
throw "ERROR: could not determine install directory. Set INSTALL_DIR."
98+
}
99+
100+
$dest_dir = New-Item -Force -ItemType Directory -Path $dest_dir
101+
Write-Information " To: $dest_dir\$app_name.exe"
102+
103+
Copy-Item (Join-Path $tmp "$app_name.exe") -Destination $dest_dir -ErrorAction Stop
104+
105+
# Clean up temp directory
106+
Remove-Item $tmp -Recurse -Force -ErrorAction SilentlyContinue
107+
108+
Write-Information ""
109+
Write-Information "$app_name $version installed successfully!"
110+
111+
# Verify installation
112+
$installed_bin = Join-Path $dest_dir "$app_name.exe"
113+
& $installed_bin --version
114+
115+
# Update PATH
116+
if (-not $NoModifyPath) {
117+
Add-Ci-Path $dest_dir
118+
if (Add-Path $dest_dir) {
119+
Write-Information ""
120+
Write-Information "To add $dest_dir to your PATH, either restart your shell or run:"
121+
Write-Information ""
122+
Write-Information " set Path=$dest_dir;%Path% (cmd)"
123+
Write-Information " `$env:Path = `"$dest_dir;`$env:Path`" (powershell)"
124+
}
125+
}
126+
}
127+
128+
function Get-Version() {
129+
if ($env:VERSION) {
130+
return $env:VERSION
131+
}
132+
133+
Write-Information "Fetching latest release..."
134+
try {
135+
$release = Invoke-RestMethod -Uri "https://api.github.com/repos/$repo/releases/latest" -ErrorAction Stop
136+
$tag = $release.tag_name
137+
if ($tag -match '^v(.+)$') {
138+
return $Matches[1]
139+
}
140+
throw "Unexpected tag format: $tag"
141+
} catch {
142+
throw "ERROR: could not determine latest version. Set VERSION manually.`n$_"
143+
}
144+
}
145+
146+
function Get-Arch() {
147+
try {
148+
# Use .NET to detect OS architecture
149+
# Works correctly on PowerShell Core 7.3+ and Windows PowerShell on Win 11 22H2+
150+
$a = [System.Reflection.Assembly]::LoadWithPartialName("System.Runtime.InteropServices.RuntimeInformation")
151+
$t = $a.GetType("System.Runtime.InteropServices.RuntimeInformation")
152+
$p = $t.GetProperty("OSArchitecture")
153+
switch ($p.GetValue($null).ToString()) {
154+
"X64" { return "amd64" }
155+
"Arm64" { return "arm64" }
156+
}
157+
} catch {
158+
Write-Verbose "Could not detect architecture via RuntimeInformation: $_"
159+
}
160+
161+
# Fallback for older .NET
162+
if ([System.Environment]::Is64BitOperatingSystem) {
163+
return "amd64"
164+
}
165+
166+
throw "ERROR: unsupported architecture. Only 64-bit Windows is supported."
167+
}
168+
169+
function Get-WebProxyFromEnvironment() {
170+
$proxy_url = if ($env:HTTPS_PROXY) { $env:HTTPS_PROXY } else { $env:ALL_PROXY }
171+
if ([string]::IsNullOrWhiteSpace($proxy_url)) {
172+
return $null
173+
}
174+
try {
175+
$uri = [System.Uri]$proxy_url
176+
$proxy = New-Object System.Net.WebProxy($uri)
177+
if (-not [string]::IsNullOrEmpty($uri.UserInfo)) {
178+
$parts = $uri.UserInfo.Split(':')
179+
$user = [System.Uri]::UnescapeDataString($parts[0])
180+
$pass = if ($null -eq $parts[1]) { "" } else { [System.Uri]::UnescapeDataString($parts[1]) }
181+
$proxy.Credentials = New-Object System.Net.NetworkCredential($user, $pass)
182+
}
183+
return $proxy
184+
} catch {
185+
Write-Verbose "Failed to parse proxy URL: $_"
186+
return $null
187+
}
188+
}
189+
190+
function Initialize-Environment() {
191+
if (($PSVersionTable.PSVersion.Major) -lt 5) {
192+
throw @"
193+
Error: PowerShell 5 or later is required to install $app_name.
194+
Upgrade PowerShell:
195+
196+
https://docs.microsoft.com/en-us/powershell/scripting/setup/installing-windows-powershell
197+
198+
"@
199+
}
200+
201+
$allowedExecutionPolicy = @('Unrestricted', 'RemoteSigned', 'Bypass')
202+
if ((Get-ExecutionPolicy).ToString() -notin $allowedExecutionPolicy) {
203+
throw @"
204+
Error: PowerShell requires an execution policy in [$($allowedExecutionPolicy -join ", ")] to run $app_name. For example, to set the execution policy to 'RemoteSigned' please run:
205+
206+
Set-ExecutionPolicy RemoteSigned -scope CurrentUser
207+
208+
"@
209+
}
210+
211+
if ([System.Enum]::GetNames([System.Net.SecurityProtocolType]) -notcontains 'Tls12') {
212+
throw @"
213+
Error: Installing $app_name requires at least .NET Framework 4.5
214+
Please download and install it first:
215+
216+
https://www.microsoft.com/net/download
217+
218+
"@
219+
}
220+
}
221+
222+
# Add to PATH in GitHub Actions
223+
function Add-Ci-Path($PathToAdd) {
224+
if (($gh_path = $env:GITHUB_PATH)) {
225+
Write-Output "$PathToAdd" | Out-File -FilePath "$gh_path" -Encoding utf8 -Append
226+
}
227+
}
228+
229+
# Permanently add to user-level PATH via the registry
230+
# Returns $true if the registry was modified, $false if already on PATH
231+
function Add-Path($LiteralPath) {
232+
$RegistryPath = 'registry::HKEY_CURRENT_USER\Environment'
233+
$CurrentDirectories = (Get-Item -LiteralPath $RegistryPath).GetValue('Path', '', 'DoNotExpandEnvironmentNames') -split ';' -ne ''
234+
235+
if ($LiteralPath -in $CurrentDirectories) {
236+
return $false
237+
}
238+
239+
$NewPath = (,$LiteralPath + $CurrentDirectories) -join ';'
240+
Set-ItemProperty -Type ExpandString -LiteralPath $RegistryPath Path $NewPath
241+
242+
# Broadcast WM_SETTINGCHANGE so the shell reloads the updated PATH
243+
$DummyName = 'matlab-proxy-' + [guid]::NewGuid().ToString()
244+
[Environment]::SetEnvironmentVariable($DummyName, 'dummy', 'User')
245+
[Environment]::SetEnvironmentVariable($DummyName, [NullString]::value, 'User')
246+
247+
return $true
248+
}
249+
250+
function New-Temp-Dir() {
251+
[CmdletBinding(SupportsShouldProcess)]
252+
param()
253+
$parent = [System.IO.Path]::GetTempPath()
254+
[string] $name = [System.Guid]::NewGuid()
255+
New-Item -ItemType Directory -Path (Join-Path $parent $name)
256+
}
257+
258+
# Suppress PSScriptAnalyzer warnings for globals
259+
$Null = $NoModifyPath, $Help
260+
$InformationPreference = "Continue"
261+
262+
try {
263+
Install-Binary "$Args"
264+
} catch {
265+
Write-Information $_
266+
exit 1
267+
}

0 commit comments

Comments
 (0)