Skip to content

Commit

Permalink
Windows Pin Bun version (#3192)
Browse files Browse the repository at this point in the history
  • Loading branch information
ElijahAhianyo authored May 1, 2024
1 parent 19d8f6c commit 93c4f50
Show file tree
Hide file tree
Showing 3 changed files with 319 additions and 3 deletions.
6 changes: 5 additions & 1 deletion reflex/constants/installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class Bun(SimpleNamespace):
"""Bun constants."""

# The Bun version.
VERSION = "1.1.5"
VERSION = "1.1.6"
# Min Bun Version
MIN_VERSION = "0.7.0"
# The directory to store the bun.
Expand All @@ -46,6 +46,10 @@ class Bun(SimpleNamespace):
)
# URL to bun install script.
INSTALL_URL = "https://bun.sh/install"
# URL to windows install script.
WINDOWS_INSTALL_URL = (
"https://raw.githubusercontent.com/reflex-dev/reflex/main/scripts/install.ps1"
)


# FNM config.
Expand Down
11 changes: 9 additions & 2 deletions reflex/utils/prerequisites.py
Original file line number Diff line number Diff line change
Expand Up @@ -743,8 +743,15 @@ def install_bun():
# if unzip is installed
if constants.IS_WINDOWS:
processes.new_process(
["powershell", "-c", f"irm {constants.Bun.INSTALL_URL}.ps1|iex"],
env={"BUN_INSTALL": constants.Bun.ROOT_PATH},
[
"powershell",
"-c",
f"irm {constants.Bun.INSTALL_URL}.ps1|iex",
], # TODO: change install url to constants.BUN.WINDOWS_INSTALL_URL
env={
"BUN_INSTALL": constants.Bun.ROOT_PATH,
"BUN_VERSION": constants.Bun.VERSION,
},
shell=True,
run=True,
show_logs=console.is_debug(),
Expand Down
305 changes: 305 additions & 0 deletions scripts/install.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,305 @@
#!/usr/bin/env pwsh
param(
# Forces installing the baseline build regardless of what CPU you are actually using.
[Switch]$ForceBaseline = $false,
# Skips adding the bun.exe directory to the user's %PATH%
[Switch]$NoPathUpdate = $false,
# Skips adding the bun to the list of installed programs
[Switch]$NoRegisterInstallation = $false,
# Skips installing powershell completions to your profile
[Switch]$NoCompletions = $false,

# Debugging: Always download with 'Invoke-RestMethod' instead of 'curl.exe'
[Switch]$DownloadWithoutCurl = $false
);
$Version = if ($env:BUN_VERSION) { $env:BUN_VERSION } else { "latest" }
# filter out 32 bit + ARM
if (-not ((Get-CimInstance Win32_ComputerSystem)).SystemType -match "x64-based") {
Write-Output "Install Failed:"
Write-Output "Bun for Windows is currently only available for x86 64-bit Windows.`n"
return 1
}

# This corresponds to .win10_rs5 in build.zig
$MinBuild = 17763;
$MinBuildName = "Windows 10 1809"

$WinVer = [System.Environment]::OSVersion.Version
if ($WinVer.Major -lt 10 -or ($WinVer.Major -eq 10 -and $WinVer.Build -lt $MinBuild)) {
Write-Warning "Bun requires at ${MinBuildName} or newer.`n`nThe install will still continue but it may not work.`n"
return 1
}

$ErrorActionPreference = "Stop"

# These three environment functions are roughly copied from https://github.com/prefix-dev/pixi/pull/692
# They are used instead of `SetEnvironmentVariable` because of unwanted variable expansions.
function Publish-Env {
if (-not ("Win32.NativeMethods" -as [Type])) {
Add-Type -Namespace Win32 -Name NativeMethods -MemberDefinition @"
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern IntPtr SendMessageTimeout(
IntPtr hWnd, uint Msg, UIntPtr wParam, string lParam,
uint fuFlags, uint uTimeout, out UIntPtr lpdwResult);
"@
}
$HWND_BROADCAST = [IntPtr] 0xffff
$WM_SETTINGCHANGE = 0x1a
$result = [UIntPtr]::Zero
[Win32.NativeMethods]::SendMessageTimeout($HWND_BROADCAST,
$WM_SETTINGCHANGE,
[UIntPtr]::Zero,
"Environment",
2,
5000,
[ref] $result
) | Out-Null
}

function Write-Env {
param([String]$Key, [String]$Value)

$RegisterKey = Get-Item -Path 'HKCU:'

$EnvRegisterKey = $RegisterKey.OpenSubKey('Environment', $true)
if ($null -eq $Value) {
$EnvRegisterKey.DeleteValue($Key)
} else {
$RegistryValueKind = if ($Value.Contains('%')) {
[Microsoft.Win32.RegistryValueKind]::ExpandString
} elseif ($EnvRegisterKey.GetValue($Key)) {
$EnvRegisterKey.GetValueKind($Key)
} else {
[Microsoft.Win32.RegistryValueKind]::String
}
$EnvRegisterKey.SetValue($Key, $Value, $RegistryValueKind)
}

Publish-Env
}

function Get-Env {
param([String] $Key)

$RegisterKey = Get-Item -Path 'HKCU:'
$EnvRegisterKey = $RegisterKey.OpenSubKey('Environment')
$EnvRegisterKey.GetValue($Key, $null, [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames)
}

# 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.
# There are also lots of sanity checks out of fear of anti-virus software or other weird Windows things happening.
function Install-Bun {
param(
[string]$Version,
[bool]$ForceBaseline = $False
);

# if a semver is given, we need to adjust it to this format: bun-v0.0.0
if ($Version -match "^\d+\.\d+\.\d+$") {
$Version = "bun-v$Version"
}
elseif ($Version -match "^v\d+\.\d+\.\d+$") {
$Version = "bun-$Version"
}

$Arch = "x64"
$IsBaseline = $ForceBaseline
if (!$IsBaseline) {
$IsBaseline = !( `
Add-Type -MemberDefinition '[DllImport("kernel32.dll")] public static extern bool IsProcessorFeaturePresent(int ProcessorFeature);' `
-Name 'Kernel32' -Namespace 'Win32' -PassThru `
)::IsProcessorFeaturePresent(40);
}

$BunRoot = if ($env:BUN_INSTALL) { $env:BUN_INSTALL } else { "${Home}\.bun" }
$BunBin = mkdir -Force "${BunRoot}\bin"

try {
Remove-Item "${BunBin}\bun.exe" -Force
} catch [System.Management.Automation.ItemNotFoundException] {
# ignore
} catch [System.UnauthorizedAccessException] {
$openProcesses = Get-Process -Name bun | Where-Object { $_.Path -eq "${BunBin}\bun.exe" }
if ($openProcesses.Count -gt 0) {
Write-Output "Install Failed - An older installation exists and is open. Please close open Bun processes and try again."
return 1
}
Write-Output "Install Failed - An unknown error occurred while trying to remove the existing installation"
Write-Output $_
return 1
} catch {
Write-Output "Install Failed - An unknown error occurred while trying to remove the existing installation"
Write-Output $_
return 1
}

$Target = "bun-windows-$Arch"
if ($IsBaseline) {
$Target = "bun-windows-$Arch-baseline"
}
$BaseURL = "https://github.com/oven-sh/bun/releases"
$URL = "$BaseURL/$(if ($Version -eq "latest") { "latest/download" } else { "download/$Version" })/$Target.zip"

$ZipPath = "${BunBin}\$Target.zip"

$DisplayVersion = $(
if ($Version -eq "latest") { "Bun" }
elseif ($Version -eq "canary") { "Bun Canary" }
elseif ($Version -match "^bun-v\d+\.\d+\.\d+$") { "Bun $($Version.Substring(4))" }
else { "Bun tag='${Version}'" }
)

$null = mkdir -Force $BunBin
Remove-Item -Force $ZipPath -ErrorAction SilentlyContinue

# curl.exe is faster than PowerShell 5's 'Invoke-WebRequest'
# note: 'curl' is an alias to 'Invoke-WebRequest'. so the exe suffix is required
if (-not $DownloadWithoutCurl) {
curl.exe "-#SfLo" "$ZipPath" "$URL"
}
if ($DownloadWithoutCurl -or ($LASTEXITCODE -ne 0)) {
Write-Warning "The command 'curl.exe $URL -o $ZipPath' exited with code ${LASTEXITCODE}`nTrying an alternative download method..."
try {
# Use Invoke-RestMethod instead of Invoke-WebRequest because Invoke-WebRequest breaks on
# some machines, see
Invoke-RestMethod -Uri $URL -OutFile $ZipPath
} catch {
Write-Output "Install Failed - could not download $URL"
Write-Output "The command 'Invoke-RestMethod $URL -OutFile $ZipPath' exited with code ${LASTEXITCODE}`n"
return 1
}
}

if (!(Test-Path $ZipPath)) {
Write-Output "Install Failed - could not download $URL"
Write-Output "The file '$ZipPath' does not exist. Did an antivirus delete it?`n"
return 1
}

try {
$lastProgressPreference = $global:ProgressPreference
$global:ProgressPreference = 'SilentlyContinue';
Expand-Archive "$ZipPath" "$BunBin" -Force
$global:ProgressPreference = $lastProgressPreference
if (!(Test-Path "${BunBin}\$Target\bun.exe")) {
throw "The file '${BunBin}\$Target\bun.exe' does not exist. Download is corrupt or intercepted Antivirus?`n"
}
} catch {
Write-Output "Install Failed - could not unzip $ZipPath"
Write-Error $_
return 1
}

Move-Item "${BunBin}\$Target\bun.exe" "${BunBin}\bun.exe" -Force

Remove-Item "${BunBin}\$Target" -Recurse -Force
Remove-Item $ZipPath -Force

$BunRevision = "$(& "${BunBin}\bun.exe" --revision)"
if ($LASTEXITCODE -eq 1073741795) { # STATUS_ILLEGAL_INSTRUCTION
if ($IsBaseline) {
Write-Output "Install Failed - bun.exe (baseline) is not compatible with your CPU.`n"
Write-Output "Please open a GitHub issue with your CPU model:`nhttps://github.com/oven-sh/bun/issues/new/choose`n"
return 1
}

Write-Output "Install Failed - bun.exe is not compatible with your CPU. This should have been detected before downloading.`n"
Write-Output "Attempting to download bun.exe (baseline) instead.`n"

Install-Bun -Version $Version -ForceBaseline $True
return 1
}
# '-1073741515' was spotted in the wild, but not clearly documented as a status code:
# https://discord.com/channels/876711213126520882/1149339379446325248/1205194965383250081
# http://community.sqlbackupandftp.com/t/error-1073741515-solved/1305
if (($LASTEXITCODE -eq 3221225781) -or ($LASTEXITCODE -eq -1073741515)) # STATUS_DLL_NOT_FOUND
{
Write-Output "Install Failed - You are missing a DLL required to run bun.exe"
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"
Write-Output "The command '${BunBin}\bun.exe --revision' exited with code ${LASTEXITCODE}`n"
return 1
}
if ($LASTEXITCODE -ne 0) {
Write-Output "Install Failed - could not verify bun.exe"
Write-Output "The command '${BunBin}\bun.exe --revision' exited with code ${LASTEXITCODE}`n"
return 1
}

try {
$env:IS_BUN_AUTO_UPDATE = "1"
# TODO: When powershell completions are added, make this switch actually do something
if ($NoCompletions) {
$env:BUN_NO_INSTALL_COMPLETIONS = "1"
}
# This completions script in general will install some extra stuff, mainly the `bunx` link.
# It also installs completions.
$output = "$(& "${BunBin}\bun.exe" completions 2>&1)"
if ($LASTEXITCODE -ne 0) {
Write-Output $output
Write-Output "Install Failed - could not finalize installation"
Write-Output "The command '${BunBin}\bun.exe completions' exited with code ${LASTEXITCODE}`n"
return 1
}
} catch {
# it is possible on powershell 5 that an error happens, but it is probably fine?
}
$env:IS_BUN_AUTO_UPDATE = $null
$env:BUN_NO_INSTALL_COMPLETIONS = $null

$DisplayVersion = if ($BunRevision -like "*-canary.*") {
"${BunRevision}"
} else {
"$(& "${BunBin}\bun.exe" --version)"
}

$C_RESET = [char]27 + "[0m"
$C_GREEN = [char]27 + "[1;32m"

Write-Output "${C_GREEN}Bun ${DisplayVersion} was installed successfully!${C_RESET}"
Write-Output "The binary is located at ${BunBin}\bun.exe`n"

$hasExistingOther = $false;
try {
$existing = Get-Command bun -ErrorAction
if ($existing.Source -ne "${BunBin}\bun.exe") {
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"
$hasExistingOther = $true;
}
} catch {}

if (-not $NoRegisterInstallation) {
$rootKey = $null
try {
$RegistryKey = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\Bun"
$rootKey = New-Item -Path $RegistryKey -Force
New-ItemProperty -Path $RegistryKey -Name "DisplayName" -Value "Bun" -PropertyType String -Force | Out-Null
New-ItemProperty -Path $RegistryKey -Name "InstallLocation" -Value "${BunRoot}" -PropertyType String -Force | Out-Null
New-ItemProperty -Path $RegistryKey -Name "DisplayIcon" -Value $BunBin\bun.exe -PropertyType String -Force | Out-Null
New-ItemProperty -Path $RegistryKey -Name "UninstallString" -Value "powershell -c `"& `'$BunRoot\uninstall.ps1`' -PauseOnError`" -ExecutionPolicy Bypass" -PropertyType String -Force | Out-Null
} catch {
if ($rootKey -ne $null) {
Remove-Item -Path $RegistryKey -Force
}
}
}

if(!$hasExistingOther) {
# Only try adding to path if there isn't already a bun.exe in the path
$Path = (Get-Env -Key "Path") -split ';'
if ($Path -notcontains $BunBin) {
if (-not $NoPathUpdate) {
$Path += $BunBin
Write-Env -Key 'Path' -Value ($Path -join ';')
$env:PATH = $Path;
} else {
Write-Output "Skipping adding '${BunBin}' to the user's %PATH%`n"
}
}

Write-Output "To get started, restart your terminal/editor, then type `"bun`"`n"
}

$LASTEXITCODE = 0;
}

Install-Bun -Version $Version -ForceBaseline $ForceBaseline

0 comments on commit 93c4f50

Please sign in to comment.