Skip to content

Commit d784859

Browse files
committed
Embed bun.sh scripts
Signed-off-by: fpv.dev <kich@octra.org>
1 parent a041fb5 commit d784859

File tree

4 files changed

+635
-25
lines changed

4 files changed

+635
-25
lines changed

scripts/install_bun.ps1

Lines changed: 311 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,311 @@
1+
#!/usr/bin/env pwsh
2+
param(
3+
# TODO: change this to 'latest' when Bun for Windows is stable.
4+
[String]$Version = "latest",
5+
# Forces installing the baseline build regardless of what CPU you are actually using.
6+
[Switch]$ForceBaseline = $false,
7+
# Skips adding the bun.exe directory to the user's %PATH%
8+
[Switch]$NoPathUpdate = $false,
9+
# Skips adding the bun to the list of installed programs
10+
[Switch]$NoRegisterInstallation = $false,
11+
# Skips installing powershell completions to your profile
12+
[Switch]$NoCompletions = $false,
13+
14+
# Debugging: Always download with 'Invoke-RestMethod' instead of 'curl.exe'
15+
[Switch]$DownloadWithoutCurl = $false
16+
);
17+
18+
# filter out 32 bit + ARM
19+
if (-not ((Get-CimInstance Win32_ComputerSystem)).SystemType -match "x64-based") {
20+
Write-Output "Install Failed:"
21+
Write-Output "Bun for Windows is currently only available for x86 64-bit Windows.`n"
22+
return 1
23+
}
24+
25+
# This corresponds to .win10_rs5 in build.zig
26+
$MinBuild = 17763;
27+
$MinBuildName = "Windows 10 1809"
28+
29+
$WinVer = [System.Environment]::OSVersion.Version
30+
if ($WinVer.Major -lt 10 -or ($WinVer.Major -eq 10 -and $WinVer.Build -lt $MinBuild)) {
31+
Write-Warning "Bun requires at ${MinBuildName} or newer.`n`nThe install will still continue but it may not work.`n"
32+
return 1
33+
}
34+
35+
$ErrorActionPreference = "Stop"
36+
37+
# These three environment functions are roughly copied from https://github.com/prefix-dev/pixi/pull/692
38+
# They are used instead of `SetEnvironmentVariable` because of unwanted variable expansions.
39+
function Publish-Env {
40+
if (-not ("Win32.NativeMethods" -as [Type])) {
41+
Add-Type -Namespace Win32 -Name NativeMethods -MemberDefinition @"
42+
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
43+
public static extern IntPtr SendMessageTimeout(
44+
IntPtr hWnd, uint Msg, UIntPtr wParam, string lParam,
45+
uint fuFlags, uint uTimeout, out UIntPtr lpdwResult);
46+
"@
47+
}
48+
$HWND_BROADCAST = [IntPtr] 0xffff
49+
$WM_SETTINGCHANGE = 0x1a
50+
$result = [UIntPtr]::Zero
51+
[Win32.NativeMethods]::SendMessageTimeout($HWND_BROADCAST,
52+
$WM_SETTINGCHANGE,
53+
[UIntPtr]::Zero,
54+
"Environment",
55+
2,
56+
5000,
57+
[ref] $result
58+
) | Out-Null
59+
}
60+
61+
function Write-Env {
62+
param([String]$Key, [String]$Value)
63+
64+
$RegisterKey = Get-Item -Path 'HKCU:'
65+
66+
$EnvRegisterKey = $RegisterKey.OpenSubKey('Environment', $true)
67+
if ($null -eq $Value) {
68+
$EnvRegisterKey.DeleteValue($Key)
69+
} else {
70+
$RegistryValueKind = if ($Value.Contains('%')) {
71+
[Microsoft.Win32.RegistryValueKind]::ExpandString
72+
} elseif ($EnvRegisterKey.GetValue($Key)) {
73+
$EnvRegisterKey.GetValueKind($Key)
74+
} else {
75+
[Microsoft.Win32.RegistryValueKind]::String
76+
}
77+
$EnvRegisterKey.SetValue($Key, $Value, $RegistryValueKind)
78+
}
79+
80+
Publish-Env
81+
}
82+
83+
function Get-Env {
84+
param([String] $Key)
85+
86+
$RegisterKey = Get-Item -Path 'HKCU:'
87+
$EnvRegisterKey = $RegisterKey.OpenSubKey('Environment')
88+
$EnvRegisterKey.GetValue($Key, $null, [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames)
89+
}
90+
91+
# The installation of bun is it's own function so that in the unlikely case the $IsBaseline check fails, we can do a recursive call.
92+
# There are also lots of sanity checks out of fear of anti-virus software or other weird Windows things happening.
93+
function Install-Bun {
94+
param(
95+
[string]$Version,
96+
[bool]$ForceBaseline = $False
97+
);
98+
99+
# if a semver is given, we need to adjust it to this format: bun-v0.0.0
100+
if ($Version -match "^\d+\.\d+\.\d+$") {
101+
$Version = "bun-v$Version"
102+
}
103+
elseif ($Version -match "^v\d+\.\d+\.\d+$") {
104+
$Version = "bun-$Version"
105+
}
106+
107+
$Arch = "x64"
108+
$IsBaseline = $ForceBaseline
109+
if (!$IsBaseline) {
110+
$IsBaseline = !( `
111+
Add-Type -MemberDefinition '[DllImport("kernel32.dll")] public static extern bool IsProcessorFeaturePresent(int ProcessorFeature);' `
112+
-Name 'Kernel32' -Namespace 'Win32' -PassThru `
113+
)::IsProcessorFeaturePresent(40);
114+
}
115+
116+
$BunRoot = if ($env:BUN_INSTALL) { $env:BUN_INSTALL } else { "${Home}\.bun" }
117+
$BunBin = mkdir -Force "${BunRoot}\bin"
118+
119+
try {
120+
Remove-Item "${BunBin}\bun.exe" -Force
121+
} catch [System.Management.Automation.ItemNotFoundException] {
122+
# ignore
123+
} catch [System.UnauthorizedAccessException] {
124+
$openProcesses = Get-Process -Name bun | Where-Object { $_.Path -eq "${BunBin}\bun.exe" }
125+
if ($openProcesses.Count -gt 0) {
126+
Write-Output "Install Failed - An older installation exists and is open. Please close open Bun processes and try again."
127+
return 1
128+
}
129+
Write-Output "Install Failed - An unknown error occurred while trying to remove the existing installation"
130+
Write-Output $_
131+
return 1
132+
} catch {
133+
Write-Output "Install Failed - An unknown error occurred while trying to remove the existing installation"
134+
Write-Output $_
135+
return 1
136+
}
137+
138+
$Target = "bun-windows-$Arch"
139+
if ($IsBaseline) {
140+
$Target = "bun-windows-$Arch-baseline"
141+
}
142+
$BaseURL = "https://github.com/oven-sh/bun/releases"
143+
$URL = "$BaseURL/$(if ($Version -eq "latest") { "latest/download" } else { "download/$Version" })/$Target.zip"
144+
145+
$ZipPath = "${BunBin}\$Target.zip"
146+
147+
$DisplayVersion = $(
148+
if ($Version -eq "latest") { "Bun" }
149+
elseif ($Version -eq "canary") { "Bun Canary" }
150+
elseif ($Version -match "^bun-v\d+\.\d+\.\d+$") { "Bun $($Version.Substring(4))" }
151+
else { "Bun tag='${Version}'" }
152+
)
153+
154+
$null = mkdir -Force $BunBin
155+
Remove-Item -Force $ZipPath -ErrorAction SilentlyContinue
156+
157+
# curl.exe is faster than PowerShell 5's 'Invoke-WebRequest'
158+
# note: 'curl' is an alias to 'Invoke-WebRequest'. so the exe suffix is required
159+
if (-not $DownloadWithoutCurl) {
160+
curl.exe "-#SfLo" "$ZipPath" "$URL"
161+
}
162+
if ($DownloadWithoutCurl -or ($LASTEXITCODE -ne 0)) {
163+
Write-Warning "The command 'curl.exe $URL -o $ZipPath' exited with code ${LASTEXITCODE}`nTrying an alternative download method..."
164+
try {
165+
# Use Invoke-RestMethod instead of Invoke-WebRequest because Invoke-WebRequest breaks on
166+
# some machines, see
167+
Invoke-RestMethod -Uri $URL -OutFile $ZipPath
168+
} catch {
169+
Write-Output "Install Failed - could not download $URL"
170+
Write-Output "The command 'Invoke-RestMethod $URL -OutFile $ZipPath' exited with code ${LASTEXITCODE}`n"
171+
return 1
172+
}
173+
}
174+
175+
if (!(Test-Path $ZipPath)) {
176+
Write-Output "Install Failed - could not download $URL"
177+
Write-Output "The file '$ZipPath' does not exist. Did an antivirus delete it?`n"
178+
return 1
179+
}
180+
181+
try {
182+
$lastProgressPreference = $global:ProgressPreference
183+
$global:ProgressPreference = 'SilentlyContinue';
184+
Expand-Archive "$ZipPath" "$BunBin" -Force
185+
$global:ProgressPreference = $lastProgressPreference
186+
if (!(Test-Path "${BunBin}\$Target\bun.exe")) {
187+
throw "The file '${BunBin}\$Target\bun.exe' does not exist. Download is corrupt or intercepted Antivirus?`n"
188+
}
189+
} catch {
190+
Write-Output "Install Failed - could not unzip $ZipPath"
191+
Write-Error $_
192+
return 1
193+
}
194+
195+
Move-Item "${BunBin}\$Target\bun.exe" "${BunBin}\bun.exe" -Force
196+
197+
Remove-Item "${BunBin}\$Target" -Recurse -Force
198+
Remove-Item $ZipPath -Force
199+
200+
$BunRevision = "$(& "${BunBin}\bun.exe" --revision)"
201+
if ($LASTEXITCODE -eq 1073741795) { # STATUS_ILLEGAL_INSTRUCTION
202+
if ($IsBaseline) {
203+
Write-Output "Install Failed - bun.exe (baseline) is not compatible with your CPU.`n"
204+
Write-Output "Please open a GitHub issue with your CPU model:`nhttps://github.com/oven-sh/bun/issues/new/choose`n"
205+
return 1
206+
}
207+
208+
Write-Output "Install Failed - bun.exe is not compatible with your CPU. This should have been detected before downloading.`n"
209+
Write-Output "Attempting to download bun.exe (baseline) instead.`n"
210+
211+
Install-Bun -Version $Version -ForceBaseline $True
212+
return 1
213+
}
214+
# '-1073741515' was spotted in the wild, but not clearly documented as a status code:
215+
# https://discord.com/channels/876711213126520882/1149339379446325248/1205194965383250081
216+
# http://community.sqlbackupandftp.com/t/error-1073741515-solved/1305
217+
if (($LASTEXITCODE -eq 3221225781) -or ($LASTEXITCODE -eq -1073741515)) # STATUS_DLL_NOT_FOUND
218+
{
219+
# TODO: as of July 2024, Bun has no external dependencies.
220+
# I want to keep this error message in for a few months to ensure that
221+
# if someone somehow runs into this, it can be reported.
222+
Write-Output "Install Failed - You are missing a DLL required to run bun.exe"
223+
Write-Output "This can be solved by installing the Visual C++ Redistributable from Microsoft:`nSee https://learn.microsoft.com/cpp/windows/latest-supported-vc-redist`nDirect Download -> https://aka.ms/vs/17/release/vc_redist.x64.exe`n`n"
224+
Write-Output "The error above should be unreachable as Bun does not depend on this library. Please comment in https://github.com/oven-sh/bun/issues/8598 or open a new issue.`n`n"
225+
Write-Output "The command '${BunBin}\bun.exe --revision' exited with code ${LASTEXITCODE}`n"
226+
return 1
227+
}
228+
if ($LASTEXITCODE -ne 0) {
229+
Write-Output "Install Failed - could not verify bun.exe"
230+
Write-Output "The command '${BunBin}\bun.exe --revision' exited with code ${LASTEXITCODE}`n"
231+
return 1
232+
}
233+
234+
try {
235+
$env:IS_BUN_AUTO_UPDATE = "1"
236+
# TODO: When powershell completions are added, make this switch actually do something
237+
if ($NoCompletions) {
238+
$env:BUN_NO_INSTALL_COMPLETIONS = "1"
239+
}
240+
# This completions script in general will install some extra stuff, mainly the `bunx` link.
241+
# It also installs completions.
242+
$output = "$(& "${BunBin}\bun.exe" completions 2>&1)"
243+
if ($LASTEXITCODE -ne 0) {
244+
Write-Output $output
245+
Write-Output "Install Failed - could not finalize installation"
246+
Write-Output "The command '${BunBin}\bun.exe completions' exited with code ${LASTEXITCODE}`n"
247+
return 1
248+
}
249+
} catch {
250+
# it is possible on powershell 5 that an error happens, but it is probably fine?
251+
}
252+
$env:IS_BUN_AUTO_UPDATE = $null
253+
$env:BUN_NO_INSTALL_COMPLETIONS = $null
254+
255+
$DisplayVersion = if ($BunRevision -like "*-canary.*") {
256+
"${BunRevision}"
257+
} else {
258+
"$(& "${BunBin}\bun.exe" --version)"
259+
}
260+
261+
$C_RESET = [char]27 + "[0m"
262+
$C_GREEN = [char]27 + "[1;32m"
263+
264+
Write-Output "${C_GREEN}Bun ${DisplayVersion} was installed successfully!${C_RESET}"
265+
Write-Output "The binary is located at ${BunBin}\bun.exe`n"
266+
267+
$hasExistingOther = $false;
268+
try {
269+
$existing = Get-Command bun -ErrorAction
270+
if ($existing.Source -ne "${BunBin}\bun.exe") {
271+
Write-Warning "Note: Another bun.exe is already in %PATH% at $($existing.Source)`nTyping 'bun' in your terminal will not use what was just installed.`n"
272+
$hasExistingOther = $true;
273+
}
274+
} catch {}
275+
276+
if (-not $NoRegisterInstallation) {
277+
$rootKey = $null
278+
try {
279+
$RegistryKey = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\Bun"
280+
$rootKey = New-Item -Path $RegistryKey -Force
281+
New-ItemProperty -Path $RegistryKey -Name "DisplayName" -Value "Bun" -PropertyType String -Force | Out-Null
282+
New-ItemProperty -Path $RegistryKey -Name "InstallLocation" -Value "${BunRoot}" -PropertyType String -Force | Out-Null
283+
New-ItemProperty -Path $RegistryKey -Name "DisplayIcon" -Value $BunBin\bun.exe -PropertyType String -Force | Out-Null
284+
New-ItemProperty -Path $RegistryKey -Name "UninstallString" -Value "powershell -c `"& `'$BunRoot\uninstall.ps1`' -PauseOnError`" -ExecutionPolicy Bypass" -PropertyType String -Force | Out-Null
285+
} catch {
286+
if ($rootKey -ne $null) {
287+
Remove-Item -Path $RegistryKey -Force
288+
}
289+
}
290+
}
291+
292+
if(!$hasExistingOther) {
293+
# Only try adding to path if there isn't already a bun.exe in the path
294+
$Path = (Get-Env -Key "Path") -split ';'
295+
if ($Path -notcontains $BunBin) {
296+
if (-not $NoPathUpdate) {
297+
$Path += $BunBin
298+
Write-Env -Key 'Path' -Value ($Path -join ';')
299+
$env:PATH = $Path;
300+
} else {
301+
Write-Output "Skipping adding '${BunBin}' to the user's %PATH%`n"
302+
}
303+
}
304+
305+
Write-Output "To get started, restart your terminal/editor, then type `"bun`"`n"
306+
}
307+
308+
$LASTEXITCODE = 0;
309+
}
310+
311+
Install-Bun -Version $Version -ForceBaseline $ForceBaseline

0 commit comments

Comments
 (0)