windows: changes to install/upgrade/uninstallation process (#9025)

This commit is contained in:
dave caruso
2024-02-27 03:11:43 -08:00
committed by GitHub
parent fd6fd78f0f
commit 4e2d00d052
13 changed files with 430 additions and 130 deletions

View File

@@ -166,11 +166,10 @@ JSValue createEnvironmentVariablesMap(Zig::GlobalObject* globalObject)
if (auto index = parseIndex(identifier)) {
ZigString valueString = { nullptr, 0 };
ZigString nameStr = toZigString(name);
JSValue value = jsUndefined();
if (Bun__getEnvValue(globalObject, &nameStr, &valueString)) {
value = jsString(vm, Zig::toStringCopy(valueString));
JSValue value = jsString(vm, Zig::toStringCopy(valueString));
object->putDirectIndex(globalObject, *index, value, 0, PutDirectIndexLikePutDirect);
}
object->putDirectIndex(globalObject, *index, value, 0, PutDirectIndexLikePutDirect);
continue;
}
}

View File

@@ -2005,12 +2005,12 @@ pub const win32 = struct {
) !void {
const flags: std.os.windows.DWORD = w.CREATE_UNICODE_ENVIRONMENT;
const image_path = &w.peb().ProcessParameters.ImagePathName;
const image_path = windows.exePathW();
var wbuf: WPathBuffer = undefined;
@memcpy(wbuf[0..image_path.Length], image_path.Buffer);
wbuf[image_path.Length] = 0;
@memcpy(wbuf[0..image_path.len], image_path);
wbuf[image_path.len] = 0;
const image_pathZ = wbuf[0..image_path.Length :0];
const image_pathZ = wbuf[0..image_path.len :0];
const kernelenv = w.kernel32.GetEnvironmentStringsW();
defer {
@@ -2062,7 +2062,7 @@ pub const win32 = struct {
.hStdError = std.io.getStdErr().handle,
};
const rc = w.kernel32.CreateProcessW(
image_pathZ,
image_pathZ.ptr,
w.kernel32.GetCommandLineW(),
null,
null,

View File

@@ -2307,7 +2307,7 @@ const GitHandler = struct {
process.stderr_behavior = .Inherit;
_ = try process.spawnAndWait();
_ = process.kill() catch undefined;
_ = process.kill() catch {};
}
Output.prettyError("\n", .{});

View File

@@ -1,35 +1,96 @@
#!/usr/bin/env pwsh
param(
# TODO: change this to 'latest' when Bun for Windows is stable.
[string]$Version = "canary"
[String]$Version = "canary",
# 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
);
# filter out 32 bit + ARM
if ($env:PROCESSOR_ARCHITECTURE -ne "AMD64") {
Write-Output "Install Failed:"
Write-Output "Bun for Windows is only available for x86 64-bit Windows.`n"
exit 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"
exit 1
}
$ErrorActionPreference = "Stop"
# This is a functions so that in the unlikely case the baseline check fails but is is needed, we can do a recursive call.
# 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
[string]$Version,
[bool]$ForceBaseline = $False
);
# filter out 32 bit and arm
if ($env:PROCESSOR_ARCHITECTURE -ne "AMD64") {
Write-Output "Install Failed:"
Write-Output "Bun for Windows is only available for x86 64-bit Windows.`n"
exit 1
}
# .win10_rs5
$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"
exit 1
}
# 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"
@@ -44,16 +105,35 @@ function Install-Bun {
$Arch = "x64"
$IsBaseline = $ForceBaseline
$EnabledXStateFeatures = ( `
Add-Type -MemberDefinition '[DllImport("kernel32.dll")]public static extern long GetEnabledXStateFeatures();' `
-Name 'Kernel32' -Namespace 'Win32' -PassThru `
)::GetEnabledXStateFeatures();
$IsBaseline = ($EnabledXStateFeatures -band 4) -neq 4;
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."
exit 1
}
Write-Output "Install Failed - An unknown error occurred while trying to remove the existing installation"
Write-Output $_
exit 1
} catch {
Write-Output "Install Failed - An unknown error occurred while trying to remove the existing installation"
Write-Output $_
exit 1
}
$Target = "bun-windows-$Arch"
if ($IsBaseline) {
$Target = "bun-windows-$Arch-baseline"
@@ -72,12 +152,16 @@ function Install-Bun {
$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
curl.exe "-#SfLo" "$ZipPath" "$URL"
if ($LASTEXITCODE -ne 0) {
Write-Output "Install Failed - could not download $URL"
Write-Output "The command 'curl.exe $URL -o $ZipPath' exited with code ${LASTEXITCODE}`n"
exit 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"
@@ -90,14 +174,14 @@ function Install-Bun {
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 / Antivirus intercepted?`n"
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 $_
exit 1
}
Remove-Item "${BunBin}\bun.exe" -ErrorAction SilentlyContinue
Move-Item "${BunBin}\$Target\bun.exe" "${BunBin}\bun.exe" -Force
Remove-Item "${BunBin}\$Target" -Recurse -Force
@@ -117,10 +201,10 @@ function Install-Bun {
Install-Bun -Version $Version -ForceBaseline $True
exit 1
}
if (($LASTEXITCODE -eq 3221225781) # STATUS_DLL_NOT_FOUND
# '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
|| ($LASTEXITCODE -eq 1073741515))
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"
@@ -132,6 +216,16 @@ function Install-Bun {
Write-Output "The command '${BunBin}\bun.exe --revision' exited with code ${LASTEXITCODE}`n"
exit 1
}
$env:IS_BUN_AUTO_UPDATE = "1"
$null = "$(& "${BunBin}\bun.exe" completions)"
# if ($LASTEXITCODE -ne 0) {
# Write-Output "Install Failed - could not finalize installation"
# Write-Output "The command '${BunBin}\bun.exe completions' exited with code ${LASTEXITCODE}`n"
# exit 1
# }
$env:IS_BUN_AUTO_UPDATE = $null
$DisplayVersion = if ($BunRevision -like "*-canary.*") {
"${BunRevision}"
} else {
@@ -141,18 +235,6 @@ function Install-Bun {
$C_RESET = [char]27 + "[0m"
$C_GREEN = [char]27 + "[1;32m"
# delete bunx if it exists already. this happens if you re-install
# we don't want to hit an "already exists" error.
Remove-Item "${BunBin}\bunx.exe" -ErrorAction SilentlyContinue
Remove-Item "${BunBin}\bunx.cmd" -ErrorAction SilentlyContinue
try {
$null = New-Item -ItemType HardLink -Path "${BunBin}\bunx.exe" -Target "${BunBin}\bun.exe" -Force
} catch {
Write-Warning "Could not create a hard link for bunx, falling back to a cmd script`n"
Set-Content -Path "${BunBin}\bunx.cmd" -Value "@%~dp0bun.exe x %*"
}
Write-Output "${C_GREEN}Bun ${DisplayVersion} was installed successfully!${C_RESET}"
Write-Output "The binary is located at ${BunBin}\bun.exe`n"
@@ -167,17 +249,36 @@ function Install-Bun {
}
} catch {}
$User = [System.EnvironmentVariableTarget]::User
$Path = [System.Environment]::GetEnvironmentVariable('Path', $User) -split ';'
if ($Path -notcontains $BunBin) {
$Path += $BunBin
[System.Environment]::SetEnvironmentVariable('Path', $Path -join ';', $User)
}
if ($env:PATH -notcontains ";${BunBin}") {
$env:PATH = "${env:Path};${BunBin}"
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`"" -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 ';')
} 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"
}
}
}
Install-Bun -Version $Version -ForceBaseline $ForceBaseline

View File

@@ -44,13 +44,10 @@ const ShellCompletions = @import("./shell_completions.zig");
pub const InstallCompletionsCommand = struct {
pub fn testPath(_: string) !std.fs.Dir {}
fn installBunxSymlink(allocator: std.mem.Allocator, cwd: []const u8) !void {
if (comptime Environment.isWindows) {
@panic("TODO on Windows");
}
const bunx_name = if (Environment.isDebug) "bunx-debug" else "bunx";
fn installBunxSymlinkPosix(allocator: std.mem.Allocator, cwd: []const u8) !void {
var buf: [bun.MAX_PATH_BYTES]u8 = undefined;
const bunx_name = if (Environment.isDebug) "bunx-debug" else "bunx";
// don't install it if it's already there
if (bun.which(&buf, bun.getenvZ("PATH") orelse cwd, cwd, bunx_name) != null)
@@ -92,6 +89,81 @@ pub const InstallCompletionsCommand = struct {
};
}
fn installBunxSymlinkWindows(_: std.mem.Allocator, _: []const u8) !void {
// Because symlinks are not always allowed on windows,
// `bunx.exe` on windows is a hardlink to `bun.exe`
// for this to work, we need to delete and recreate the hardlink every time
const image_path = bun.windows.exePathW();
const image_dirname = image_path[0 .. (std.mem.lastIndexOfScalar(u16, image_path, '\\') orelse unreachable) + 1];
var bunx_path_buf: bun.WPathBuffer = undefined;
std.os.windows.DeleteFile(try bun.strings.concatBufT(u16, &bunx_path_buf, .{
&bun.windows.nt_object_prefix,
image_dirname,
comptime bun.strings.literal(u16, bunx_name ++ ".cmd"),
}), .{ .dir = null }) catch {};
const bunx_path_with_z = try bun.strings.concatBufT(u16, &bunx_path_buf, .{
&bun.windows.nt_object_prefix,
image_dirname,
comptime bun.strings.literal(u16, bunx_name ++ ".exe\x00"),
});
const bunx_path = bunx_path_with_z[0 .. bunx_path_with_z.len - 1 :0];
std.os.windows.DeleteFile(bunx_path, .{ .dir = null }) catch {};
if (bun.windows.CreateHardLinkW(bunx_path, image_path, null) == 0) {
// if hard link fails, use a cmd script
const script = "@%~dp0bun.exe x %*\n";
const bunx_cmd_with_z = try bun.strings.concatBufT(u16, &bunx_path_buf, .{
&bun.windows.nt_object_prefix,
image_dirname,
comptime bun.strings.literal(u16, bunx_name ++ ".exe\x00"),
});
const bunx_cmd = bunx_cmd_with_z[0 .. bunx_cmd_with_z.len - 1 :0];
// TODO: fix this zig bug, it is one line change to a few functions.
// const file = try std.fs.createFileAbsoluteW(bunx_cmd, .{});
const file = try std.fs.cwd().createFileW(bunx_cmd, .{});
defer file.close();
try file.writeAll(script);
}
}
fn installBunxSymlink(allocator: std.mem.Allocator, cwd: []const u8) !void {
if (Environment.isWindows) {
try installBunxSymlinkWindows(allocator, cwd);
} else {
try installBunxSymlinkPosix(allocator, cwd);
}
}
fn installUninstallerWindows() !void {
// This uninstaller file is only written if the current exe is within a path
// like `\bun\bin\<whatever>.exe` so that it probably only runs when the
// powershell `install.ps1` was used to install.
const image_path = bun.windows.exePathW();
const image_dirname = image_path[0..(std.mem.lastIndexOfScalar(u16, image_path, '\\') orelse unreachable)];
if (!std.mem.endsWith(u16, image_dirname, comptime bun.strings.literal(u16, "\\bun\\bin")))
return;
const content = @embedFile("uninstall.ps1");
var bunx_path_buf: bun.WPathBuffer = undefined;
const uninstaller_path = try bun.strings.concatBufT(u16, &bunx_path_buf, .{
&bun.windows.nt_object_prefix,
image_dirname[0 .. image_dirname.len - 3],
comptime bun.strings.literal(u16, "uninstall.ps1"),
});
const file = try std.fs.cwd().createFileW(uninstaller_path, .{});
defer file.close();
try file.writeAll(content);
}
pub fn exec(allocator: std.mem.Allocator) !void {
// Fail silently on auto-update.
const fail_exit_code: u8 = if (bun.getenvZ("IS_BUN_AUTO_UPDATE") == null) 1 else 0;
@@ -120,9 +192,21 @@ pub const InstallCompletionsCommand = struct {
installBunxSymlink(allocator, cwd) catch {};
if (Environment.isWindows) {
installUninstallerWindows() catch {};
}
// TODO: https://github.com/oven-sh/bun/issues/8939
if (Environment.isWindows) {
Output.errGeneric("PowerShell completions are not yet written for Bun yet.", .{});
Output.printErrorln("See https://github.com/oven-sh/bun/issues/8939", .{});
return;
}
switch (shell) {
.unknown => {
Output.prettyErrorln("<r><red>error:<r> Unknown or unsupported shell. Please set $SHELL to one of zsh, fish, or bash. To manually output completions, run this:\n bun getcompletes", .{});
Output.errGeneric("Unknown or unsupported shell. Please set $SHELL to one of zsh, fish, or bash.", .{});
Output.note("To manually output completions, run 'bun getcompletes'", .{});
Global.exit(fail_exit_code);
},
else => {},

View File

@@ -610,15 +610,14 @@ pub const RunCommand = struct {
const file_slice = target_path_buffer[0 .. prefix.len + len + file_name.len - "\x00".len];
const dir_slice = target_path_buffer[0 .. prefix.len + len + dir_name.len];
const ImagePathName = std.os.windows.peb().ProcessParameters.ImagePathName;
std.debug.assert(ImagePathName.Buffer[ImagePathName.Length / 2] == 0); // trust windows
const image_path = bun.windows.exePathW();
if (Environment.isDebug) {
// the link becomes out of date on rebuild
std.os.unlinkW(file_slice) catch {};
}
if (bun.windows.CreateHardLinkW(@ptrCast(file_slice.ptr), @ptrCast(ImagePathName.Buffer), null) == 0) {
if (bun.windows.CreateHardLinkW(@ptrCast(file_slice.ptr), image_path.ptr, null) == 0) {
switch (std.os.windows.kernel32.GetLastError()) {
.ALREADY_EXISTS => {},
else => {
@@ -629,7 +628,7 @@ pub const RunCommand = struct {
target_path_buffer[dir_slice.len] = '\\';
}
if (bun.windows.CreateHardLinkW(@ptrCast(file_slice.ptr), @ptrCast(ImagePathName.Buffer), null) == 0) {
if (bun.windows.CreateHardLinkW(@ptrCast(file_slice.ptr), image_path.ptr, null) == 0) {
return;
}
},

View File

@@ -15,6 +15,7 @@ pub const Shell = enum {
bash,
zsh,
fish,
pwsh,
const bash_completions = @import("root").completions.bash;
const zsh_completions = @import("root").completions.zsh;
@@ -32,13 +33,17 @@ pub const Shell = enum {
pub fn fromEnv(comptime Type: type, SHELL: Type) Shell {
const basename = std.fs.path.basename(SHELL);
if (strings.eqlComptime(basename, "bash")) {
return Shell.bash;
return .bash;
} else if (strings.eqlComptime(basename, "zsh")) {
return Shell.zsh;
return .zsh;
} else if (strings.eqlComptime(basename, "fish")) {
return Shell.fish;
return .fish;
} else if (strings.eqlComptime(basename, "pwsh") or
strings.eqlComptime(basename, "powershell"))
{
return .pwsh;
} else {
return Shell.unknown;
return .unknown;
}
}
};

112
src/cli/uninstall.ps1 Normal file
View File

@@ -0,0 +1,112 @@
# This script will remove the Bun installation at the location of this
# script, removing it from %PATH%, deleting caches, and removing it from
# the list of installed programs.
param(
[switch]$PauseOnError = $false
)
$ErrorActionPreference = "Stop"
# These two 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 Write-Env {
param([String]$Key, [String]$Value)
$EnvRegisterKey = Get-Item -Path 'HKCU:Environment'
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)
}
}
function Get-Env {
param([String] $Key)
$RegisterKey = Get-Item -Path 'HKCU:Environment'
$EnvRegisterKey.GetValue($Key, $null, [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames)
}
if (-not (Test-Path "${PSScriptRoot}\bin\bun.exe")) {
Write-Host "bun.exe not found in ${PSScriptRoot}\bin`n`nRefusing to delete this directory as it may.`n`nIf this uninstallation is still intentional, please just manually delete this folder."
if ($PauseOnError) { pause }
exit 1
}
function Stop-Bun {
try {
Get-Process -Name bun | Where-Object { $_.Path -eq "${PSScriptRoot}\bin\bun.exe" } | Stop-Process -Force
} catch [Microsoft.PowerShell.Commands.ProcessCommandException] {
# ignore
} catch {
Write-Host "There are open instances of bun.exe that could not be automatically closed."
if ($PauseOnError) { pause }
exit 1
}
}
# Remove ~\.bun\bin\bun.exe
try {
Stop-Bun
Remove-Item "${PSScriptRoot}\bin\bun.exe" -Force
} catch {
# Try a second time
Stop-Bun
Start-Sleep -Seconds 1
try {
Remove-Item "${PSScriptRoot}\bin\bun.exe" -Force
} catch {
Write-Host $_
Write-Host "`n`nCould not delete ${PSScriptRoot}\bin\bun.exe."
Write-Host "Please close all instances of bun.exe and try again."
if ($PauseOnError) { pause }
exit 1
}
}
# Remove ~\.bun
try {
Remove-Item "${PSScriptRoot}" -Recurse -Force
} catch {
Write-Host "Could not delete ${PSScriptRoot}."
if ($PauseOnError) { pause }
exit 1
}
# Delete some tempdir files. Do not fail if an error happens here
try {
Remove-Item "${Temp}\bun-*" -Recurse -Force
} catch {}
try {
Remove-Item "${Temp}\bunx-*" -Recurse -Force
} catch {}
# Remove Entry from path
try {
$Path = Get-Env -Key 'Path'
$Path = $Path -split ';'
$Path = $Path | Where-Object { $_ -ne "${PSScriptRoot}\bin" }
Write-Env -Key 'Path' -Value ($Path -join ';')
} catch {
Write-Host "Could not remove ${PSScriptRoot}\bin from PATH."
if ($PauseOnError) { pause }
exit 1
}
# Remove Entry from Windows Installer, if it is owned by this installation.
try {
$item = Get-Item "HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\Bun";
$location = $item.GetValue("InstallLocation");
if ($location -eq "${PSScriptRoot}") {
Remove-Item "HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\Bun" -Recurse
}
} catch {
# unlucky tbh
}

View File

@@ -69,7 +69,7 @@ pub const Version = struct {
),
),
},
) catch unreachable;
) catch bun.outOfMemory();
}
return this.tag;
}
@@ -117,13 +117,12 @@ pub const Version = struct {
};
pub const UpgradeCheckerThread = struct {
var update_checker_thread: std.Thread = undefined;
pub fn spawn(env_loader: *DotEnv.Loader) void {
if (env_loader.map.get("BUN_DISABLE_UPGRADE_CHECK") != null or
env_loader.map.get("CI") != null or
strings.eqlComptime(env_loader.get("BUN_CANARY") orelse "0", "1"))
return;
update_checker_thread = std.Thread.spawn(.{}, run, .{env_loader}) catch return;
var update_checker_thread = std.Thread.spawn(.{}, run, .{env_loader}) catch return;
update_checker_thread.detach();
}
@@ -133,13 +132,14 @@ pub const UpgradeCheckerThread = struct {
std.time.sleep(std.time.ns_per_ms * delay);
Output.Source.configureThread();
HTTP.HTTPThread.init() catch unreachable;
try HTTP.HTTPThread.init();
defer {
js_ast.Expr.Data.Store.deinit();
js_ast.Stmt.Data.Store.deinit();
}
var version = (try UpgradeCommand.getLatestVersion(default_allocator, env_loader, undefined, undefined, false, true)) orelse return;
var version = (try UpgradeCommand.getLatestVersion(default_allocator, env_loader, null, null, false, true)) orelse return;
if (!version.isCurrent()) {
if (version.name()) |name| {
@@ -171,8 +171,8 @@ pub const UpgradeCommand = struct {
pub fn getLatestVersion(
allocator: std.mem.Allocator,
env_loader: *DotEnv.Loader,
refresher: *std.Progress,
progress: *std.Progress.Node,
refresher: ?*std.Progress,
progress: ?*std.Progress.Node,
use_profile: bool,
comptime silent: bool,
) !?Version {
@@ -234,7 +234,7 @@ pub const UpgradeCommand = struct {
var metadata_body = try MutableString.init(allocator, 2048);
// ensure very stable memory address
var async_http: *HTTP.AsyncHTTP = allocator.create(HTTP.AsyncHTTP) catch unreachable;
var async_http: *HTTP.AsyncHTTP = try allocator.create(HTTP.AsyncHTTP);
async_http.* = HTTP.AsyncHTTP.initSync(
allocator,
.GET,
@@ -250,7 +250,7 @@ pub const UpgradeCommand = struct {
);
async_http.client.reject_unauthorized = env_loader.getTLSRejectUnauthorized();
if (!silent) async_http.client.progress_node = progress;
if (!silent) async_http.client.progress_node = progress.?;
const response = try async_http.sendSync(true);
switch (response.status_code) {
@@ -268,8 +268,8 @@ pub const UpgradeCommand = struct {
initializeStore();
var expr = ParseJSON(&source, &log, allocator) catch |err| {
if (!silent) {
progress.end();
refresher.refresh();
progress.?.end();
refresher.?.refresh();
if (log.errors > 0) {
if (Output.enable_ansi_colors) {
@@ -277,6 +277,7 @@ pub const UpgradeCommand = struct {
} else {
try log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), false);
}
Global.exit(1);
} else {
Output.prettyErrorln("Error parsing releases from GitHub: <r><red>{s}<r>", .{@errorName(err)});
@@ -288,9 +289,9 @@ pub const UpgradeCommand = struct {
};
if (log.errors > 0) {
if (comptime !silent) {
progress.end();
refresher.refresh();
if (!silent) {
progress.?.end();
refresher.?.refresh();
if (Output.enable_ansi_colors) {
try log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), true);
@@ -306,9 +307,9 @@ pub const UpgradeCommand = struct {
var version = Version{ .zip_url = "", .tag = "", .buf = metadata_body, .size = 0 };
if (expr.data != .e_object) {
if (comptime !silent) {
progress.end();
refresher.refresh();
if (!silent) {
progress.?.end();
refresher.?.refresh();
const json_type: js_ast.Expr.Tag = @as(js_ast.Expr.Tag, expr.data);
Output.prettyErrorln("JSON error - expected an object but received {s}", .{@tagName(json_type)});
@@ -326,8 +327,8 @@ pub const UpgradeCommand = struct {
if (version.tag.len == 0) {
if (comptime !silent) {
progress.end();
refresher.refresh();
progress.?.end();
refresher.?.refresh();
Output.prettyErrorln("JSON Error parsing releases from GitHub: <r><red>tag_name<r> is missing?\n{s}", .{metadata_body.list.items});
Global.exit(1);
@@ -380,8 +381,8 @@ pub const UpgradeCommand = struct {
}
if (comptime !silent) {
progress.end();
refresher.refresh();
progress.?.end();
refresher.?.refresh();
if (version.name()) |name| {
Output.prettyErrorln("Bun v{s} is out, but not for this platform ({s}) yet.", .{
name, Version.triplet,
@@ -433,8 +434,6 @@ pub const UpgradeCommand = struct {
};
env_loader.loadProcess();
var version: Version = undefined;
const use_canary = brk: {
const default_use_canary = Environment.is_canary;
@@ -447,11 +446,11 @@ pub const UpgradeCommand = struct {
const use_profile = strings.containsAny(bun.argv(), "--profile");
if (!use_canary) {
const version: Version = if (!use_canary) v: {
var refresher = std.Progress{};
var progress = refresher.start("Fetching version tags", 0);
version = (try getLatestVersion(ctx.allocator, &env_loader, &refresher, progress, use_profile, false)) orelse return;
const version = (try getLatestVersion(ctx.allocator, &env_loader, &refresher, progress, use_profile, false)) orelse return;
progress.end();
refresher.refresh();
@@ -482,14 +481,14 @@ pub const UpgradeCommand = struct {
Output.prettyErrorln("<r><b>Downgrading from Bun <blue>{s}-canary<r> to Bun <cyan>v{s}<r><r>\n", .{ Global.package_json_version, version.name().? });
}
Output.flush();
} else {
version = Version{
.tag = "canary",
.zip_url = "https://github.com/oven-sh/bun/releases/download/canary/" ++ Version.zip_filename,
.size = 0,
.buf = MutableString.initEmpty(bun.default_allocator),
};
}
break :v version;
} else Version{
.tag = "canary",
.zip_url = "https://github.com/oven-sh/bun/releases/download/canary/" ++ Version.zip_filename,
.size = 0,
.buf = MutableString.initEmpty(bun.default_allocator),
};
const zip_url = URL.parse(version.zip_url);
const http_proxy: ?URL = env_loader.getHttpProxy(zip_url);
@@ -498,7 +497,7 @@ pub const UpgradeCommand = struct {
var refresher = std.Progress{};
var progress = refresher.start("Downloading", version.size);
refresher.refresh();
var async_http = ctx.allocator.create(HTTP.AsyncHTTP) catch unreachable;
var async_http = try ctx.allocator.create(HTTP.AsyncHTTP);
var zip_file_buffer = try ctx.allocator.create(MutableString);
zip_file_buffer.* = try MutableString.init(ctx.allocator, @max(version.size, 1024));
@@ -865,7 +864,7 @@ pub const UpgradeCommand = struct {
"completions",
};
env_loader.map.put("IS_BUN_AUTO_UPDATE", "true") catch unreachable;
env_loader.map.put("IS_BUN_AUTO_UPDATE", "true") catch bun.outOfMemory();
var buf_map = try env_loader.map.cloneToEnvMap(ctx.allocator);
_ = std.ChildProcess.run(.{
.allocator = ctx.allocator,
@@ -873,7 +872,7 @@ pub const UpgradeCommand = struct {
.cwd = target_dirname,
.max_output_bytes = 4096,
.env_map = &buf_map,
}) catch undefined;
}) catch {};
}
Output.printStartEnd(ctx.start_time, std.time.nanoTimestamp());

View File

@@ -829,7 +829,7 @@ fn scopedWriter() std.fs.File.Writer {
std.fs.cwd().fd,
path,
std.os.O.TRUNC | std.os.O.CREAT | std.os.O.WRONLY,
0o644,
if (Environment.isWindows) 0 else 0o644,
) catch |err_| {
// Ensure we don't panic inside panic
Scoped.loaded_env = false;

View File

@@ -1033,8 +1033,8 @@ pub inline fn append(allocator: std.mem.Allocator, self: string, other: string)
return buf;
}
pub inline fn joinAlloc(allocator: std.mem.Allocator, strs: anytype) ![]u8 {
const buf = try allocator.alloc(u8, len: {
pub inline fn concatAllocT(comptime T: type, allocator: std.mem.Allocator, strs: anytype) ![]T {
const buf = try allocator.alloc(T, len: {
var len: usize = 0;
inline for (strs) |s| {
len += s.len;
@@ -1042,28 +1042,24 @@ pub inline fn joinAlloc(allocator: std.mem.Allocator, strs: anytype) ![]u8 {
break :len len;
});
var remain = buf;
inline for (strs) |s| {
if (s.len > 0) {
@memcpy(remain, s);
remain = remain[s.len..];
}
}
return buf;
return concatBufT(T, buf, strs) catch |e| switch (e) {
error.NoSpaceLeft => unreachable, // exact size calculated
};
}
pub inline fn joinBuf(out: []u8, parts: anytype, comptime parts_len: usize) []u8 {
pub inline fn concatBufT(comptime T: type, out: []T, strs: anytype) ![]T {
var remain = out;
var count: usize = 0;
inline for (0..parts_len) |i| {
const part = parts[i];
bun.copy(u8, remain, part);
remain = remain[part.len..];
count += part.len;
var n: usize = 0;
inline for (strs) |s| {
if (s.len > remain.len) {
return error.NoSpaceLeft;
}
@memcpy(remain.ptr, s);
remain = remain[s.len..];
n += s.len;
}
return out[0..count];
return out[0..n];
}
pub fn index(self: string, str: string) i32 {

View File

@@ -3091,6 +3091,11 @@ pub extern "kernel32" fn OpenProcess(
// https://learn.microsoft.com/en-us/windows/win32/procthread/process-security-and-access-rights
pub const PROCESS_QUERY_LIMITED_INFORMATION: DWORD = 0x1000;
pub fn exePathW() [:0]const u16 {
const image_path_unicode_string = &std.os.windows.peb().ProcessParameters.ImagePathName;
return image_path_unicode_string.Buffer[0 .. image_path_unicode_string.Length / 2 :0];
}
pub const KEY_EVENT_RECORD = extern struct {
bKeyDown: BOOL,
wRepeatCount: WORD,