From 2c6b9edaa84f7f34623f6401a0e5095ac7650d7e Mon Sep 17 00:00:00 2001 From: dave caruso Date: Fri, 23 Feb 2024 23:40:00 -0800 Subject: [PATCH 1/9] a --- src/bun.zig | 10 +- src/cli/create_command.zig | 2 +- src/cli/install.ps1 | 200 ++++++++++++++++++------ src/cli/install_completions_command.zig | 94 ++++++++++- src/cli/run_command.zig | 7 +- src/cli/shell_completions.zig | 3 + src/cli/uninstall.ps1 | 111 +++++++++++++ src/cli/upgrade_command.zig | 4 +- src/fd.zig | 11 +- src/string_immutable.zig | 34 ++-- src/windows.zig | 5 + 11 files changed, 384 insertions(+), 97 deletions(-) create mode 100644 src/cli/uninstall.ps1 diff --git a/src/bun.zig b/src/bun.zig index 015ba67584d358..9e90f57c8ab70d 100644 --- a/src/bun.zig +++ b/src/bun.zig @@ -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, diff --git a/src/cli/create_command.zig b/src/cli/create_command.zig index 6e6a5cabcc8c4b..bd209664ed9eb9 100644 --- a/src/cli/create_command.zig +++ b/src/cli/create_command.zig @@ -2307,7 +2307,7 @@ const GitHandler = struct { process.stderr_behavior = .Inherit; _ = try process.spawnAndWait(); - _ = process.kill() catch undefined; + _ = process.kill() catch {}; } Output.prettyError("\n", .{}); diff --git a/src/cli/install.ps1 b/src/cli/install.ps1 index 22a907a4f7fed8..647105940ae3e8 100644 --- a/src/cli/install.ps1 +++ b/src/cli/install.ps1 @@ -1,35 +1,95 @@ #!/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 +104,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 "C:\Users\dave\.bun\bin\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 +151,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 +173,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 +200,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 +215,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 +234,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 +248,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" } -} \ No newline at end of file +} + +Install-Bun -Version $Version -ForceBaseline $ForceBaseline diff --git a/src/cli/install_completions_command.zig b/src/cli/install_completions_command.zig index b0953d9b99fb9a..a1c0e37d189009 100644 --- a/src/cli/install_completions_command.zig +++ b/src/cli/install_completions_command.zig @@ -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,79 @@ 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]; + const file = try std.fs.createFileAbsoluteW(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\.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 :0], + comptime bun.strings.literal(u16, "uninstall.ps1"), + }); + + const file = try std.fs.createFileAbsoluteW(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 +190,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("error: 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 => {}, diff --git a/src/cli/run_command.zig b/src/cli/run_command.zig index bd7cf01124e6c3..3c0d8f666a53d6 100644 --- a/src/cli/run_command.zig +++ b/src/cli/run_command.zig @@ -612,15 +612,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 => { @@ -631,7 +630,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; } }, diff --git a/src/cli/shell_completions.zig b/src/cli/shell_completions.zig index 35f0ff00b94cf3..d3bf642645f46d 100644 --- a/src/cli/shell_completions.zig +++ b/src/cli/shell_completions.zig @@ -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; @@ -37,6 +38,8 @@ pub const Shell = enum { return Shell.zsh; } else if (strings.eqlComptime(basename, "fish")) { return Shell.fish; + } else if (strings.eqlComptime(basename, "pwsh") or strings.eqlComptime(basename, "powershell")) { + return Shell.fish; } else { return Shell.unknown; } diff --git a/src/cli/uninstall.ps1 b/src/cli/uninstall.ps1 new file mode 100644 index 00000000000000..3d8eafd1bdd384 --- /dev/null +++ b/src/cli/uninstall.ps1 @@ -0,0 +1,111 @@ +# 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]$PauseAtEnd = $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) + + $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) + } +} + +function Get-Env { + param([String] $Key) + + $RegisterKey = Get-Item -Path 'HKCU:' + $EnvRegisterKey = $RegisterKey.OpenSubKey('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`nDeletion will not continue to prevent accidental deletion of unrelated files." + if ($PauseAtEnd) { pause } + exit 1 +} + +function Stop-Bun { + try { + Get-Process -Name bun | Where-Object { $_.Path -eq "C:\Users\dave\.bun\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 ($PauseAtEnd) { 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 ($PauseAtEnd) { pause } + exit 1 + } +} + +# Remove ~\.bun +try { + Remove-Item "${PSScriptRoot}" -Recurse -Force +} catch { + Write-Host "Could not delete ${PSScriptRoot}." + if ($PauseAtEnd) { 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 ($PauseAtEnd) { pause } + exit 1 +} + +# Remove Entry from Windows Installer, if added. +try { + Remove-Item "HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\Bun" -Recurse +} catch { + # unlucky tbh +} diff --git a/src/cli/upgrade_command.zig b/src/cli/upgrade_command.zig index bfc0619b81a224..586797541d0a7c 100644 --- a/src/cli/upgrade_command.zig +++ b/src/cli/upgrade_command.zig @@ -69,7 +69,7 @@ pub const Version = struct { ), ), }, - ) catch unreachable; + ) catch bun.outOfMemory(); } return this.tag; } @@ -873,7 +873,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()); diff --git a/src/fd.zig b/src/fd.zig index 28cec3987c48ee..a1ade3c6ef1ba1 100644 --- a/src/fd.zig +++ b/src/fd.zig @@ -302,16 +302,7 @@ pub const FDImpl = packed struct { return JSValue.jsNumberFromInt32(value.makeLibUVOwned().uv()); } - pub fn format(this: FDImpl, comptime fmt: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { - if (fmt.len == 1 and fmt[0] == 'd') { - try writer.print("{d}", .{this.system()}); - return; - } - - if (fmt.len != 0) { - @compileError("invalid format string for FDImpl.format. must be either '' or 'd'"); - } - + pub fn format(this: FDImpl, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { if (!this.isValid()) { try writer.writeAll("[invalid_fd]"); return; diff --git a/src/string_immutable.zig b/src/string_immutable.zig index b58ad599901547..2c91662fbfcae8 100644 --- a/src/string_immutable.zig +++ b/src/string_immutable.zig @@ -1032,8 +1032,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; @@ -1041,28 +1041,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 { diff --git a/src/windows.zig b/src/windows.zig index 52bfd953bec529..f5d11a5738767c 100644 --- a/src/windows.zig +++ b/src/windows.zig @@ -3086,3 +3086,8 @@ 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]; +} From dd7c1a443c91ab68e31b65085e11411c8153e306 Mon Sep 17 00:00:00 2001 From: dave caruso Date: Sat, 24 Feb 2024 00:09:32 -0800 Subject: [PATCH 2/9] ojk --- src/cli/upgrade_command.zig | 63 ++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 32 deletions(-) diff --git a/src/cli/upgrade_command.zig b/src/cli/upgrade_command.zig index 586797541d0a7c..2b6717f058eb19 100644 --- a/src/cli/upgrade_command.zig +++ b/src/cli/upgrade_command.zig @@ -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,7 +268,7 @@ pub const UpgradeCommand = struct { initializeStore(); var expr = ParseJSON(&source, &log, allocator) catch |err| { if (!silent) { - progress.end(); + progress.?.end(); refresher.refresh(); if (log.errors > 0) { @@ -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: {s}", .{@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) { + if (!silent) { progress.end(); - refresher.refresh(); + 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: tag_name 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("Downgrading from Bun {s}-canary to Bun v{s}\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, From 9b67cbfda3919a298fe07ab915a78523d7459af2 Mon Sep 17 00:00:00 2001 From: dave caruso Date: Sat, 24 Feb 2024 00:16:51 -0800 Subject: [PATCH 3/9] wrong home dir --- src/cli/install.ps1 | 2 +- src/cli/uninstall.ps1 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cli/install.ps1 b/src/cli/install.ps1 index 647105940ae3e8..683449306ecbb7 100644 --- a/src/cli/install.ps1 +++ b/src/cli/install.ps1 @@ -119,7 +119,7 @@ function Install-Bun { } catch [System.Management.Automation.ItemNotFoundException] { # ignore } catch [System.UnauthorizedAccessException] { - $openProcesses = Get-Process -Name bun | Where-Object { $_.Path -eq "C:\Users\dave\.bun\bin\bun.exe" } + $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 diff --git a/src/cli/uninstall.ps1 b/src/cli/uninstall.ps1 index 3d8eafd1bdd384..bb736baf0d9a06 100644 --- a/src/cli/uninstall.ps1 +++ b/src/cli/uninstall.ps1 @@ -45,7 +45,7 @@ if (-not (Test-Path "${PSScriptRoot}\bin\bun.exe")) { function Stop-Bun { try { - Get-Process -Name bun | Where-Object { $_.Path -eq "C:\Users\dave\.bun\bin\bun.exe" } | Stop-Process -Force + Get-Process -Name bun | Where-Object { $_.Path -eq "${PSScriptRoot}\bin\bun.exe" } | Stop-Process -Force } catch [Microsoft.PowerShell.Commands.ProcessCommandException] { # ignore } catch { From 7a8a4c03b8d4316418c6157df046cd2cf4b0b498 Mon Sep 17 00:00:00 2001 From: dave caruso Date: Sat, 24 Feb 2024 16:16:45 -0800 Subject: [PATCH 4/9] a --- src/cli/install.ps1 | 15 +++++++------ src/cli/install_completions_command.zig | 2 +- src/cli/shell_completions.zig | 14 +++++++----- src/cli/uninstall.ps1 | 29 +++++++++++++------------ src/cli/upgrade_command.zig | 4 ++-- 5 files changed, 34 insertions(+), 30 deletions(-) diff --git a/src/cli/install.ps1 b/src/cli/install.ps1 index 683449306ecbb7..285a1b073bcfe9 100644 --- a/src/cli/install.ps1 +++ b/src/cli/install.ps1 @@ -20,9 +20,10 @@ if ($env:PROCESSOR_ARCHITECTURE -ne "AMD64") { # 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" + Write-Warning "Bun requires at ${MinBuildName} or newer.`n`nThe install will still continue but it may not work.`n" exit 1 } @@ -218,11 +219,11 @@ function Install-Bun { $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 - } + # 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.*") { @@ -254,7 +255,7 @@ function Install-Bun { $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 "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 { diff --git a/src/cli/install_completions_command.zig b/src/cli/install_completions_command.zig index a1c0e37d189009..78b4958dc7bcd9 100644 --- a/src/cli/install_completions_command.zig +++ b/src/cli/install_completions_command.zig @@ -152,7 +152,7 @@ pub const InstallCompletionsCommand = struct { 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 :0], + image_dirname[0 .. image_dirname.len - 3], comptime bun.strings.literal(u16, "uninstall.ps1"), }); diff --git a/src/cli/shell_completions.zig b/src/cli/shell_completions.zig index d3bf642645f46d..4df3eb70b831d6 100644 --- a/src/cli/shell_completions.zig +++ b/src/cli/shell_completions.zig @@ -33,15 +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; - } else if (strings.eqlComptime(basename, "pwsh") or strings.eqlComptime(basename, "powershell")) { - return Shell.fish; + return .fish; + } else if (strings.eqlComptime(basename, "pwsh") or + strings.eqlComptime(basename, "powershell")) + { + return .pwsh; } else { - return Shell.unknown; + return .unknown; } } }; diff --git a/src/cli/uninstall.ps1 b/src/cli/uninstall.ps1 index bb736baf0d9a06..df74be1872f7d2 100644 --- a/src/cli/uninstall.ps1 +++ b/src/cli/uninstall.ps1 @@ -2,7 +2,7 @@ # script, removing it from %PATH%, deleting caches, and removing it from # the list of installed programs. param( - [switch]$PauseAtEnd = $false + [switch]$PauseOnError = $false ) $ErrorActionPreference = "Stop" @@ -12,9 +12,7 @@ $ErrorActionPreference = "Stop" function Write-Env { param([String]$Key, [String]$Value) - $RegisterKey = Get-Item -Path 'HKCU:' - - $EnvRegisterKey = $RegisterKey.OpenSubKey('Environment', $true) + $EnvRegisterKey = Get-Item -Path 'HKCU:Environment' if ($null -eq $Value) { $EnvRegisterKey.DeleteValue($Key) } else { @@ -32,14 +30,13 @@ function Write-Env { function Get-Env { param([String] $Key) - $RegisterKey = Get-Item -Path 'HKCU:' - $EnvRegisterKey = $RegisterKey.OpenSubKey('Environment') + $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`nDeletion will not continue to prevent accidental deletion of unrelated files." - if ($PauseAtEnd) { pause } + 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 } @@ -50,7 +47,7 @@ function Stop-Bun { # ignore } catch { Write-Host "There are open instances of bun.exe that could not be automatically closed." - if ($PauseAtEnd) { pause } + if ($PauseOnError) { pause } exit 1 } } @@ -69,7 +66,7 @@ try { 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 ($PauseAtEnd) { pause } + if ($PauseOnError) { pause } exit 1 } } @@ -79,7 +76,7 @@ try { Remove-Item "${PSScriptRoot}" -Recurse -Force } catch { Write-Host "Could not delete ${PSScriptRoot}." - if ($PauseAtEnd) { pause } + if ($PauseOnError) { pause } exit 1 } @@ -99,13 +96,17 @@ try { Write-Env -Key 'Path' -Value ($Path -join ';') } catch { Write-Host "Could not remove ${PSScriptRoot}\bin from PATH." - if ($PauseAtEnd) { pause } + if ($PauseOnError) { pause } exit 1 } -# Remove Entry from Windows Installer, if added. +# Remove Entry from Windows Installer, if it is owned by this installation. try { - Remove-Item "HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\Bun" -Recurse + $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 } diff --git a/src/cli/upgrade_command.zig b/src/cli/upgrade_command.zig index 2b6717f058eb19..ba677b0b95cbd5 100644 --- a/src/cli/upgrade_command.zig +++ b/src/cli/upgrade_command.zig @@ -269,7 +269,7 @@ pub const UpgradeCommand = struct { var expr = ParseJSON(&source, &log, allocator) catch |err| { if (!silent) { progress.?.end(); - refresher.refresh(); + refresher.?.refresh(); if (log.errors > 0) { if (Output.enable_ansi_colors) { @@ -308,7 +308,7 @@ pub const UpgradeCommand = struct { if (expr.data != .e_object) { if (!silent) { - progress.end(); + progress.?.end(); refresher.?.refresh(); const json_type: js_ast.Expr.Tag = @as(js_ast.Expr.Tag, expr.data); From 183a8a7683f223d1e8a5486a8a342842cd5fb03e Mon Sep 17 00:00:00 2001 From: Dylan Conway <35280289+dylan-conway@users.noreply.github.com> Date: Wed, 21 Feb 2024 14:47:43 -0800 Subject: [PATCH 5/9] fix(install): semver prerelease bugfix (#9026) * make sure prereleases match correctly * add the file * few more tests * make sure pre is in query, not group --- src/install/semver.zig | 113 +++++++++++++++++++++++--------- test/cli/install/semver.test.ts | 10 +++ 2 files changed, 91 insertions(+), 32 deletions(-) diff --git a/src/install/semver.zig b/src/install/semver.zig index 7b47619f3bf5f0..25c3485f9ba948 100644 --- a/src/install/semver.zig +++ b/src/install/semver.zig @@ -1394,7 +1394,6 @@ pub const Range = struct { version: Version, comparator_buf: string, version_buf: string, - include_pre: bool, ) bool { const order = version.orderWithoutBuild(comparator.version, version_buf, comparator_buf); @@ -1404,11 +1403,11 @@ pub const Range = struct { else => false, }, .gt => switch (comparator.op) { - .gt, .gte => if (!include_pre) false else true, + .gt, .gte => true, else => false, }, .lt => switch (comparator.op) { - .lt, .lte => if (!include_pre) false else true, + .lt, .lte => true, else => false, }, }; @@ -1423,39 +1422,46 @@ pub const Range = struct { return true; } - // When the boundaries of a range do not include a pre-release tag on either side, - // we should not consider that '7.0.0-rc2' < "7.0.0" - // ``` - // > semver.satisfies("7.0.0-rc2", "<=7.0.0") - // false - // > semver.satisfies("7.0.0-rc2", ">=7.0.0") - // false - // > semver.satisfies("7.0.0-rc2", "<=7.0.0-rc2") - // true - // > semver.satisfies("7.0.0-rc2", ">=7.0.0-rc2") - // true - // ``` - // - // - https://github.com/npm/node-semver#prerelease-tags - // - https://github.com/npm/node-semver/blob/cce61804ba6f997225a1267135c06676fe0524d2/classes/range.js#L505-L539 - var include_pre = true; - if (version.tag.hasPre()) { - if (!has_right) { - if (!range.left.version.tag.hasPre()) { - include_pre = false; - } - } else { - if (!range.left.version.tag.hasPre() and !range.right.version.tag.hasPre()) { - include_pre = false; - } - } + if (!range.left.satisfies(version, range_buf, version_buf)) { + return false; + } + + if (has_right and !range.right.satisfies(version, range_buf, version_buf)) { + return false; + } + + return true; + } + + pub fn satisfiesPre(range: Range, version: Version, range_buf: string, version_buf: string, pre_matched: *bool) bool { + if (comptime Environment.allow_assert) { + std.debug.assert(version.tag.hasPre()); } + const has_left = range.hasLeft(); + const has_right = range.hasRight(); - if (!range.left.satisfies(version, range_buf, version_buf, include_pre)) { + if (!has_left) { + return true; + } + + // If left has prerelease check if major,minor,patch matches with left. If + // not, check the same with right if right exists and has prerelease. + pre_matched.* = pre_matched.* or + (range.left.version.tag.hasPre() and + version.patch == range.left.version.patch and + version.minor == range.left.version.minor and + version.major == range.left.version.major) or + (has_right and + range.right.version.tag.hasPre() and + version.patch == range.right.version.patch and + version.minor == range.right.version.minor and + version.major == range.right.version.major); + + if (!range.left.satisfies(version, range_buf, version_buf)) { return false; } - if (has_right and !range.right.satisfies(version, range_buf, version_buf, include_pre)) { + if (has_right and !range.right.satisfies(version, range_buf, version_buf)) { return false; } @@ -1502,6 +1508,29 @@ pub const Query = struct { ); } + pub fn satisfiesPre(list: *const List, version: Version, list_buf: string, version_buf: string) bool { + if (comptime Environment.allow_assert) { + std.debug.assert(version.tag.hasPre()); + } + + // `version` has a prerelease tag: + // - needs to satisfy each comparator in the query ( AND AND ...) like normal comparison + // - if it does, also needs to match major, minor, patch with at least one of the other versions + // with a prerelease + // https://github.com/npm/node-semver/blob/ac9b35769ab0ddfefd5a3af4a3ecaf3da2012352/classes/range.js#L505 + var pre_matched = false; + return (list.head.satisfiesPre( + version, + list_buf, + version_buf, + &pre_matched, + ) and pre_matched) or (list.next orelse return false).satisfiesPre( + version, + list_buf, + version_buf, + ); + } + pub fn eql(lhs: *const List, rhs: *const List) bool { if (!lhs.head.eql(&rhs.head)) return false; @@ -1647,7 +1676,10 @@ pub const Query = struct { group_buf: string, version_buf: string, ) bool { - return group.head.satisfies(version, group_buf, version_buf); + return if (version.tag.hasPre()) + group.head.satisfiesPre(version, group_buf, version_buf) + else + group.head.satisfies(version, group_buf, version_buf); } }; @@ -1672,6 +1704,23 @@ pub const Query = struct { ); } + pub fn satisfiesPre(query: *const Query, version: Version, query_buf: string, version_buf: string, pre_matched: *bool) bool { + if (comptime Environment.allow_assert) { + std.debug.assert(version.tag.hasPre()); + } + return query.range.satisfiesPre( + version, + query_buf, + version_buf, + pre_matched, + ) and (query.next orelse return true).satisfiesPre( + version, + query_buf, + version_buf, + pre_matched, + ); + } + const Token = struct { tag: Tag = Tag.none, wildcard: Wildcard = Wildcard.none, diff --git a/test/cli/install/semver.test.ts b/test/cli/install/semver.test.ts index 8c955a092f19e6..c9c5efc9ef7399 100644 --- a/test/cli/install/semver.test.ts +++ b/test/cli/install/semver.test.ts @@ -340,6 +340,16 @@ describe("Bun.semver.satisfies()", () => { testSatisfies("5.0 || 1.2 - 1.3", "5.0.2", true); testSatisfies("5.0 || 1.2 - 1.3 || >8", "9.0.2", true); + testSatisfies(">=0.34.0-next.3 <1.0.0", "0.34.0-next.8", true); + testSatisfies("<1.0.0", "0.34.0-next.8", false); + + testSatisfies("<=7.0.0", "7.0.0-rc2", false); + testSatisfies(">=7.0.0", "7.0.0-rc2", false); + testSatisfies("<=7.0.0-rc2", "7.0.0-rc2", true); + testSatisfies(">=7.0.0-rc2", "7.0.0-rc2", true); + + testSatisfies("^1.2.3-pr.1 || >=1.2.4-alpha", "1.2.4-alpha.notready", true); + const notPassing = [ "0.1.0", "0.10.0", From fa714b7af4a59f1d2f62af29fd9da75b506bd72d Mon Sep 17 00:00:00 2001 From: dave caruso Date: Sat, 24 Feb 2024 16:30:54 -0800 Subject: [PATCH 6/9] windows build idk --- src/output.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/output.zig b/src/output.zig index 78df0b8c9c0271..2c6160b80e3c47 100644 --- a/src/output.zig +++ b/src/output.zig @@ -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; From 2cd988d705e32c9936315569a12d125aa160632c Mon Sep 17 00:00:00 2001 From: dave caruso Date: Thu, 22 Feb 2024 20:28:05 -0800 Subject: [PATCH 7/9] this is silly --- src/bun.js/bindings/JSEnvironmentVariableMap.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/bun.js/bindings/JSEnvironmentVariableMap.cpp b/src/bun.js/bindings/JSEnvironmentVariableMap.cpp index d0514ae781935e..3d59d9c45947df 100644 --- a/src/bun.js/bindings/JSEnvironmentVariableMap.cpp +++ b/src/bun.js/bindings/JSEnvironmentVariableMap.cpp @@ -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; } } From 3389717439ce44ee7c3fcd0fb7dd115ad53aaa29 Mon Sep 17 00:00:00 2001 From: dave caruso Date: Fri, 1 Mar 2024 00:53:23 -0800 Subject: [PATCH 8/9] fix compile erorr on windows --- src/bun.js/WebKit | 2 +- src/cli/install_completions_command.zig | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/bun.js/WebKit b/src/bun.js/WebKit index c0c648e5a793ac..c3712c13dcdc09 160000 --- a/src/bun.js/WebKit +++ b/src/bun.js/WebKit @@ -1 +1 @@ -Subproject commit c0c648e5a793acb9a6ee48c898e98a9afc8bd9cd +Subproject commit c3712c13dcdc091cfe4c7cb8f2c1fd16472e6f92 diff --git a/src/cli/install_completions_command.zig b/src/cli/install_completions_command.zig index 78b4958dc7bcd9..362f922b46f2c9 100644 --- a/src/cli/install_completions_command.zig +++ b/src/cli/install_completions_command.zig @@ -122,7 +122,9 @@ pub const InstallCompletionsCommand = struct { comptime bun.strings.literal(u16, bunx_name ++ ".exe\x00"), }); const bunx_cmd = bunx_cmd_with_z[0 .. bunx_cmd_with_z.len - 1 :0]; - const file = try std.fs.createFileAbsoluteW(bunx_cmd, .{}); + // 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); } @@ -156,7 +158,7 @@ pub const InstallCompletionsCommand = struct { comptime bun.strings.literal(u16, "uninstall.ps1"), }); - const file = try std.fs.createFileAbsoluteW(uninstaller_path, .{}); + const file = try std.fs.cwd().createFileW(uninstaller_path, .{}); defer file.close(); try file.writeAll(content); From 9a9a41684ba79066291c979f7d50673e26b321d7 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Tue, 27 Feb 2024 07:54:55 +0000 Subject: [PATCH 9/9] [autofix.ci] apply automated fixes --- src/windows.zig | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/windows.zig b/src/windows.zig index 175e7b1fdaf67f..dcd57e14014333 100644 --- a/src/windows.zig +++ b/src/windows.zig @@ -3091,7 +3091,6 @@ 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]; @@ -3138,4 +3137,3 @@ pub const INPUT_RECORD = extern struct { FocusEvent: FOCUS_EVENT_RECORD, }, }; -