Compare commits

...

5 Commits

Author SHA1 Message Date
Jarred Sumner
1a88df0cda Add crash reporting for fs_watch and fs_watchfile 2026-02-18 21:01:55 -08:00
Jarred Sumner
dc4dd3aa5b fix(fs): create owned copies of watch event paths on Windows (#27099)
`onPathUpdateWindows` stored the `Event` directly from
`PathWatcherManager.onFileUpdate` without creating an owned copy. The
event's path was a `[]const u8` sub-slice of the watchlist's storage,
auto-coerced to `StringOrBytesToDecode{.bytes_to_free}`. When
`FSWatchTaskWindows.run()` later accessed `path.string` (for utf8
encoding), it reinterpreted the raw pointer bytes as a `bun.String`,
whose `tag` field contained the LSB of a heap pointer — almost always
an invalid enum value, causing `panic: switch on corrupt value` in
`String.deref()`.

The original implementation in #9972 properly created owned copies per
encoding (`bun.String.createUTF8` for utf8, `allocator.dupeZ` for
others), but this was lost during the watcher refactor in #10492.

Restore correct ownership by creating a `bun.String` for utf8 encoding
or a duped `[]const u8` for other encodings before enqueuing the task.

Closes #27099, closes #27108.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 20:55:57 -08:00
Jarred Sumner
a350d496cb Revert "fix(bundler): place standalone HTML scripts in <head> to preserve execution order (#27114)"
This reverts commit 9e32360195.
2026-02-18 19:08:03 -08:00
robobun
9e32360195 fix(bundler): place standalone HTML scripts in <head> to preserve execution order (#27114)
## Summary

- Fix standalone HTML mode (`--compile --target=browser`) placing
bundled JS as `<script type="module">` before `</body>`, which broke
execution order with existing inline body scripts
- Move bundled JS into `<head>` as a classic `<script>` (not
`type="module"`) so it executes synchronously before inline body
scripts, preserving the original script execution order
- Remove the now-unnecessary `addBodyTags()` function and associated
body script injection paths

Fixes #27113

## Test plan

- [x] Added regression test `test/regression/issue/27113.test.ts` that
verifies head scripts appear before `</head>` and don't use
`type="module"`
- [x] Updated existing standalone HTML tests in
`test/bundler/standalone.test.ts` to reflect the change from `<script
type="module">` to classic `<script>`
- [x] All 18 standalone tests pass (`bun bd test
test/bundler/standalone.test.ts`)
- [x] Regression test passes (`bun bd test
test/regression/issue/27113.test.ts`)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-18 19:07:10 -08:00
Dylan Conway
9785af304c Windows arm64 CI (#26746)
### What does this PR do?
Sets up ci for windows arm64
### How did you verify your code works?

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-02-18 18:08:10 -08:00
56 changed files with 2100 additions and 586 deletions

View File

@@ -99,6 +99,23 @@ function getTargetLabel(target) {
* @property {string[]} [features]
*/
// Azure VM sizes for Windows CI runners.
// DDSv6 = x64, DPSv6 = ARM64 (Cobalt 100). Quota: 100 cores per family in eastus2.
const azureVmSizes = {
"windows-x64": {
build: "Standard_D16ds_v6", // 16 vCPU, 64 GiB — C++ build, link
test: "Standard_D4ds_v6", // 4 vCPU, 16 GiB — test shards
},
"windows-aarch64": {
build: "Standard_D16ps_v6", // 16 vCPU, 64 GiB — C++ build, link
test: "Standard_D4ps_v6", // 4 vCPU, 16 GiB — test shards
},
};
function getAzureVmSize(os, arch, tier = "build") {
return azureVmSizes[`${os}-${arch}`]?.[tier];
}
/**
* @type {Platform[]}
*/
@@ -114,8 +131,7 @@ const buildPlatforms = [
{ os: "linux", arch: "x64", abi: "musl", baseline: true, distro: "alpine", release: "3.23" },
{ os: "windows", arch: "x64", release: "2019" },
{ os: "windows", arch: "x64", baseline: true, release: "2019" },
// TODO: Re-enable when Windows ARM64 VS component installation is resolved on Buildkite runners
// { os: "windows", arch: "aarch64", release: "2019" },
{ os: "windows", arch: "aarch64", release: "11" },
];
/**
@@ -138,8 +154,7 @@ const testPlatforms = [
{ os: "linux", arch: "x64", abi: "musl", baseline: true, distro: "alpine", release: "3.23", tier: "latest" },
{ os: "windows", arch: "x64", release: "2019", tier: "oldest" },
{ os: "windows", arch: "x64", release: "2019", baseline: true, tier: "oldest" },
// TODO: Enable when Windows ARM64 CI runners are ready
// { os: "windows", arch: "aarch64", release: "2019", tier: "oldest" },
{ os: "windows", arch: "aarch64", release: "11", tier: "latest" },
];
/**
@@ -304,15 +319,8 @@ function getCppAgent(platform, options) {
};
}
// Cross-compile Windows ARM64 from x64 runners
if (os === "windows" && arch === "aarch64") {
return getEc2Agent({ ...platform, arch: "x64" }, options, {
instanceType: "c7i.4xlarge",
});
}
return getEc2Agent(platform, options, {
instanceType: arch === "aarch64" ? "c8g.4xlarge" : "c7i.4xlarge",
instanceType: os === "windows" ? getAzureVmSize(os, arch) : arch === "aarch64" ? "c8g.4xlarge" : "c7i.4xlarge",
});
}
@@ -333,10 +341,8 @@ function getLinkBunAgent(platform, options) {
}
if (os === "windows") {
// Cross-compile Windows ARM64 from x64 runners
const agentPlatform = arch === "aarch64" ? { ...platform, arch: "x64" } : platform;
return getEc2Agent(agentPlatform, options, {
instanceType: "r7i.large",
return getEc2Agent(platform, options, {
instanceType: getAzureVmSize(os, arch),
});
}
@@ -363,7 +369,17 @@ function getZigPlatform() {
* @param {PipelineOptions} options
* @returns {Agent}
*/
function getZigAgent(_platform, options) {
function getZigAgent(platform, options) {
const { os, arch } = platform;
// Windows builds Zig natively on Azure
if (os === "windows") {
return getEc2Agent(platform, options, {
instanceType: getAzureVmSize(os, arch),
});
}
// Everything else cross-compiles from Linux aarch64
return getEc2Agent(getZigPlatform(), options, {
instanceType: "r8g.large",
});
@@ -388,7 +404,7 @@ function getTestAgent(platform, options) {
// TODO: delete this block when we upgrade to mimalloc v3
if (os === "windows") {
return getEc2Agent(platform, options, {
instanceType: "c7i.2xlarge",
instanceType: getAzureVmSize(os, arch, "test"),
cpuCount: 2,
threadsPerCore: 1,
});
@@ -465,17 +481,6 @@ function getBuildCommand(target, options, label) {
return `bun run build:${buildProfile}`;
}
/**
* Get extra flags needed when cross-compiling Windows ARM64 from x64.
* Applied to C++ and link steps (not Zig, which has its own toolchain handling).
*/
function getWindowsArm64CrossFlags(target) {
if (target.os === "windows" && target.arch === "aarch64") {
return " --toolchain windows-aarch64";
}
return "";
}
/**
* @param {Platform} platform
* @param {PipelineOptions} options
@@ -483,7 +488,6 @@ function getWindowsArm64CrossFlags(target) {
*/
function getBuildCppStep(platform, options) {
const command = getBuildCommand(platform, options);
const crossFlags = getWindowsArm64CrossFlags(platform);
return {
key: `${getTargetKey(platform)}-build-cpp`,
@@ -498,7 +502,7 @@ function getBuildCppStep(platform, options) {
// We used to build the C++ dependencies and bun in separate steps.
// However, as long as the zig build takes longer than both sequentially,
// it's cheaper to run them in the same step. Can be revisited in the future.
command: [`${command}${crossFlags} --target bun`, `${command}${crossFlags} --target dependencies`],
command: [`${command} --target bun`, `${command} --target dependencies`],
};
}
@@ -524,7 +528,10 @@ function getBuildToolchain(target) {
* @returns {Step}
*/
function getBuildZigStep(platform, options) {
const { os, arch } = platform;
const toolchain = getBuildToolchain(platform);
// Native Windows builds don't need a cross-compilation toolchain
const toolchainArg = os === "windows" ? "" : ` --toolchain ${toolchain}`;
return {
key: `${getTargetKey(platform)}-build-zig`,
retry: getRetry(),
@@ -532,7 +539,7 @@ function getBuildZigStep(platform, options) {
agents: getZigAgent(platform, options),
cancel_on_build_failing: isMergeQueue(),
env: getBuildEnv(platform, options),
command: `${getBuildCommand(platform, options)} --target bun-zig --toolchain ${toolchain}`,
command: `${getBuildCommand(platform, options)} --target bun-zig${toolchainArg}`,
timeout_in_minutes: 35,
};
}
@@ -555,7 +562,7 @@ function getLinkBunStep(platform, options) {
ASAN_OPTIONS: "allow_user_segv_handler=1:disable_coredump=0:detect_leaks=0",
...getBuildEnv(platform, options),
},
command: `${getBuildCommand(platform, options, "build-bun")}${getWindowsArm64CrossFlags(platform)} --target bun`,
command: `${getBuildCommand(platform, options, "build-bun")} --target bun`,
};
}
@@ -717,14 +724,14 @@ function getTestBunStep(platform, options, testOptions = {}) {
agents: getTestAgent(platform, options),
retry: getRetry(),
cancel_on_build_failing: isMergeQueue(),
parallelism: os === "darwin" ? 2 : 20,
parallelism: os === "darwin" ? 2 : os === "windows" ? 8 : 20,
timeout_in_minutes: profile === "asan" || os === "windows" ? 45 : 30,
env: {
ASAN_OPTIONS: "allow_user_segv_handler=1:disable_coredump=0:detect_leaks=0",
},
command:
os === "windows"
? `node .\\scripts\\runner.node.mjs ${args.join(" ")}`
? `pwsh -NoProfile -File .\\scripts\\vs-shell.ps1 node .\\scripts\\runner.node.mjs ${args.join(" ")}`
: `./scripts/runner.node.mjs ${args.join(" ")}`,
};
}
@@ -739,6 +746,7 @@ function getBuildImageStep(platform, options) {
const { publishImages } = options;
const action = publishImages ? "publish-image" : "create-image";
const cloud = os === "windows" ? "azure" : "aws";
const command = [
"node",
"./scripts/machine.mjs",
@@ -747,7 +755,7 @@ function getBuildImageStep(platform, options) {
`--arch=${arch}`,
distro && `--distro=${distro}`,
`--release=${release}`,
"--cloud=aws",
`--cloud=${cloud}`,
"--ci",
"--authorized-org=oven-sh",
];
@@ -1169,9 +1177,10 @@ async function getPipelineOptions() {
skipBuilds: parseOption(/\[(skip builds?|no builds?|only tests?)\]/i),
forceBuilds: parseOption(/\[(force builds?)\]/i),
skipTests: parseOption(/\[(skip tests?|no tests?|only builds?)\]/i),
buildImages: parseOption(/\[(build images?)\]/i),
buildImages: parseOption(/\[(build (?:(?:windows|linux) )?images?)\]/i),
dryRun: parseOption(/\[(dry run)\]/i),
publishImages: parseOption(/\[(publish images?)\]/i),
publishImages: parseOption(/\[(publish (?:(?:windows|linux) )?images?)\]/i),
imageFilter: (commitMessage.match(/\[(?:build|publish) (windows|linux) images?\]/i) || [])[1]?.toLowerCase(),
buildPlatforms: Array.from(buildPlatformsMap.values()),
testPlatforms: Array.from(testPlatformsMap.values()),
};
@@ -1196,13 +1205,12 @@ async function getPipeline(options = {}) {
return;
}
const { buildPlatforms = [], testPlatforms = [], buildImages, publishImages } = options;
const { buildPlatforms = [], testPlatforms = [], buildImages, publishImages, imageFilter } = options;
const imagePlatforms = new Map(
buildImages || publishImages
? [...buildPlatforms, ...testPlatforms]
.filter(({ os }) => os !== "darwin")
// Windows ARM64 cross-compiles from x64 runners, no separate image needed
.filter(({ os, arch }) => !(os === "windows" && arch === "aarch64"))
.filter(({ os, distro }) => !imageFilter || os === imageFilter || distro === imageFilter)
.map(platform => [getImageKey(platform), platform])
: [],
);

View File

@@ -161,6 +161,31 @@ test("(multi-file test) my feature", async () => {
- `src/sql/` - SQL database integrations
- `src/bake/` - Server-side rendering framework
#### Vendored Dependencies (`vendor/`)
Third-party C/C++ libraries are vendored locally and can be read from disk (these are not git submodules):
- `vendor/boringssl/` - BoringSSL (TLS/crypto)
- `vendor/brotli/` - Brotli compression
- `vendor/cares/` - c-ares (async DNS)
- `vendor/hdrhistogram/` - HdrHistogram (latency tracking)
- `vendor/highway/` - Google Highway (SIMD)
- `vendor/libarchive/` - libarchive (tar/zip)
- `vendor/libdeflate/` - libdeflate (fast deflate)
- `vendor/libuv/` - libuv (Windows event loop)
- `vendor/lolhtml/` - lol-html (HTML rewriter)
- `vendor/lshpack/` - ls-hpack (HTTP/2 HPACK)
- `vendor/mimalloc/` - mimalloc (memory allocator)
- `vendor/nodejs/` - Node.js headers (compatibility)
- `vendor/picohttpparser/` - PicoHTTPParser (HTTP parsing)
- `vendor/tinycc/` - TinyCC (FFI JIT compiler, fork: oven-sh/tinycc)
- `vendor/WebKit/` - WebKit/JavaScriptCore (JS engine)
- `vendor/zig/` - Zig compiler/stdlib
- `vendor/zlib/` - zlib (compression, cloudflare fork)
- `vendor/zstd/` - Zstandard (compression)
Build configuration for these is in `cmake/targets/Build*.cmake`.
### JavaScript Class Implementation (C++)
When implementing JavaScript classes in C++:

View File

@@ -434,7 +434,7 @@ function(register_command)
endif()
# SKIP_CODEGEN: Skip commands that use BUN_EXECUTABLE if all outputs exist
# This is used for Windows ARM64 builds where x64 bun crashes under emulation
# Useful for bootstrapping new platforms where bun may not be available
if(SKIP_CODEGEN AND CMD_EXECUTABLE STREQUAL "${BUN_EXECUTABLE}")
set(ALL_OUTPUTS_EXIST TRUE)
foreach(output ${CMD_OUTPUTS})
@@ -456,7 +456,7 @@ function(register_command)
endif()
return()
else()
message(FATAL_ERROR "SKIP_CODEGEN: Cannot skip ${CMD_TARGET} - missing outputs. Run codegen on x64 first.")
message(FATAL_ERROR "SKIP_CODEGEN: Cannot skip ${CMD_TARGET} - missing outputs.")
endif()
endif()
@@ -831,13 +831,6 @@ function(register_cmake_command)
list(APPEND MAKE_EFFECTIVE_ARGS "-DCMAKE_${flag}=${MAKE_${flag}}")
endforeach()
# Workaround for CMake 4.1.0 bug: Force correct machine type for Windows ARM64
# Use toolchain file and set CMP0197 policy to prevent duplicate /machine: flags
if(WIN32 AND CMAKE_SYSTEM_PROCESSOR MATCHES "ARM64|aarch64|AARCH64")
list(APPEND MAKE_EFFECTIVE_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CWD}/cmake/toolchains/windows-aarch64.cmake")
list(APPEND MAKE_EFFECTIVE_ARGS "-DCMAKE_POLICY_DEFAULT_CMP0197=NEW")
list(APPEND MAKE_EFFECTIVE_ARGS "-DCMAKE_PROJECT_INCLUDE=${CWD}/cmake/arm64-static-lib-fix.cmake")
endif()
if(DEFINED FRESH)
list(APPEND MAKE_EFFECTIVE_ARGS --fresh)

View File

@@ -4,7 +4,7 @@ endif()
optionx(BUN_LINK_ONLY BOOL "If only the linking step should be built" DEFAULT OFF)
optionx(BUN_CPP_ONLY BOOL "If only the C++ part of Bun should be built" DEFAULT OFF)
optionx(SKIP_CODEGEN BOOL "Skip JavaScript codegen (for Windows ARM64 debug)" DEFAULT OFF)
optionx(SKIP_CODEGEN BOOL "Skip JavaScript codegen (useful for bootstrapping new platforms)" DEFAULT OFF)
optionx(BUILDKITE BOOL "If Buildkite is enabled" DEFAULT OFF)
optionx(GITHUB_ACTIONS BOOL "If GitHub Actions is enabled" DEFAULT OFF)
@@ -58,18 +58,6 @@ else()
message(FATAL_ERROR "Unsupported architecture: ${CMAKE_SYSTEM_PROCESSOR}")
endif()
# CMake 4.0+ policy CMP0197 controls how MSVC machine type flags are handled
# Setting to NEW prevents duplicate /machine: flags being added to linker commands
if(WIN32 AND ARCH STREQUAL "aarch64")
set(CMAKE_POLICY_DEFAULT_CMP0197 NEW)
set(CMAKE_MSVC_CMP0197 NEW)
# Set linker flags for exe/shared linking
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /machine:ARM64")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /machine:ARM64")
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} /machine:ARM64")
set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} /machine:ARM64")
endif()
# Windows Code Signing Option
if(WIN32)
optionx(ENABLE_WINDOWS_CODESIGNING BOOL "Enable Windows code signing with DigiCert KeyLocker" DEFAULT OFF)

View File

@@ -1,8 +0,0 @@
# This file is included after project() via CMAKE_PROJECT_INCLUDE
# It fixes the static library creation command to use ARM64 machine type
if(WIN32 AND CMAKE_SYSTEM_PROCESSOR STREQUAL \"aarch64\")
# Override the static library creation commands to avoid spurious /machine:x64 flags
set(CMAKE_C_CREATE_STATIC_LIBRARY \"<CMAKE_AR> /nologo /machine:ARM64 /out:<TARGET> <OBJECTS>\" CACHE STRING \"\" FORCE)
set(CMAKE_CXX_CREATE_STATIC_LIBRARY \"<CMAKE_AR> /nologo /machine:ARM64 /out:<TARGET> <OBJECTS>\" CACHE STRING \"\" FORCE)
endif()

View File

@@ -21,12 +21,7 @@ if(NOT DEFINED CMAKE_HOST_SYSTEM_PROCESSOR)
endif()
if(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "arm64|ARM64|aarch64|AARCH64")
# Windows ARM64 can run x86_64 via emulation, and no native ARM64 Zig build exists yet
if(CMAKE_HOST_WIN32)
set(ZIG_ARCH "x86_64")
else()
set(ZIG_ARCH "aarch64")
endif()
set(ZIG_ARCH "aarch64")
elseif(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "amd64|AMD64|x86_64|X86_64|x64|X64")
set(ZIG_ARCH "x86_64")
else()

View File

@@ -1,34 +0,0 @@
@echo off
setlocal enabledelayedexpansion
REM Wrapper for llvm-lib that strips conflicting /machine:x64 flag for ARM64 builds
REM This is a workaround for CMake 4.1.0 bug
REM Find llvm-lib.exe - check LLVM_LIB env var, then PATH, then known locations
if defined LLVM_LIB (
set "LLVM_LIB_EXE=!LLVM_LIB!"
) else (
where llvm-lib.exe >nul 2>&1
if !ERRORLEVEL! equ 0 (
for /f "delims=" %%i in ('where llvm-lib.exe') do set "LLVM_LIB_EXE=%%i"
) else if exist "C:\Program Files\LLVM\bin\llvm-lib.exe" (
set "LLVM_LIB_EXE=C:\Program Files\LLVM\bin\llvm-lib.exe"
) else (
echo Error: Cannot find llvm-lib.exe. Set LLVM_LIB environment variable or add LLVM to PATH.
exit /b 1
)
)
set "ARGS="
for %%a in (%*) do (
set "ARG=%%a"
if /i "!ARG!"=="/machine:x64" (
REM Skip this argument
) else (
set "ARGS=!ARGS! %%a"
)
)
"!LLVM_LIB_EXE!" %ARGS%
exit /b %ERRORLEVEL%

View File

@@ -1,18 +0,0 @@
# Wrapper for llvm-lib that strips conflicting /machine:x64 flag for ARM64 builds
# This is a workaround for CMake 4.1.0 bug where both /machine:ARM64 and /machine:x64 are added
# Find llvm-lib.exe - check LLVM_LIB env var, then PATH, then known locations
if ($env:LLVM_LIB) {
$llvmLib = $env:LLVM_LIB
} elseif (Get-Command llvm-lib.exe -ErrorAction SilentlyContinue) {
$llvmLib = (Get-Command llvm-lib.exe).Source
} elseif (Test-Path "C:\Program Files\LLVM\bin\llvm-lib.exe") {
$llvmLib = "C:\Program Files\LLVM\bin\llvm-lib.exe"
} else {
Write-Error "Cannot find llvm-lib.exe. Set LLVM_LIB environment variable or add LLVM to PATH."
exit 1
}
$filteredArgs = $args | Where-Object { $_ -ne "/machine:x64" }
& $llvmLib @filteredArgs
exit $LASTEXITCODE

View File

@@ -1,34 +0,0 @@
@echo off
setlocal enabledelayedexpansion
REM Wrapper for llvm-lib that strips conflicting /machine:x64 flag for ARM64 builds
REM This is a workaround for CMake 4.1.0 bug
REM Find llvm-lib.exe - check LLVM_LIB env var, then PATH, then known locations
if defined LLVM_LIB (
set "LLVM_LIB_EXE=!LLVM_LIB!"
) else (
where llvm-lib.exe >nul 2>&1
if !ERRORLEVEL! equ 0 (
for /f "delims=" %%i in ('where llvm-lib.exe') do set "LLVM_LIB_EXE=%%i"
) else if exist "C:\Program Files\LLVM\bin\llvm-lib.exe" (
set "LLVM_LIB_EXE=C:\Program Files\LLVM\bin\llvm-lib.exe"
) else (
echo Error: Cannot find llvm-lib.exe. Set LLVM_LIB environment variable or add LLVM to PATH.
exit /b 1
)
)
set NEWARGS=
for %%a in (%*) do (
set "ARG=%%a"
if /i "!ARG!"=="/machine:x64" (
REM Skip /machine:x64 argument
) else (
set "NEWARGS=!NEWARGS! %%a"
)
)
"!LLVM_LIB_EXE!" %NEWARGS%
exit /b %ERRORLEVEL%

View File

@@ -7,13 +7,6 @@ register_repository(
4f4f5ef8ebc6e23cbf393428f0ab1b526773f7ac
)
set(BORINGSSL_CMAKE_ARGS -DBUILD_SHARED_LIBS=OFF)
# Disable ASM on Windows ARM64 to avoid mixing non-ARM object files into ARM64 libs
if(WIN32 AND CMAKE_SYSTEM_PROCESSOR MATCHES "ARM64|aarch64|AARCH64")
list(APPEND BORINGSSL_CMAKE_ARGS -DOPENSSL_NO_ASM=1)
endif()
register_cmake_command(
TARGET
boringssl
@@ -22,7 +15,7 @@ register_cmake_command(
ssl
decrepit
ARGS
${BORINGSSL_CMAKE_ARGS}
-DBUILD_SHARED_LIBS=OFF
INCLUDES
include
)

View File

@@ -341,6 +341,7 @@ register_command(
SOURCES
${BUN_JAVASCRIPT_CODEGEN_SOURCES}
${BUN_CXX_SOURCES}
${ESBUILD_EXECUTABLE}
OUTPUTS
${BUN_CPP_OUTPUTS}
)
@@ -362,7 +363,7 @@ register_command(
)
if(SKIP_CODEGEN)
# Skip JavaScript codegen - useful for Windows ARM64 debug builds where bun crashes
# Skip JavaScript codegen - useful for bootstrapping new platforms
message(STATUS "SKIP_CODEGEN is ON - skipping bun-js-modules codegen")
foreach(output ${BUN_JAVASCRIPT_OUTPUTS})
if(NOT EXISTS ${output})
@@ -680,8 +681,7 @@ if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm|ARM|arm64|ARM64|aarch64|AARCH64")
if(APPLE)
set(ZIG_CPU "apple_m1")
elseif(WIN32)
# Windows ARM64: use a specific CPU with NEON support
# Zig running under x64 emulation would detect wrong CPU with "native"
# Windows ARM64: use a specific CPU target for consistent builds
set(ZIG_CPU "cortex_a76")
else()
set(ZIG_CPU "native")
@@ -1457,8 +1457,6 @@ if(NOT BUN_CPP_ONLY)
# ==856230==See https://github.com/google/sanitizers/issues/856 for possible workarounds.
# the linked issue refers to very old kernels but this still happens to us on modern ones.
# disabling ASLR to run the binary works around it
# Skip post-build test/features when cross-compiling (can't run the target binary on the host)
if(NOT CMAKE_CROSSCOMPILING)
set(TEST_BUN_COMMAND_BASE ${BUILD_PATH}/${bunExe} --revision)
set(TEST_BUN_COMMAND_ENV_WRAP
${CMAKE_COMMAND} -E env BUN_DEBUG_QUIET_LOGS=1)
@@ -1507,7 +1505,6 @@ if(NOT BUN_CPP_ONLY)
${BUILD_PATH}/features.json
)
endif()
endif() # NOT CMAKE_CROSSCOMPILING
if(CMAKE_HOST_APPLE AND bunStrip)
register_command(
@@ -1554,10 +1551,7 @@ if(NOT BUN_CPP_ONLY)
string(REPLACE bun ${bunTriplet} bunPath ${bun})
endif()
set(bunFiles ${bunExe})
if(NOT CMAKE_CROSSCOMPILING)
list(APPEND bunFiles features.json)
endif()
set(bunFiles ${bunExe} features.json)
if(WIN32)
list(APPEND bunFiles ${bun}.pdb)
elseif(APPLE)

View File

@@ -26,7 +26,7 @@ if(RELEASE)
list(APPEND LOLHTML_BUILD_ARGS --release)
endif()
# Cross-compilation: tell cargo to target ARM64
# Explicitly tell cargo to target ARM64 on Windows ARM64
if(WIN32 AND CMAKE_SYSTEM_PROCESSOR MATCHES "ARM64|aarch64|AARCH64")
list(APPEND LOLHTML_BUILD_ARGS --target aarch64-pc-windows-msvc)
set(LOLHTML_LIBRARY ${LOLHTML_BUILD_PATH}/aarch64-pc-windows-msvc/${LOLHTML_BUILD_TYPE}/${CMAKE_STATIC_LIBRARY_PREFIX}lolhtml${CMAKE_STATIC_LIBRARY_SUFFIX})
@@ -57,11 +57,11 @@ if(WIN32)
if(MSVC_VERSIONS)
list(GET MSVC_VERSIONS -1 MSVC_LATEST) # Get the latest version
if(CMAKE_SYSTEM_PROCESSOR MATCHES "ARM64|aarch64")
# Use Hostx64/arm64 for cross-compilation from x64, fall back to native
if(EXISTS "${MSVC_LATEST}/bin/Hostx64/arm64/link.exe")
set(MSVC_LINK_PATH "${MSVC_LATEST}/bin/Hostx64/arm64/link.exe")
else()
# Prefer native HostARM64, fall back to Hostx64/arm64
if(EXISTS "${MSVC_LATEST}/bin/HostARM64/arm64/link.exe")
set(MSVC_LINK_PATH "${MSVC_LATEST}/bin/HostARM64/arm64/link.exe")
else()
set(MSVC_LINK_PATH "${MSVC_LATEST}/bin/Hostx64/arm64/link.exe")
endif()
set(CARGO_LINKER_VAR "CARGO_TARGET_AARCH64_PC_WINDOWS_MSVC_LINKER")
set(MSVC_LIB_ARCH "arm64")

View File

@@ -7,6 +7,32 @@ register_repository(
886098f3f339617b4243b286f5ed364b9989e245
)
# cloudflare/zlib hardcodes STATIC_LIBRARY_FLAGS "/machine:x64" for 64-bit MSVC,
# which conflicts with ARM64 object files. Patch it after clone to use the correct
# machine type based on CMAKE_SYSTEM_PROCESSOR.
if(WIN32 AND CMAKE_SYSTEM_PROCESSOR MATCHES "ARM64|aarch64|AARCH64")
set(ZLIB_PATCH_SCRIPT "${BUILD_PATH}/zlib-arm64-patch.cmake")
file(WRITE ${ZLIB_PATCH_SCRIPT} "
file(READ \"\${ZLIB_CMAKELISTS}\" content)
string(REPLACE \"/machine:x64\" \"/machine:ARM64\" content \"\${content}\")
file(WRITE \"\${ZLIB_CMAKELISTS}\" \"\${content}\")
file(TOUCH \"\${ZLIB_PATCH_MARKER}\")
")
register_command(
COMMENT "Patching zlib for ARM64"
TARGET patch-zlib
COMMAND ${CMAKE_COMMAND}
-DZLIB_CMAKELISTS=${VENDOR_PATH}/zlib/CMakeLists.txt
-DZLIB_PATCH_MARKER=${VENDOR_PATH}/zlib/.arm64-patched
-P ${ZLIB_PATCH_SCRIPT}
SOURCES ${VENDOR_PATH}/zlib/.ref
OUTPUTS ${VENDOR_PATH}/zlib/.arm64-patched
)
if(TARGET clone-zlib)
add_dependencies(patch-zlib clone-zlib)
endif()
endif()
# https://gitlab.kitware.com/cmake/cmake/-/issues/25755
if(APPLE)
set(ZLIB_CMAKE_C_FLAGS "-fno-define-target-os-macros")
@@ -38,3 +64,8 @@ register_cmake_command(
INCLUDES
.
)
# Ensure zlib is patched before configure
if(TARGET patch-zlib)
add_dependencies(configure-zlib patch-zlib)
endif()

View File

@@ -4,34 +4,3 @@ set(CMAKE_SYSTEM_PROCESSOR aarch64)
set(CMAKE_C_COMPILER_WORKS ON)
set(CMAKE_CXX_COMPILER_WORKS ON)
set(CMAKE_CROSSCOMPILING ON)
# The rest only applies when building on Windows (C++ and link steps).
# The Zig step runs on Linux and only needs CMAKE_SYSTEM_NAME/PROCESSOR above.
if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
# Ensure clang/clang-cl targets Windows ARM64 (otherwise ARM64-specific flags like
# -march=armv8-a are rejected as x86-only).
set(CMAKE_C_COMPILER_TARGET aarch64-pc-windows-msvc CACHE STRING "" FORCE)
set(CMAKE_CXX_COMPILER_TARGET aarch64-pc-windows-msvc CACHE STRING "" FORCE)
# ARM64 has lock-free atomics (highway's FindAtomics check can't run ARM64 test binary on x64)
set(ATOMICS_LOCK_FREE_INSTRUCTIONS TRUE CACHE BOOL "" FORCE)
set(HAVE_CXX_ATOMICS_WITHOUT_LIB TRUE CACHE BOOL "" FORCE)
set(HAVE_CXX_ATOMICS64_WITHOUT_LIB TRUE CACHE BOOL "" FORCE)
# Force ARM64 architecture ID - this is what CMake uses to determine /machine: flag
set(MSVC_C_ARCHITECTURE_ID ARM64 CACHE INTERNAL "")
set(MSVC_CXX_ARCHITECTURE_ID ARM64 CACHE INTERNAL "")
# CMake 4.0+ policy CMP0197 controls how MSVC machine type flags are handled
set(CMAKE_POLICY_DEFAULT_CMP0197 NEW CACHE INTERNAL "")
# Clear any inherited static linker flags that might have wrong machine types
set(CMAKE_STATIC_LINKER_FLAGS "" CACHE STRING "" FORCE)
# Use wrapper script for llvm-lib that strips /machine:x64 flags
# This works around CMake 4.1.0 bug where both ARM64 and x64 machine flags are added
get_filename_component(_TOOLCHAIN_DIR "${CMAKE_CURRENT_LIST_DIR}" DIRECTORY)
set(CMAKE_AR "${_TOOLCHAIN_DIR}/scripts/llvm-lib-wrapper.bat" CACHE FILEPATH "" FORCE)
endif()

View File

@@ -17,13 +17,7 @@ if (NOT CI)
set(BUN_EXECUTABLE ${BUN_EXECUTABLE} CACHE FILEPATH "Bun executable" FORCE)
endif()
# On Windows ARM64, we need to add --smol flag to avoid crashes when running
# x64 bun under WoW64 emulation
if(WIN32 AND ARCH STREQUAL "aarch64")
set(BUN_FLAGS "--smol" CACHE STRING "Extra flags for bun executable")
else()
set(BUN_FLAGS "" CACHE STRING "Extra flags for bun executable")
endif()
set(BUN_FLAGS "" CACHE STRING "Extra flags for bun executable")
# If this is not set, some advanced features are not checked.
# https://github.com/oven-sh/bun/blob/cd7f6a1589db7f1e39dc4e3f4a17234afbe7826c/src/bun.js/javascript.zig#L1069-L1072

View File

@@ -51,7 +51,7 @@ if(APPLE)
endif()
if(WIN32)
# Prefer standalone LLVM over VS-bundled (standalone supports cross-compilation)
# Prefer standalone LLVM over VS-bundled
list(APPEND LLVM_PATHS "C:/Program Files/LLVM/bin")
endif()

View File

@@ -9,8 +9,6 @@ if(NOT WEBKIT_VERSION)
set(WEBKIT_VERSION 8af7958ff0e2a4787569edf64641a1ae7cfe074a)
endif()
# Use preview build URL for Windows ARM64 until the fix is merged to main
set(WEBKIT_PREVIEW_PR 140)
string(SUBSTRING ${WEBKIT_VERSION} 0 16 WEBKIT_VERSION_PREFIX)
string(SUBSTRING ${WEBKIT_VERSION} 0 8 WEBKIT_VERSION_SHORT)

View File

@@ -20,7 +20,7 @@ else()
unsupported(CMAKE_SYSTEM_NAME)
endif()
set(ZIG_COMMIT "c1423ff3fc7064635773a4a4616c5bf986eb00fe")
set(ZIG_COMMIT "c031cbebf5b063210473ff5204a24ebfb2492c72")
optionx(ZIG_TARGET STRING "The zig target to use" DEFAULT ${DEFAULT_ZIG_TARGET})
if(CMAKE_BUILD_TYPE STREQUAL "Release")
@@ -55,7 +55,14 @@ optionx(ZIG_OBJECT_FORMAT "obj|bc" "Output file format for Zig object files" DEF
optionx(ZIG_LOCAL_CACHE_DIR FILEPATH "The path to local the zig cache directory" DEFAULT ${CACHE_PATH}/zig/local)
optionx(ZIG_GLOBAL_CACHE_DIR FILEPATH "The path to the global zig cache directory" DEFAULT ${CACHE_PATH}/zig/global)
optionx(ZIG_COMPILER_SAFE BOOL "Download a ReleaseSafe build of the Zig compiler." DEFAULT ${CI})
# The ReleaseSafe Zig compiler for Windows ARM64 has an LLVM SEH epilogue bug
# (incorrect size for compiler_rt.rem_pio2_large epilogue). Use the default build instead.
if(CI AND WIN32 AND DEFAULT_ZIG_ARCH STREQUAL "aarch64")
set(DEFAULT_ZIG_COMPILER_SAFE OFF)
else()
set(DEFAULT_ZIG_COMPILER_SAFE ${CI})
endif()
optionx(ZIG_COMPILER_SAFE BOOL "Download a ReleaseSafe build of the Zig compiler." DEFAULT ${DEFAULT_ZIG_COMPILER_SAFE})
setenv(ZIG_LOCAL_CACHE_DIR ${ZIG_LOCAL_CACHE_DIR})
setenv(ZIG_GLOBAL_CACHE_DIR ${ZIG_GLOBAL_CACHE_DIR})

View File

@@ -22,8 +22,9 @@ bun upgrade
- [Linux, arm64](https://www.npmjs.com/package/@oven/bun-linux-aarch64)
- [Linux, x64](https://www.npmjs.com/package/@oven/bun-linux-x64)
- [Linux, x64 (without AVX2 instructions)](https://www.npmjs.com/package/@oven/bun-linux-x64-baseline)
- [Windows](https://www.npmjs.com/package/@oven/bun-windows-x64)
- [Windows (without AVX2 instructions)](https://www.npmjs.com/package/@oven/bun-windows-x64-baseline)
- [Windows, x64](https://www.npmjs.com/package/@oven/bun-windows-x64)
- [Windows, x64 (without AVX2 instructions)](https://www.npmjs.com/package/@oven/bun-windows-x64-baseline)
- [Windows ARM64](https://www.npmjs.com/package/@oven/bun-windows-aarch64)
### Future Platforms

View File

@@ -95,12 +95,12 @@ export const platforms: Platform[] = [
bin: "bun-windows-x64-baseline",
exe: "bin/bun.exe",
},
// {
// os: "win32",
// arch: "arm64",
// bin: "bun-windows-aarch64",
// exe: "bin/bun.exe",
// },
{
os: "win32",
arch: "arm64",
bin: "bun-windows-aarch64",
exe: "bin/bun.exe",
},
];
export const supportedPlatforms: Platform[] = platforms

View File

@@ -2428,7 +2428,7 @@ declare module "bun" {
}
namespace Build {
type Architecture = "x64" | "arm64";
type Architecture = "x64" | "arm64" | "aarch64";
type Libc = "glibc" | "musl";
type SIMD = "baseline" | "modern";
type CompileTarget =

View File

@@ -828,7 +828,7 @@ struct us_socket_t *us_socket_context_adopt_socket(int ssl, struct us_socket_con
struct us_socket_t *new_s = s;
if (ext_size != -1) {
struct us_poll_t *pool_ref = &s->p;
new_s = (struct us_socket_t *) us_poll_resize(pool_ref, loop, sizeof(struct us_socket_t) + old_ext_size, sizeof(struct us_socket_t) + ext_size);
new_s = (struct us_socket_t *) us_poll_resize(pool_ref, loop, sizeof(struct us_socket_t) - sizeof(struct us_poll_t) + old_ext_size, sizeof(struct us_socket_t) - sizeof(struct us_poll_t) + ext_size);
if(new_s != s) {
/* Mark the old socket as closed */
s->flags.is_closed = 1;

665
scripts/azure.mjs Normal file
View File

@@ -0,0 +1,665 @@
// Azure REST API client for machine.mjs
// Used by the [build images] pipeline to create Windows VM images (x64 and ARM64)
import { getSecret, isCI } from "./utils.mjs";
/**
* @typedef {Object} AzureConfig
* @property {string} tenantId
* @property {string} clientId
* @property {string} clientSecret
* @property {string} subscriptionId
* @property {string} resourceGroup
* @property {string} location
* @property {string} galleryName
*/
/** @returns {AzureConfig} */
function getConfig() {
const env = (name, fallback) => {
if (isCI) {
try {
return getSecret(name, { required: !fallback }) || fallback;
} catch {
if (fallback) return fallback;
throw new Error(`Azure secret not found: ${name}`);
}
}
return process.env[name] || fallback;
};
return {
tenantId: env("AZURE_TENANT_ID"),
clientId: env("AZURE_CLIENT_ID"),
clientSecret: env("AZURE_CLIENT_SECRET"),
subscriptionId: env("AZURE_SUBSCRIPTION_ID"),
resourceGroup: env("AZURE_RESOURCE_GROUP", "BUN-CI"),
location: env("AZURE_LOCATION", "eastus2"),
galleryName: env("AZURE_GALLERY_NAME", "bunCIGallery2"),
};
}
let _config;
function config() {
return (_config ??= getConfig());
}
// ============================================================================
// Authentication
// ============================================================================
let _accessToken = null;
let _tokenExpiry = 0;
async function getAccessToken() {
if (_accessToken && Date.now() < _tokenExpiry - 300_000) {
return _accessToken;
}
const { tenantId, clientId, clientSecret } = config();
const response = await fetch(`https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "client_credentials",
client_id: clientId,
client_secret: clientSecret,
scope: "https://management.azure.com/.default",
}),
});
if (!response.ok) {
throw new Error(`[azure] Auth failed: ${response.status} ${await response.text()}`);
}
const data = await response.json();
_accessToken = data.access_token;
_tokenExpiry = Date.now() + data.expires_in * 1000;
return _accessToken;
}
// ============================================================================
// REST Client
// ============================================================================
/**
* @param {"GET"|"PUT"|"POST"|"PATCH"|"DELETE"} method
* @param {string} path - Relative path under management.azure.com, or absolute URL
* @param {object} [body]
* @param {string} [apiVersion]
*/
async function azureFetch(method, path, body, apiVersion = "2024-07-01") {
const token = await getAccessToken();
const url = path.startsWith("http") ? new URL(path) : new URL(`https://management.azure.com${path}`);
if (!url.searchParams.has("api-version")) {
url.searchParams.set("api-version", apiVersion);
}
const options = {
method,
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
};
if (body && method !== "GET" && method !== "DELETE") {
options.body = JSON.stringify(body);
}
for (let attempt = 0; attempt < 3; attempt++) {
const response = await fetch(url, options);
if (response.status === 429 || response.status >= 500) {
const wait = Math.pow(2, attempt) * 1000;
console.warn(`[azure] ${method} ${path} returned ${response.status}, retrying in ${wait}ms...`);
await new Promise(r => setTimeout(r, wait));
continue;
}
// 202 Accepted — async operation, poll for completion
if (response.status === 202) {
const operationUrl = response.headers.get("Azure-AsyncOperation") || response.headers.get("Location");
if (operationUrl) {
return waitForOperation(operationUrl);
}
}
if (response.status === 204) {
return null;
}
if (!response.ok) {
const text = await response.text();
throw new Error(`[azure] ${method} ${path} failed: ${response.status} ${text}`);
}
const text = await response.text();
return text ? JSON.parse(text) : null;
}
throw new Error(`[azure] ${method} ${path} failed after 3 retries`);
}
async function waitForOperation(operationUrl, maxWaitMs = 3_600_000) {
const start = Date.now();
let fetchErrors = 0;
while (Date.now() - start < maxWaitMs) {
const token = await getAccessToken();
let response;
try {
response = await fetch(operationUrl, {
headers: { Authorization: `Bearer ${token}` },
});
} catch (err) {
fetchErrors++;
if (fetchErrors > 10) {
throw new Error(`[azure] Operation poll failed after ${fetchErrors} fetch errors`, { cause: err });
}
console.warn(`[azure] Operation poll fetch error (${fetchErrors}), retrying...`);
await new Promise(r => setTimeout(r, 10_000));
continue;
}
if (!response.ok) {
throw new Error(`[azure] Operation poll failed: ${response.status} ${await response.text()}`);
}
const data = await response.json();
if (data.status === "Succeeded") {
return data.properties?.output ?? data;
}
if (data.status === "Failed" || data.status === "Canceled") {
throw new Error(`[azure] Operation ${data.status}: ${data.error?.message ?? "unknown"}`);
}
await new Promise(r => setTimeout(r, 5000));
}
throw new Error(`[azure] Operation timed out after ${maxWaitMs}ms`);
}
// ============================================================================
// Resource helpers
// ============================================================================
function rgPath() {
const { subscriptionId, resourceGroup } = config();
return `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroup}`;
}
// ============================================================================
// Public IP
// ============================================================================
async function createPublicIp(name) {
const { location } = config();
console.log(`[azure] Creating public IP: ${name}`);
const result = await azureFetch("PUT", `${rgPath()}/providers/Microsoft.Network/publicIPAddresses/${name}`, {
location,
sku: { name: "Standard" },
properties: {
publicIPAllocationMethod: "Static",
deleteOption: "Delete",
},
});
return result?.properties?.ipAddress;
}
async function deletePublicIp(name) {
await azureFetch("DELETE", `${rgPath()}/providers/Microsoft.Network/publicIPAddresses/${name}`).catch(() => {});
}
// ============================================================================
// Network Security Group
// ============================================================================
// ============================================================================
// Network Interface
// ============================================================================
async function createNic(name, publicIpName, subnetId, nsgId) {
const { location } = config();
console.log(`[azure] Creating NIC: ${name}`);
const publicIpId = `${rgPath()}/providers/Microsoft.Network/publicIPAddresses/${publicIpName}`;
await azureFetch("PUT", `${rgPath()}/providers/Microsoft.Network/networkInterfaces/${name}`, {
location,
properties: {
ipConfigurations: [
{
name: "ipconfig1",
properties: {
privateIPAllocationMethod: "Dynamic",
publicIPAddress: { id: publicIpId, properties: { deleteOption: "Delete" } },
subnet: { id: subnetId },
},
},
],
...(nsgId ? { networkSecurityGroup: { id: nsgId } } : {}),
},
});
return `${rgPath()}/providers/Microsoft.Network/networkInterfaces/${name}`;
}
async function deleteNic(name) {
await azureFetch("DELETE", `${rgPath()}/providers/Microsoft.Network/networkInterfaces/${name}`).catch(() => {});
}
// ============================================================================
// Virtual Machines
// ============================================================================
/**
* @param {object} opts
* @param {string} opts.name
* @param {string} opts.vmSize
* @param {object} opts.imageReference
* @param {number} opts.osDiskSizeGB
* @param {string} opts.nicId
* @param {string} opts.adminUsername
* @param {string} opts.adminPassword
* @param {Record<string, string>} [opts.tags]
*/
async function createVm(opts) {
const { location } = config();
console.log(`[azure] Creating VM: ${opts.name} (${opts.vmSize})`);
const result = await azureFetch("PUT", `${rgPath()}/providers/Microsoft.Compute/virtualMachines/${opts.name}`, {
location,
tags: opts.tags,
properties: {
hardwareProfile: { vmSize: opts.vmSize },
storageProfile: {
imageReference: opts.imageReference,
osDisk: {
createOption: "FromImage",
diskSizeGB: opts.osDiskSizeGB,
deleteOption: "Delete",
managedDisk: { storageAccountType: "Premium_LRS" },
},
},
osProfile: {
computerName: opts.name.substring(0, 15),
adminUsername: opts.adminUsername,
adminPassword: opts.adminPassword,
},
securityProfile: {
securityType: "TrustedLaunch",
},
networkProfile: {
networkInterfaces: [{ id: opts.nicId, properties: { deleteOption: "Delete" } }],
},
},
});
return result;
}
async function getVm(name) {
try {
return await azureFetch(
"GET",
`${rgPath()}/providers/Microsoft.Compute/virtualMachines/${name}?$expand=instanceView`,
);
} catch {
return null;
}
}
async function getVmPowerState(name) {
const vm = await getVm(name);
const statuses = vm?.properties?.instanceView?.statuses ?? [];
const powerStatus = statuses.find(s => s.code?.startsWith("PowerState/"));
return powerStatus?.code;
}
async function stopVm(name) {
console.log(`[azure] Stopping VM: ${name}`);
await azureFetch("POST", `${rgPath()}/providers/Microsoft.Compute/virtualMachines/${name}/deallocate`);
}
async function generalizeVm(name) {
console.log(`[azure] Generalizing VM: ${name}`);
await azureFetch("POST", `${rgPath()}/providers/Microsoft.Compute/virtualMachines/${name}/generalize`);
}
async function deleteVm(name) {
console.log(`[azure] Deleting VM: ${name}`);
await azureFetch("DELETE", `${rgPath()}/providers/Microsoft.Compute/virtualMachines/${name}?forceDeletion=true`);
}
async function getPublicIpAddress(publicIpName) {
const result = await azureFetch("GET", `${rgPath()}/providers/Microsoft.Network/publicIPAddresses/${publicIpName}`);
return result?.properties?.ipAddress;
}
/**
* Run a PowerShell script on a Windows VM via Azure Run Command.
* This works even without SSH installed on the VM.
*/
async function runCommand(vmName, script) {
console.log(`[azure] Running command on VM: ${vmName}`);
return azureFetch("POST", `${rgPath()}/providers/Microsoft.Compute/virtualMachines/${vmName}/runCommand`, {
commandId: "RunPowerShellScript",
script: Array.isArray(script) ? script : [script],
});
}
/**
* Install OpenSSH server and configure authorized keys on a Windows VM.
*/
// SSH is not used — all remote operations go through Azure Run Command API.
// ============================================================================
// Virtual Network
// ============================================================================
// ============================================================================
// Compute Gallery
// ============================================================================
const GALLERY_API_VERSION = "2024-03-03";
async function ensureImageDefinition(name, os, arch) {
const { location, galleryName } = config();
const path = `${rgPath()}/providers/Microsoft.Compute/galleries/${galleryName}/images/${name}`;
try {
const def = await azureFetch("GET", path, undefined, GALLERY_API_VERSION);
if (def) return;
} catch {}
console.log(`[azure] Creating image definition: ${name}`);
await azureFetch(
"PUT",
path,
{
location,
properties: {
osType: os === "windows" ? "Windows" : "Linux",
osState: "Generalized",
hyperVGeneration: "V2",
architecture: arch === "aarch64" ? "Arm64" : "x64",
identifier: {
publisher: "bun",
offer: `${os}-${arch}-ci`,
sku: name,
},
features: [
{ name: "DiskControllerTypes", value: "SCSI, NVMe" },
{ name: "SecurityType", value: "TrustedLaunch" },
],
},
},
GALLERY_API_VERSION,
);
}
async function createImageVersion(imageDefName, version, vmId) {
const { location, galleryName } = config();
const path = `${rgPath()}/providers/Microsoft.Compute/galleries/${galleryName}/images/${imageDefName}/versions/${version}`;
console.log(`[azure] Creating image version: ${imageDefName}/${version}`);
const result = await azureFetch(
"PUT",
path,
{
location,
properties: {
storageProfile: {
source: { virtualMachineId: vmId },
},
},
},
GALLERY_API_VERSION,
);
return result;
}
// ============================================================================
// Base Images
// ============================================================================
function getBaseImageReference(os, arch) {
if (os === "windows") {
if (arch === "aarch64") {
return {
publisher: "MicrosoftWindowsDesktop",
offer: "windows11preview-arm64",
sku: "win11-24h2-pro",
version: "latest",
};
}
// Windows Server 2019 x64 — oldest supported version
return {
publisher: "MicrosoftWindowsServer",
offer: "WindowsServer",
sku: "2019-datacenter-gensecond",
version: "latest",
};
}
throw new Error(`[azure] Unsupported OS: ${os}`);
}
function getVmSize(arch) {
return arch === "aarch64" ? "Standard_D4ps_v6" : "Standard_D4ds_v6";
}
// ============================================================================
// Exports
// ============================================================================
export const azure = {
get name() {
return "azure";
},
config,
/**
* @param {import("./machine.mjs").MachineOptions} options
* @returns {Promise<import("./machine.mjs").Machine>}
*/
async createMachine(options) {
const { os, arch, tags, sshKeys } = options;
const vmName = `bun-${os}-${arch}-${Date.now()}`;
const publicIpName = `${vmName}-ip`;
const nicName = `${vmName}-nic`;
const vmSize = options.instanceType || getVmSize(arch);
const diskSizeGB = options.diskSizeGb || (os === "windows" ? 150 : 40);
// Generate a random password for the admin account
const adminPassword = `P@${crypto.randomUUID().replace(/-/g, "").substring(0, 20)}!`;
const subnetId = `${rgPath()}/providers/Microsoft.Network/virtualNetworks/bun-ci-vnet/subnets/default`;
const nsgId = `${rgPath()}/providers/Microsoft.Network/networkSecurityGroups/bun-ci-ssh-nsg`;
await createPublicIp(publicIpName);
const nicId = await createNic(nicName, publicIpName, subnetId, nsgId);
// Create VM
const imageReference = options.imageId ? { id: options.imageId } : getBaseImageReference(os, arch);
await createVm({
name: vmName,
vmSize,
imageReference,
osDiskSizeGB: diskSizeGB,
nicId,
adminUsername: "bunadmin",
adminPassword,
tags: tags
? Object.fromEntries(
Object.entries(tags)
.filter(([_, v]) => v != null)
.map(([k, v]) => [k, String(v)]),
)
: undefined,
});
// Wait for public IP to be assigned
let publicIp;
for (let i = 0; i < 30; i++) {
publicIp = await getPublicIpAddress(publicIpName);
if (publicIp) break;
await new Promise(r => setTimeout(r, 5000));
}
if (!publicIp) {
throw new Error(`[azure] Failed to get public IP for ${vmName}`);
}
console.log(`[azure] VM created: ${vmName} at ${publicIp}`);
// Use Azure Run Command for all remote operations instead of SSH.
// This avoids the sshd startup issues on Azure Windows VMs.
const spawnFn = async (command, opts) => {
const script = command.join(" ");
console.log(`[azure] Run: ${script}`);
// Note: Azure Run Command output is limited to the last 4096 bytes.
// Full output is not available — only the tail is returned.
// value[0] = stdout (ComponentStatus/StdOut), value[1] = stderr (ComponentStatus/StdErr)
const result = await runCommand(vmName, [script]);
const values = result?.value ?? [];
const stdout = values[0]?.message ?? "";
const stderr = values[1]?.message ?? "";
if (opts?.stdio === "inherit") {
if (stdout) process.stdout.write(stdout);
if (stderr) process.stderr.write(stderr);
}
// Only use displayStatus to detect errors — stderr often contains non-error
// output (rustup progress, cargo warnings, PowerShell Write-Warning, etc.)
const hasError = values.some(v => v?.displayStatus === "Provisioning failed");
const exitCode = hasError ? 1 : 0;
return { exitCode, stdout, stderr };
};
const spawnSafeFn = async (command, opts) => {
const result = await spawnFn(command, opts);
if (result.exitCode !== 0) {
const msg = result.stderr || result.stdout || "Unknown error";
throw new Error(`[azure] Command failed (exit ${result.exitCode}): ${command.join(" ")}\n${msg}`);
}
return result;
};
const upload = async (source, destination) => {
// Read the file locally and write it on the VM via Run Command
const { readFileSync } = await import("node:fs");
const content = readFileSync(source, "utf-8");
// Escape for PowerShell — use base64 to avoid escaping issues
const b64 = Buffer.from(content).toString("base64");
const script = [
`$bytes = [Convert]::FromBase64String('${b64}')`,
`$dir = Split-Path '${destination}' -Parent`,
`if (-not (Test-Path $dir)) { New-Item -Path $dir -ItemType Directory -Force | Out-Null }`,
`[IO.File]::WriteAllBytes('${destination}', $bytes)`,
`Write-Host "Uploaded to ${destination} ($($bytes.Length) bytes)"`,
];
console.log(`[azure] Uploading ${source} -> ${destination}`);
await runCommand(vmName, script);
};
const attach = async () => {
console.log(`[azure] Attach not supported via Run Command (VM: ${vmName}, IP: ${publicIp})`);
};
const waitForSsh = async () => {
// No SSH needed — Run Command works immediately after VM is provisioned
// Just verify the VM is responsive
console.log(`[azure] Verifying VM is responsive...`);
await runCommand(vmName, ["Write-Host 'VM is ready'"]);
console.log(`[azure] VM is responsive`);
};
const snapshot = async label => {
const vmId = `${rgPath()}/providers/Microsoft.Compute/virtualMachines/${vmName}`;
// Run sysprep inside the VM before deallocating.
// This prepares Windows for generalization so the gallery image
// can be used to create new VMs with OS provisioning.
console.log(`[azure] Running sysprep on ${vmName}...`);
await runCommand(vmName, ["C:\\Windows\\System32\\Sysprep\\sysprep.exe /generalize /oobe /shutdown /quiet"]);
// Wait for VM to shut down after sysprep (sysprep triggers shutdown)
for (let i = 0; i < 60; i++) {
const state = await getVmPowerState(vmName);
if (state === "PowerState/stopped" || state === "PowerState/deallocated") break;
await new Promise(r => setTimeout(r, 10000));
}
// Deallocate the VM
await stopVm(vmName);
// Wait for VM to be deallocated
for (let i = 0; i < 60; i++) {
const state = await getVmPowerState(vmName);
if (state === "PowerState/deallocated") break;
await new Promise(r => setTimeout(r, 5000));
}
await generalizeVm(vmName);
// Ensure gallery and image definition exist.
// Use the label as the image definition name — this matches what ci.mjs
// emits as the image-name agent tag, so robobun can look it up directly.
const imageDefName = label;
await ensureImageDefinition(imageDefName, os, arch);
// Create a single version "1.0.0" under this definition.
await createImageVersion(imageDefName, "1.0.0", vmId);
// Wait for image replication to complete before returning.
// Single-region replication typically takes 5-15 minutes.
const { galleryName } = config();
const versionPath = `${rgPath()}/providers/Microsoft.Compute/galleries/${galleryName}/images/${imageDefName}/versions/1.0.0`;
console.log(`[azure] Waiting for image replication...`);
for (let i = 0; i < 120; i++) {
const ver = await azureFetch("GET", versionPath, undefined, GALLERY_API_VERSION);
const state = ver?.properties?.provisioningState;
if (state === "Succeeded") {
console.log(`[azure] Image ready: ${imageDefName}/1.0.0`);
break;
}
if (state === "Failed") {
throw new Error(`[azure] Image replication failed: ${JSON.stringify(ver?.properties)}`);
}
if (i % 6 === 0) {
console.log(`[azure] Image replicating... (${i}m elapsed)`);
}
await new Promise(r => setTimeout(r, 10_000));
}
return label;
};
const terminate = async () => {
await deleteVm(vmName);
// Resources with deleteOption=Delete are cleaned up automatically
// But clean up anything that might be left
await deleteNic(nicName);
await deletePublicIp(publicIpName);
};
return {
cloud: "azure",
id: vmName,
imageId: options.imageId,
instanceType: vmSize,
region: config().location,
get publicIp() {
return publicIp;
},
spawn: spawnFn,
spawnSafe: spawnSafeFn,
upload,
attach,
snapshot,
waitForSsh,
close: terminate,
[Symbol.asyncDispose]: terminate,
};
},
};

View File

@@ -1,6 +1,7 @@
# Version: 12
# A script that installs the dependencies needed to build and test Bun.
# This should work on Windows 10 or newer with PowerShell.
# Version: 14
# A script that installs the dependencies needed to build and test Bun on Windows.
# Supports both x64 and ARM64 using Scoop for package management.
# Used by Azure [build images] pipeline.
# If this script does not work on your machine, please open an issue:
# https://github.com/oven-sh/bun/issues
@@ -11,7 +12,7 @@
param (
[Parameter(Mandatory = $false)]
[switch]$CI = $false,
[switch]$CI = ($env:CI -eq "true"),
[Parameter(Mandatory = $false)]
[switch]$Optimize = $CI
)
@@ -19,17 +20,26 @@ param (
$ErrorActionPreference = "Stop"
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass -Force
function Execute-Command {
$command = $args -join ' '
Write-Output "$ $command"
# Detect ARM64 from registry (works even under x64 emulation)
$realArch = (Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment').PROCESSOR_ARCHITECTURE
$script:IsARM64 = $realArch -eq "ARM64"
& $args[0] $args[1..$args.Length]
if ((-not $?) -or ($LASTEXITCODE -ne 0 -and $null -ne $LASTEXITCODE)) {
throw "Command failed: $command"
# If we're on ARM64 but running under x64 emulation, re-launch as native ARM64.
# Azure Run Command uses x64-emulated PowerShell which breaks package installs.
if ($script:IsARM64 -and $env:PROCESSOR_ARCHITECTURE -ne "ARM64") {
$nativePS = "$env:SystemRoot\Sysnative\WindowsPowerShell\v1.0\powershell.exe"
if (Test-Path $nativePS) {
Write-Output "Re-launching bootstrap as native ARM64 PowerShell..."
& $nativePS -NoProfile -ExecutionPolicy Bypass -File $MyInvocation.MyCommand.Path @PSBoundParameters
exit $LASTEXITCODE
}
}
# ============================================================================
# Utility functions
# ============================================================================
function Which {
param ([switch]$Required = $false)
@@ -46,16 +56,6 @@ function Which {
}
}
function Execute-Script {
param (
[Parameter(Mandatory = $true, Position = 0)]
[string]$Path
)
$pwsh = Which pwsh powershell -Required
Execute-Command $pwsh $Path
}
function Download-File {
param (
[Parameter(Mandatory = $true, Position = 0)]
@@ -87,18 +87,6 @@ function Download-File {
return $Path
}
function Install-Chocolatey {
if (Which choco) {
return
}
Write-Output "Installing Chocolatey..."
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072
$installScript = Download-File "https://community.chocolatey.org/install.ps1"
Execute-Script $installScript
Refresh-Path
}
function Refresh-Path {
$paths = @(
[System.Environment]::GetEnvironmentVariable("Path", "Machine"),
@@ -111,15 +99,19 @@ function Refresh-Path {
Where-Object { $_ -and (Test-Path $_) } |
Select-Object -Unique
$env:Path = ($uniquePaths -join ';').TrimEnd(';')
if ($env:ChocolateyInstall) {
Import-Module $env:ChocolateyInstall\helpers\chocolateyProfile.psm1 -ErrorAction SilentlyContinue
}
}
function Add-To-Path {
$absolutePath = Resolve-Path $args[0]
$currentPath = [Environment]::GetEnvironmentVariable("Path", "Machine")
param (
[Parameter(Mandatory = $true, Position = 0)]
[string]$PathToAdd,
[Parameter(Mandatory = $false)]
[ValidateSet("Machine", "User")]
[string]$Scope = "Machine"
)
$absolutePath = Resolve-Path $PathToAdd
$currentPath = [Environment]::GetEnvironmentVariable("Path", $Scope)
if ($currentPath -like "*$absolutePath*") {
return
}
@@ -140,8 +132,8 @@ function Add-To-Path {
}
}
Write-Output "Adding $absolutePath to PATH..."
[Environment]::SetEnvironmentVariable("Path", "$newPath", "Machine")
Write-Output "Adding $absolutePath to PATH ($Scope)..."
[Environment]::SetEnvironmentVariable("Path", "$newPath", $Scope)
Refresh-Path
}
@@ -158,104 +150,374 @@ function Set-Env {
[System.Environment]::SetEnvironmentVariable("$Name", "$Value", "Process")
}
function Install-Package {
# ============================================================================
# Scoop — ARM64-native package manager
# ============================================================================
function Install-Scoop {
if (Which scoop) {
return
}
Write-Output "Installing Scoop..."
# Scoop blocks admin installs unless -RunAsAdmin is passed.
# Install to a known global location so all users can access it.
$env:SCOOP = "C:\Scoop"
[Environment]::SetEnvironmentVariable("SCOOP", $env:SCOOP, "Machine")
iex "& {$(irm get.scoop.sh)} -RunAsAdmin -ScoopDir C:\Scoop"
Add-To-Path "C:\Scoop\shims"
Refresh-Path
Write-Output "Scoop version: $(scoop --version)"
}
function Install-Scoop-Package {
param (
[Parameter(Mandatory = $true, Position = 0)]
[string]$Name,
[Parameter(Mandatory = $false)]
[string]$Command = $Name,
[Parameter(Mandatory = $false)]
[string]$Version,
[Parameter(Mandatory = $false)]
[switch]$Force = $false,
[Parameter(Mandatory = $false)]
[string[]]$ExtraArgs = @()
[string]$Command = $Name
)
if (-not $Force `
-and (Which $Command) `
-and (-not $Version -or (& $Command --version) -like "*$Version*")) {
if (Which $Command) {
return
}
Write-Output "Installing $Name..."
$flags = @(
"--yes",
"--accept-license",
"--no-progress",
"--force"
)
if ($Version) {
$flags += "--version=$Version"
}
Execute-Command choco install $Name @flags @ExtraArgs
Write-Output "Installing $Name (via Scoop)..."
# Scoop post_install scripts can have non-fatal Remove-Item errors
# (e.g. 7zip ARM64 7zr.exe locked, llvm-arm64 missing Uninstall.exe).
# Suppress all error streams so they don't kill the bootstrap or Packer.
$prevErrorPref = $ErrorActionPreference
$ErrorActionPreference = "SilentlyContinue"
scoop install $Name *>&1 | ForEach-Object { "$_" } | Write-Host
$ErrorActionPreference = $prevErrorPref
Refresh-Path
}
function Install-Packages {
foreach ($package in $args) {
Install-Package $package
}
}
function Install-Common-Software {
Install-Chocolatey
Install-Pwsh
Install-Git
Install-Packages curl 7zip nssm
Install-NodeJs
Install-Bun
Install-Cygwin
if ($CI) {
# FIXME: Installing tailscale causes the AWS metadata server to become unreachable
# Install-Tailscale
Install-Buildkite
}
}
function Install-Pwsh {
Install-Package powershell-core -Command pwsh
if ($CI) {
$shellPath = (Which pwsh -Required)
New-ItemProperty `
-Path "HKLM:\\SOFTWARE\\OpenSSH" `
-Name DefaultShell `
-Value $shellPath `
-PropertyType String `
-Force
}
}
# ============================================================================
# Scoop packages (native ARM64 binaries)
# ============================================================================
function Install-Git {
Install-Packages git
Install-Scoop-Package git
# Git for Windows ships Unix tools (cat, head, tail, etc.) in usr\bin
$gitUsrBin = "C:\Scoop\apps\git\current\usr\bin"
if (Test-Path $gitUsrBin) {
Add-To-Path $gitUsrBin
}
if ($CI) {
Execute-Command git config --system --add safe.directory "*"
Execute-Command git config --system core.autocrlf false
Execute-Command git config --system core.eol lf
Execute-Command git config --system core.longpaths true
git config --system --add safe.directory "*"
git config --system core.autocrlf false
git config --system core.eol lf
git config --system core.longpaths true
}
}
function Install-NodeJs {
Install-Package nodejs -Command node -Version "24.3.0"
# Pin to match the ABI version Bun expects (NODE_MODULE_VERSION 137).
# Latest Node (25.x) uses ABI 141 which breaks node-gyp tests.
Install-Scoop-Package "nodejs@24.3.0" -Command node
}
function Install-Bun {
Install-Package bun -Version "1.3.1"
function Install-CMake {
Install-Scoop-Package cmake
}
function Install-Llvm {
$LLVM_VERSION = "21.1.8"
if (Which clang-cl) {
return
}
if ($script:IsARM64) {
Install-Scoop-Package "llvm-arm64@$LLVM_VERSION" -Command clang-cl
} else {
Install-Scoop-Package "llvm@$LLVM_VERSION" -Command clang-cl
}
}
function Install-Ninja {
Install-Scoop-Package ninja
}
function Install-Python {
Install-Scoop-Package python
}
function Install-Go {
Install-Scoop-Package go
}
function Install-Ruby {
Install-Scoop-Package ruby
}
function Install-7zip {
Install-Scoop-Package 7zip -Command 7z
}
function Install-Make {
Install-Scoop-Package make
}
function Install-Cygwin {
Install-Package cygwin
Add-To-Path "C:\tools\cygwin\bin"
# Cygwin's default mirror (mirrors.kernel.org) can be unreachable from Azure.
# Make this non-fatal — the build will fail later if cygwin is actually needed.
try {
Install-Scoop-Package cygwin
# Cygwin binaries are at <scoop>/apps/cygwin/current/root/bin
$cygwinBin = "C:\Scoop\apps\cygwin\current\root\bin"
if (Test-Path $cygwinBin) {
Add-To-Path $cygwinBin # Machine scope (default) — survives Sysprep
}
} catch {
Write-Warning "Cygwin installation failed (non-fatal): $_"
}
}
function Install-Tailscale {
Install-Package tailscale
# ============================================================================
# Manual installs (not available or not ideal via Scoop)
# ============================================================================
function Install-Pwsh {
if (Which pwsh) {
return
}
$pwshArch = if ($script:IsARM64) { "arm64" } else { "x64" }
Write-Output "Installing PowerShell Core ($pwshArch)..."
$msi = Download-File "https://github.com/PowerShell/PowerShell/releases/download/v7.5.2/PowerShell-7.5.2-win-$pwshArch.msi" -Name "pwsh-$pwshArch.msi"
$process = Start-Process msiexec -ArgumentList "/i `"$msi`" /quiet /norestart ADD_PATH=1" -Wait -PassThru -NoNewWindow
if ($process.ExitCode -ne 0) {
throw "Failed to install PowerShell: code $($process.ExitCode)"
}
Remove-Item $msi -ErrorAction SilentlyContinue
Refresh-Path
}
function Install-OpenSSH {
$sshdService = Get-Service -Name sshd -ErrorAction SilentlyContinue
if ($sshdService) {
return
}
Write-Output "Installing OpenSSH Server..."
# Add-WindowsCapability requires DISM elevation which isn't available in Packer's
# WinRM session. Download and install from GitHub releases instead.
$arch = if ([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture -eq "Arm64") { "Arm64" } else { "Win64" }
$url = "https://github.com/PowerShell/Win32-OpenSSH/releases/download/v9.8.1.0p1-Preview/OpenSSH-${arch}.zip"
$zip = "$env:TEMP\OpenSSH.zip"
$dest = "$env:ProgramFiles\OpenSSH"
Invoke-WebRequest -Uri $url -OutFile $zip -UseBasicParsing
Expand-Archive -Path $zip -DestinationPath "$env:TEMP\OpenSSH" -Force
New-Item -Path $dest -ItemType Directory -Force | Out-Null
$extractedDir = Get-ChildItem -Path "$env:TEMP\OpenSSH" -Directory | Select-Object -First 1
Get-ChildItem -Path $extractedDir.FullName -Recurse | Move-Item -Destination $dest -Force
& "$dest\install-sshd.ps1"
& "$dest\FixHostFilePermissions.ps1" -Confirm:$false
Remove-Item $zip, "$env:TEMP\OpenSSH" -Recurse -Force -ErrorAction SilentlyContinue
# Configure sshd to start on boot (don't start now — host keys may not exist yet during image build)
Set-Service -Name sshd -StartupType Automatic
# Set default shell to pwsh
$pwshPath = (Which pwsh -ErrorAction SilentlyContinue)
if (-not $pwshPath) { $pwshPath = (Which powershell) }
if ($pwshPath) {
New-ItemProperty -Path "HKLM:\SOFTWARE\OpenSSH" -Name DefaultShell `
-Value $pwshPath -PropertyType String -Force
}
# Firewall rule for port 22
$rule = Get-NetFirewallRule -Name "OpenSSH-Server" -ErrorAction SilentlyContinue
if (-not $rule) {
New-NetFirewallRule -Profile Any -Name "OpenSSH-Server" `
-DisplayName "OpenSSH Server (sshd)" -Enabled True `
-Direction Inbound -Protocol TCP -Action Allow -LocalPort 22
}
# Configure sshd_config for key-based auth
$sshdConfigPath = "C:\ProgramData\ssh\sshd_config"
if (Test-Path $sshdConfigPath) {
$config = Get-Content $sshdConfigPath
$config = $config -replace '#PubkeyAuthentication yes', 'PubkeyAuthentication yes'
$config = $config -replace 'PasswordAuthentication yes', 'PasswordAuthentication no'
Set-Content -Path $sshdConfigPath -Value $config
}
Write-Output "OpenSSH Server installed and configured"
# Register a startup task that fetches oven-sh GitHub org members' SSH keys
# on every boot so any bun dev can SSH in.
$fetchScript = @'
try {
$members = Invoke-RestMethod -Uri "https://api.github.com/orgs/oven-sh/members" -Headers @{ "User-Agent" = "bun-ci" }
$keys = @()
foreach ($member in $members) {
if ($member.type -ne "User" -or -not $member.login) { continue }
try {
$userKeys = (Invoke-WebRequest -Uri "https://github.com/$($member.login).keys" -UseBasicParsing).Content
if ($userKeys) { $keys += $userKeys.Trim() }
} catch { }
}
if ($keys.Count -gt 0) {
$keysPath = "C:\ProgramData\ssh\administrators_authorized_keys"
Set-Content -Path $keysPath -Value ($keys -join "`n") -Force
icacls $keysPath /inheritance:r /grant "SYSTEM:(F)" /grant "Administrators:(R)" | Out-Null
}
} catch { }
'@
$scriptPath = "C:\ProgramData\ssh\fetch-ssh-keys.ps1"
Set-Content -Path $scriptPath -Value $fetchScript -Force
$action = New-ScheduledTaskAction -Execute "pwsh.exe" -Argument "-NoProfile -ExecutionPolicy Bypass -File `"$scriptPath`""
$trigger = New-ScheduledTaskTrigger -AtStartup
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries
Register-ScheduledTask -TaskName "FetchSshKeys" -Action $action -Trigger $trigger `
-Settings $settings -User "SYSTEM" -RunLevel Highest -Force
Write-Output "Registered FetchSshKeys startup task"
}
function Install-Ccache {
if (Which ccache) {
return
}
$version = "4.12.2"
$archSuffix = if ($script:IsARM64) { "aarch64" } else { "x86_64" }
Write-Output "Installing ccache $version ($archSuffix)..."
$zip = Download-File "https://github.com/ccache/ccache/releases/download/v$version/ccache-$version-windows-$archSuffix.zip" -Name "ccache-$archSuffix.zip"
$extractDir = "$env:TEMP\ccache-extract"
Expand-Archive $zip $extractDir -Force
$installDir = "$env:ProgramFiles\ccache"
New-Item -Path $installDir -ItemType Directory -Force | Out-Null
Copy-Item "$extractDir\ccache-$version-windows-$archSuffix\*" $installDir -Recurse -Force
Remove-Item $zip -ErrorAction SilentlyContinue
Remove-Item $extractDir -Recurse -ErrorAction SilentlyContinue
Add-To-Path $installDir
}
function Install-Bun {
if (Which bun) {
return
}
if ($script:IsARM64) {
# No published ARM64 bun binary yet — download from our blob storage
Write-Output "Installing Bun (ARM64 from blob storage)..."
$zip = Download-File "https://buncistore.blob.core.windows.net/artifacts/bun-windows-aarch64.zip" -Name "bun-arm64.zip"
$extractDir = "$env:TEMP\bun-arm64"
Expand-Archive -Path $zip -DestinationPath $extractDir -Force
$bunExe = Get-ChildItem $extractDir -Recurse -Filter "*.exe" | Where-Object { $_.Name -match "bun" } | Select-Object -First 1
if ($bunExe) {
Copy-Item $bunExe.FullName "C:\Windows\System32\bun.exe" -Force
Write-Output "Bun ARM64 installed to C:\Windows\System32\bun.exe"
} else {
throw "Failed to find bun executable in ARM64 zip"
}
} else {
Write-Output "Installing Bun..."
$installScript = Download-File "https://bun.sh/install.ps1" -Name "bun-install.ps1"
$pwsh = Which pwsh powershell -Required
& $pwsh $installScript
Refresh-Path
# Copy to System32 so it survives Sysprep (user profile PATH is lost)
$bunPath = Which bun
if ($bunPath) {
Copy-Item $bunPath "C:\Windows\System32\bun.exe" -Force
Write-Output "Bun copied to C:\Windows\System32\bun.exe"
}
}
}
function Install-Rust {
if (Which rustc) {
return
}
$rustPath = Join-Path $env:ProgramFiles "Rust"
if (-not (Test-Path $rustPath)) {
New-Item -Path $rustPath -ItemType Directory | Out-Null
}
# Set install paths before running rustup so it installs directly
# to Program Files (avoids issues with SYSTEM user profile path)
$env:CARGO_HOME = "$rustPath\cargo"
$env:RUSTUP_HOME = "$rustPath\rustup"
Write-Output "Installing Rustup..."
$rustupInit = Download-File "https://win.rustup.rs/" -Name "rustup-init.exe"
Write-Output "Installing Rust..."
& $rustupInit -y
Write-Output "Setting environment variables for Rust..."
Set-Env "CARGO_HOME" "$rustPath\cargo"
Set-Env "RUSTUP_HOME" "$rustPath\rustup"
Add-To-Path "$rustPath\cargo\bin"
}
function Install-Visual-Studio {
param (
[Parameter(Mandatory = $false)]
[string]$Edition = "community"
)
Write-Output "Downloading Visual Studio installer..."
$vsInstaller = Download-File "https://aka.ms/vs/17/release/vs_$Edition.exe"
Write-Output "Installing Visual Studio..."
$vsInstallArgs = @(
"--passive",
"--norestart",
"--wait",
"--force",
"--locale en-US",
"--add Microsoft.VisualStudio.Workload.NativeDesktop",
"--includeRecommended"
)
$process = Start-Process $vsInstaller -ArgumentList ($vsInstallArgs -join ' ') -Wait -PassThru -NoNewWindow
# Exit code 3010 means "reboot required" which is not a real error
if ($process.ExitCode -ne 0 -and $process.ExitCode -ne 3010) {
throw "Failed to install Visual Studio: code $($process.ExitCode)"
}
}
function Install-PdbAddr2line {
cargo install --examples "pdb-addr2line@0.11.2"
# Also copy to System32 so it's always on PATH (like bun.exe)
$src = Join-Path $env:CARGO_HOME "bin\pdb-addr2line.exe"
if (Test-Path $src) {
Copy-Item $src "C:\Windows\System32\pdb-addr2line.exe" -Force
Write-Output "pdb-addr2line copied to C:\Windows\System32"
}
}
function Install-Nssm {
if (Which nssm) {
return
}
# Try Scoop first, fall back to our mirror if nssm.cc is down (503 errors)
Install-Scoop-Package nssm
if (-not (Which nssm)) {
Write-Output "Scoop install of nssm failed, downloading from mirror..."
$zip = Download-File "https://buncistore.blob.core.windows.net/artifacts/nssm-2.24-103-gdee49fc.zip" -Name "nssm.zip"
Expand-Archive -Path $zip -DestinationPath "C:\Windows\Temp\nssm" -Force
$nssm = Get-ChildItem "C:\Windows\Temp\nssm" -Recurse -Filter "nssm.exe" | Where-Object { $_.DirectoryName -like "*win64*" } | Select-Object -First 1
if ($nssm) {
Copy-Item $nssm.FullName "C:\Windows\System32\nssm.exe" -Force
Write-Output "nssm installed to C:\Windows\System32\nssm.exe"
} else {
throw "Failed to install nssm"
}
}
}
# ============================================================================
# Buildkite
# ============================================================================
function Create-Buildkite-Environment-Hooks {
param (
[Parameter(Mandatory = $true)]
@@ -278,6 +540,17 @@ function Create-Buildkite-Environment-Hooks {
"@ | Set-Content -Path $environmentHook -Encoding UTF8
Write-Output "Environment hook created at $environmentHook"
# pre-exit hook: logout from Tailscale so ephemeral nodes are removed
# instantly instead of waiting 30-60 minutes. This runs after the job
# finishes, which is after the SSH user wait loop in runner.node.mjs.
$preExitHook = Join-Path $hooksDir "pre-exit.ps1"
@"
if (Test-Path "C:\Program Files\Tailscale\tailscale.exe") {
& "C:\Program Files\Tailscale\tailscale.exe" logout 2>`$null
}
"@ | Set-Content -Path $preExitHook -Encoding UTF8
Write-Output "Pre-exit hook created at $preExitHook"
}
function Install-Buildkite {
@@ -288,7 +561,8 @@ function Install-Buildkite {
Write-Output "Installing Buildkite agent..."
$env:buildkiteAgentToken = "xxx"
$installScript = Download-File "https://raw.githubusercontent.com/buildkite/agent/main/install.ps1"
Execute-Script $installScript
$pwsh = Which pwsh powershell -Required
& $pwsh $installScript
Refresh-Path
if ($CI) {
@@ -300,96 +574,9 @@ function Install-Buildkite {
}
}
function Install-Build-Essentials {
Install-Visual-Studio
Install-Packages `
cmake `
make `
ninja `
python `
golang `
nasm `
ruby `
strawberryperl `
mingw
Install-Rust
Install-Ccache
# Needed to remap stack traces
Install-PdbAddr2line
Install-Llvm
}
function Install-Visual-Studio {
param (
[Parameter(Mandatory = $false)]
[string]$Edition = "community"
)
Write-Output "Downloading Visual Studio installer..."
$vsInstaller = Download-File "https://aka.ms/vs/17/release/vs_$Edition.exe"
Write-Output "Installing Visual Studio..."
$vsInstallArgs = @(
"--passive",
"--norestart",
"--wait",
"--force",
"--locale en-US",
"--add Microsoft.VisualStudio.Workload.NativeDesktop",
"--includeRecommended"
)
$startInfo = New-Object System.Diagnostics.ProcessStartInfo
$startInfo.FileName = $vsInstaller
$startInfo.Arguments = $vsInstallArgs -join ' '
$startInfo.CreateNoWindow = $true
$process = New-Object System.Diagnostics.Process
$process.StartInfo = $startInfo
$process.Start()
$process.WaitForExit()
if ($process.ExitCode -ne 0) {
throw "Failed to install Visual Studio: code $($process.ExitCode)"
}
}
function Install-Rust {
if (Which rustc) {
return
}
Write-Output "Installing Rustup..."
$rustupInit = Download-File "https://win.rustup.rs/" -Name "rustup-init.exe"
Write-Output "Installing Rust..."
Execute-Command $rustupInit -y
Write-Output "Moving Rust to $env:ProgramFiles..."
$rustPath = Join-Path $env:ProgramFiles "Rust"
if (-not (Test-Path $rustPath)) {
New-Item -Path $rustPath -ItemType Directory
}
Move-Item "$env:UserProfile\.cargo" "$rustPath\cargo" -Force
Move-Item "$env:UserProfile\.rustup" "$rustPath\rustup" -Force
Write-Output "Setting environment variables for Rust..."
Set-Env "CARGO_HOME" "$rustPath\cargo"
Set-Env "RUSTUP_HOME" "$rustPath\rustup"
Add-To-Path "$rustPath\cargo\bin"
}
function Install-Ccache {
Install-Package ccache
}
function Install-PdbAddr2line {
Execute-Command cargo install --examples "pdb-addr2line@0.11.2"
}
function Install-Llvm {
Install-Package llvm `
-Command clang-cl `
-Version "21.1.8"
Add-To-Path "$env:ProgramFiles\LLVM\bin"
}
# ============================================================================
# System optimization
# ============================================================================
function Optimize-System {
Disable-Windows-Defender
@@ -417,8 +604,11 @@ function Disable-Windows-Threat-Protection {
}
function Uninstall-Windows-Defender {
Write-Output "Uninstalling Windows Defender..."
Uninstall-WindowsFeature -Name Windows-Defender
# Requires a reboot — run before the windows-restart Packer provisioner.
if (Get-Command Uninstall-WindowsFeature -ErrorAction SilentlyContinue) {
Write-Output "Uninstalling Windows Defender..."
Uninstall-WindowsFeature -Name Windows-Defender
}
}
function Disable-Windows-Services {
@@ -432,8 +622,12 @@ function Disable-Windows-Services {
)
foreach ($service in $services) {
Stop-Service $service -Force
Set-Service $service -StartupType Disabled
try {
Stop-Service $service -Force -ErrorAction SilentlyContinue
Set-Service $service -StartupType Disabled -ErrorAction SilentlyContinue
} catch {
Write-Warning "Could not disable service: $service"
}
}
}
@@ -448,13 +642,98 @@ function Disable-Power-Management {
powercfg /change hibernate-timeout-dc 0
}
# ============================================================================
# Main
# ============================================================================
if ($Optimize) {
Optimize-System
}
Install-Common-Software
Install-Build-Essentials
# Scoop package manager
Install-Scoop
# Packages via Scoop (native ARM64 or x64 depending on architecture)
# 7zip must be installed before git — git depends on 7zip via Scoop,
# and 7zip's post_install has a cleanup error on ARM64 SYSTEM context.
Install-7zip
Install-Git
Install-NodeJs
Install-CMake
Install-Ninja
Install-Python
Install-Go
Install-Ruby
Install-Make
Install-Llvm
Install-Cygwin
Install-Nssm
Install-Scoop-Package perl
# x64-only packages (not needed on ARM64)
if (-not $script:IsARM64) {
Install-Scoop-Package nasm
Install-Scoop-Package mingw -Command gcc
}
function Install-Tailscale {
if (Which tailscale -ErrorAction SilentlyContinue) {
return
}
Write-Output "Installing Tailscale..."
$msi = "$env:TEMP\tailscale-setup.msi"
$arch = if ([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture -eq "Arm64") { "arm64" } else { "amd64" }
Invoke-WebRequest -Uri "https://pkgs.tailscale.com/stable/tailscale-setup-latest-${arch}.msi" -OutFile $msi -UseBasicParsing
Start-Process msiexec.exe -ArgumentList "/i `"$msi`" /quiet /norestart" -Wait
Remove-Item $msi -ErrorAction SilentlyContinue
Refresh-Path
Write-Output "Tailscale installed"
# Register a startup task that reads the tailscale authkey from Azure IMDS
# tags and joins the tailnet. The key is set by robobun as a VM tag.
$joinScript = @'
try {
$headers = @{ "Metadata" = "true" }
$response = Invoke-RestMethod -Uri "http://169.254.169.254/metadata/instance/compute/tagsList?api-version=2021-02-01" -Headers $headers
$authkey = ($response | Where-Object { $_.name -eq "tailscale:authkey" }).value
if ($authkey) {
$stepKey = ($response | Where-Object { $_.name -eq "buildkite:step-key" }).value
$buildNumber = ($response | Where-Object { $_.name -eq "buildkite:build-number" }).value
if ($stepKey) {
$hostname = "azure-${stepKey}"
if ($buildNumber) { $hostname += "-${buildNumber}" }
} else {
$hostname = (Invoke-RestMethod -Uri "http://169.254.169.254/metadata/instance/compute/name?api-version=2021-02-01&format=text" -Headers $headers)
}
& "C:\Program Files\Tailscale\tailscale.exe" up --authkey=$authkey --hostname=$hostname --unattended
}
} catch { }
'@
$scriptPath = "C:\ProgramData\tailscale-join.ps1"
Set-Content -Path $scriptPath -Value $joinScript -Force
$action = New-ScheduledTaskAction -Execute "pwsh.exe" -Argument "-NoProfile -ExecutionPolicy Bypass -File `"$scriptPath`""
$trigger = New-ScheduledTaskTrigger -AtStartup
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries
Register-ScheduledTask -TaskName "TailscaleJoin" -Action $action -Trigger $trigger `
-Settings $settings -User "SYSTEM" -RunLevel Highest -Force
Write-Output "Registered TailscaleJoin startup task"
}
# Manual installs (not in Scoop or need special handling)
Install-Pwsh
Install-OpenSSH
#Install-Tailscale # Disabled — Tailscale adapter interferes with IPv6 multicast tests (node-dgram)
Install-Bun
Install-Ccache
Install-Rust
Install-Visual-Studio
Install-PdbAddr2line
if ($CI) {
Install-Buildkite
}
if ($Optimize) {
Optimize-System-Needs-Reboot
}
}

View File

@@ -14,14 +14,7 @@ import {
startGroup,
} from "./utils.mjs";
// Detect Windows ARM64 - bun may run under x64 emulation (WoW64), so check multiple indicators
const isWindowsARM64 =
isWindows &&
(process.env.PROCESSOR_ARCHITECTURE === "ARM64" ||
process.env.VSCMD_ARG_HOST_ARCH === "arm64" ||
process.env.MSYSTEM_CARCH === "aarch64" ||
(process.env.PROCESSOR_IDENTIFIER || "").includes("ARMv8") ||
process.arch === "arm64");
const isWindowsARM64 = isWindows && process.arch === "arm64";
if (globalThis.Bun) {
await import("./glob-sources.mjs");
@@ -57,11 +50,7 @@ async function build(args) {
if (process.platform === "win32" && !process.env["VSINSTALLDIR"]) {
const shellPath = join(import.meta.dirname, "vs-shell.ps1");
const scriptPath = import.meta.filename;
// When cross-compiling to ARM64, tell vs-shell.ps1 to set up the x64_arm64 VS environment
const toolchainIdx = args.indexOf("--toolchain");
const requestedVsArch = toolchainIdx !== -1 && args[toolchainIdx + 1] === "windows-aarch64" ? "arm64" : undefined;
const env = requestedVsArch ? { ...process.env, BUN_VS_ARCH: requestedVsArch } : undefined;
return spawn("pwsh", ["-NoProfile", "-NoLogo", "-File", shellPath, process.argv0, scriptPath, ...args], { env });
return spawn("pwsh", ["-NoProfile", "-NoLogo", "-File", shellPath, process.argv0, scriptPath, ...args]);
}
if (isCI) {

View File

@@ -1,9 +1,11 @@
#!/usr/bin/env node
import { existsSync, mkdtempSync, readdirSync } from "node:fs";
import { chmodSync, existsSync, mkdtempSync, readdirSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { basename, extname, join, relative, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { inspect, parseArgs } from "node:util";
import { azure } from "./azure.mjs";
import { docker } from "./docker.mjs";
import { tart } from "./tart.mjs";
import {
@@ -35,7 +37,6 @@ import {
spawnSshSafe,
spawnSyncSafe,
startGroup,
tmpdir,
waitForPort,
which,
writeFile,
@@ -1047,16 +1048,14 @@ function getRdpFile(hostname, username) {
* @property {(options: MachineOptions) => Promise<Machine>} createMachine
*/
/**
* @param {string} name
* @returns {Cloud}
*/
function getCloud(name) {
switch (name) {
case "docker":
return docker;
case "aws":
return aws;
case "azure":
return azure;
case "tart":
return tart;
}
@@ -1127,6 +1126,173 @@ function getCloud(name) {
* @property {SshKey[]} sshKeys
*/
async function getAzureToken(tenantId, clientId, clientSecret) {
const response = await fetch(`https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: `grant_type=client_credentials&client_id=${clientId}&client_secret=${encodeURIComponent(clientSecret)}&scope=https://management.azure.com/.default`,
});
if (!response.ok) throw new Error(`Azure auth failed: ${response.status}`);
const data = await response.json();
return data.access_token;
}
/**
* Build a Windows image using Packer (Azure only).
* Packer handles VM creation, bootstrap, sysprep, and gallery capture via WinRM.
* This eliminates all the Azure Run Command issues (output truncation, x64 emulation,
* PATH not refreshing, stderr false positives, quote escaping).
*/
async function buildWindowsImageWithPacker({ os, arch, release, command, ci, agentPath, bootstrapPath }) {
const { getSecret } = await import("./utils.mjs");
// Determine Packer template
const templateName = arch === "aarch64" ? "windows-arm64" : "windows-x64";
const templateDir = resolve(import.meta.dirname, "packer");
const templateFile = join(templateDir, `${templateName}.pkr.hcl`);
if (!existsSync(templateFile)) {
throw new Error(`Packer template not found: ${templateFile}`);
}
// Get Azure credentials from Buildkite secrets
const clientId = await getSecret("AZURE_CLIENT_ID");
const clientSecret = await getSecret("AZURE_CLIENT_SECRET");
const subscriptionId = await getSecret("AZURE_SUBSCRIPTION_ID");
const tenantId = await getSecret("AZURE_TENANT_ID");
const resourceGroup = await getSecret("AZURE_RESOURCE_GROUP");
const location = (await getSecret("AZURE_LOCATION")) || "eastus2";
const galleryName = (await getSecret("AZURE_GALLERY_NAME")) || "bunCIGallery2";
// Image naming must match getImageName() in ci.mjs:
// [publish images] / normal CI: "windows-x64-2019-v13"
// [build images]: "windows-x64-2019-build-37194"
const imageKey = arch === "aarch64" ? "windows-aarch64-11" : "windows-x64-2019";
const imageDefName =
command === "publish-image"
? `${imageKey}-v${getBootstrapVersion(os)}`
: ci
? `${imageKey}-build-${getBuildNumber()}`
: `${imageKey}-build-draft-${Date.now()}`;
const galleryArch = arch === "aarch64" ? "Arm64" : "x64";
console.log(`[packer] Ensuring gallery image definition: ${imageDefName}`);
const galleryPath = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroup}/providers/Microsoft.Compute/galleries/${galleryName}/images/${imageDefName}`;
const token = await getAzureToken(tenantId, clientId, clientSecret);
const defResponse = await fetch(`https://management.azure.com${galleryPath}?api-version=2024-03-03`, {
method: "PUT",
headers: { "Authorization": `Bearer ${token}`, "Content-Type": "application/json" },
body: JSON.stringify({
location: location,
properties: {
osType: "Windows",
osState: "Generalized",
hyperVGeneration: "V2",
architecture: galleryArch,
identifier: { publisher: "bun", offer: `${os}-${arch}-ci`, sku: imageDefName },
features: [
{ name: "DiskControllerTypes", value: "SCSI, NVMe" },
{ name: "SecurityType", value: "TrustedLaunch" },
],
},
}),
});
if (!defResponse.ok && defResponse.status !== 409) {
throw new Error(`Failed to create gallery image definition: ${defResponse.status} ${await defResponse.text()}`);
}
// Install Packer if not available
const packerBin = await ensurePacker();
// Initialize plugins
console.log("[packer] Initializing plugins...");
await spawnSafe([packerBin, "init", templateDir], { stdio: "inherit" });
// Build the image
console.log(`[packer] Building ${templateName} image: ${imageDefName}`);
const packerArgs = [
packerBin,
"build",
"-only",
`azure-arm.${templateName}`,
"-var",
`client_id=${clientId}`,
"-var",
`client_secret=${clientSecret}`,
"-var",
`subscription_id=${subscriptionId}`,
"-var",
`tenant_id=${tenantId}`,
"-var",
`resource_group=${resourceGroup}-EASTUS2`,
"-var",
`gallery_resource_group=${resourceGroup}`,
"-var",
`location=${location}`,
"-var",
`gallery_name=${galleryName}`,
"-var",
`image_name=${imageDefName}`,
"-var",
`bootstrap_script=${bootstrapPath}`,
"-var",
`agent_script=${agentPath}`,
templateDir,
];
await spawnSafe(packerArgs, {
stdio: "inherit",
env: {
...process.env,
// Packer also reads these env vars
ARM_CLIENT_ID: clientId,
ARM_CLIENT_SECRET: clientSecret,
ARM_SUBSCRIPTION_ID: subscriptionId,
ARM_TENANT_ID: tenantId,
},
});
console.log(`[packer] Image built successfully: ${imageDefName}`);
}
/**
* Download and install Packer if not already available.
*/
async function ensurePacker() {
// Check if packer is already in PATH
const packerPath = which("packer");
if (packerPath) {
console.log("[packer] Found:", packerPath);
return packerPath;
}
// Check if we have a local copy
const localPacker = join(tmpdir(), "packer");
if (existsSync(localPacker)) {
return localPacker;
}
// Download Packer
const version = "1.15.0";
const platform = process.platform === "win32" ? "windows" : process.platform;
const packerArch = process.arch === "arm64" ? "arm64" : "amd64";
const url = `https://releases.hashicorp.com/packer/${version}/packer_${version}_${platform}_${packerArch}.zip`;
console.log(`[packer] Downloading Packer ${version}...`);
const zipPath = join(tmpdir(), "packer.zip");
const response = await fetch(url);
if (!response.ok) throw new Error(`Failed to download Packer: ${response.status}`);
const buffer = Buffer.from(await response.arrayBuffer());
writeFileSync(zipPath, buffer);
// Extract
await spawnSafe(["unzip", "-o", zipPath, "-d", tmpdir()], { stdio: "inherit" });
chmodSync(localPacker, 0o755);
console.log(`[packer] Installed Packer ${version}`);
return localPacker;
}
async function main() {
const { positionals } = parseArgs({
allowPositionals: true,
@@ -1269,6 +1435,13 @@ async function main() {
}
}
// Use Packer for Windows Azure image builds — it handles VM creation,
// bootstrap, sysprep, and gallery capture via WinRM (no Run Command hacks).
if (args["cloud"] === "azure" && os === "windows" && (command === "create-image" || command === "publish-image")) {
await buildWindowsImageWithPacker({ os, arch, release, command, ci, agentPath, bootstrapPath });
return;
}
/** @type {Machine} */
const machine = await startGroup("Creating machine...", async () => {
console.log("Creating machine:");
@@ -1342,7 +1515,7 @@ async function main() {
});
}
await startGroup("Connecting with SSH...", async () => {
await startGroup(`Connecting${options.cloud === "azure" ? "" : " with SSH"}...`, async () => {
const command = os === "windows" ? ["cmd", "/c", "ver"] : ["uname", "-a"];
await machine.spawnSafe(command, { stdio: "inherit" });
});
@@ -1392,7 +1565,12 @@ async function main() {
if (cloud.name === "docker" || features?.includes("docker")) {
return;
}
await machine.spawnSafe(["node", remotePath, "install"], { stdio: "inherit" });
// Refresh PATH from registry before running agent.mjs — bootstrap added
// buildkite-agent to PATH but Azure Run Command sessions have stale PATH.
const cmd = `$env:PATH = [Environment]::GetEnvironmentVariable('PATH', 'Machine') + ';' + [Environment]::GetEnvironmentVariable('PATH', 'User'); C:\\Scoop\\apps\\nodejs\\current\\node.exe ${remotePath} install`;
await machine.spawnSafe(["powershell", "-NoProfile", "-Command", cmd], {
stdio: "inherit",
});
});
} else {
const tmpPath = "/tmp/agent.mjs";

View File

@@ -0,0 +1,74 @@
packer {
required_plugins {
azure = {
source = "github.com/hashicorp/azure"
version = "= 2.5.0"
}
}
}
// Shared variables for all Windows image builds
variable "client_id" {
type = string
default = env("AZURE_CLIENT_ID")
}
variable "client_secret" {
type = string
sensitive = true
default = env("AZURE_CLIENT_SECRET")
}
variable "subscription_id" {
type = string
default = env("AZURE_SUBSCRIPTION_ID")
}
variable "tenant_id" {
type = string
default = env("AZURE_TENANT_ID")
}
variable "resource_group" {
type = string
default = env("AZURE_RESOURCE_GROUP")
}
variable "location" {
type = string
default = "eastus2"
}
variable "gallery_name" {
type = string
default = "bunCIGallery2"
}
variable "build_number" {
type = string
default = "0"
}
variable "image_name" {
type = string
default = ""
description = "Gallery image definition name. If empty, derived from build_number."
}
variable "bootstrap_script" {
type = string
default = "scripts/bootstrap.ps1"
}
variable "agent_script" {
type = string
default = ""
description = "Path to bundled agent.mjs. If empty, agent install is skipped."
}
variable "gallery_resource_group" {
type = string
default = "BUN-CI"
description = "Resource group containing the Compute Gallery (may differ from build RG)"
}

View File

@@ -0,0 +1,143 @@
source "azure-arm" "windows-arm64" {
// Authentication
client_id = var.client_id
client_secret = var.client_secret
subscription_id = var.subscription_id
tenant_id = var.tenant_id
// Source image Windows 11 ARM64 (no Windows Server ARM64 exists)
os_type = "Windows"
image_publisher = "MicrosoftWindowsDesktop"
image_offer = "windows11preview-arm64"
image_sku = "win11-24h2-pro"
image_version = "latest"
// Build VM only used during image creation, not for CI runners.
// CI runner VM sizes are set in ci.mjs (azureVmSizes).
vm_size = "Standard_D4ps_v6"
// Use existing resource group instead of creating a temp one
build_resource_group_name = var.resource_group
os_disk_size_gb = 150
// Security
security_type = "TrustedLaunch"
secure_boot_enabled = true
vtpm_enabled = true
// Networking Packer creates a temp VNet + public IP + NSG automatically.
// WinRM communicator
communicator = "winrm"
winrm_use_ssl = true
winrm_insecure = true
winrm_timeout = "15m"
winrm_username = "packer"
// CRITICAL: No managed_image_name ARM64 doesn't support Managed Images.
// Packer publishes directly from the VM to the gallery (PR #242 feature).
shared_image_gallery_destination {
subscription = var.subscription_id
resource_group = var.gallery_resource_group
gallery_name = var.gallery_name
image_name = var.image_name != "" ? var.image_name : "windows-aarch64-11-build-${var.build_number}"
image_version = "1.0.0"
storage_account_type = "Standard_LRS"
target_region { name = var.location }
target_region { name = "australiaeast" }
target_region { name = "brazilsouth" }
target_region { name = "canadacentral" }
target_region { name = "canadaeast" }
target_region { name = "centralindia" }
target_region { name = "centralus" }
target_region { name = "francecentral" }
target_region { name = "germanywestcentral" }
target_region { name = "italynorth" }
target_region { name = "japaneast" }
target_region { name = "japanwest" }
target_region { name = "koreacentral" }
target_region { name = "mexicocentral" }
target_region { name = "northcentralus" }
target_region { name = "northeurope" }
target_region { name = "southcentralus" }
target_region { name = "southeastasia" }
target_region { name = "spaincentral" }
target_region { name = "swedencentral" }
target_region { name = "switzerlandnorth" }
target_region { name = "uaenorth" }
target_region { name = "ukwest" }
target_region { name = "westeurope" }
target_region { name = "westus" }
target_region { name = "westus2" }
target_region { name = "westus3" }
}
azure_tags = {
os = "windows"
arch = "aarch64"
build = var.build_number
}
}
build {
sources = ["source.azure-arm.windows-arm64"]
// Step 1: Run bootstrap installs all build dependencies
provisioner "powershell" {
script = var.bootstrap_script
valid_exit_codes = [0, 3010]
environment_vars = ["CI=true"]
}
// Step 2: Upload agent.mjs
provisioner "file" {
source = var.agent_script
destination = "C:\\buildkite-agent\\agent.mjs"
}
// Step 3: Install agent service via nssm
provisioner "powershell" {
inline = [
"C:\\Scoop\\apps\\nodejs\\current\\node.exe C:\\buildkite-agent\\agent.mjs install"
]
valid_exit_codes = [0]
}
// Step 4: Reboot to clear pending updates (VS Build Tools, Windows Updates)
provisioner "windows-restart" {
restart_timeout = "10m"
}
// Step 5: Sysprep MUST be last provisioner
provisioner "powershell" {
inline = [
"Remove-Item -Recurse -Force C:\\Windows\\Panther -ErrorAction SilentlyContinue",
"Write-Output '>>> Clearing pending reboot flags...'",
"Remove-Item 'HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Component Based Servicing\\RebootPending' -Recurse -Force -ErrorAction SilentlyContinue",
"Remove-ItemProperty 'HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\WindowsUpdate\\Auto Update' -Name 'RebootRequired' -Force -ErrorAction SilentlyContinue",
"Remove-Item 'HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\WindowsUpdate\\Auto Update\\RebootRequired' -Recurse -Force -ErrorAction SilentlyContinue",
"Remove-ItemProperty 'HKLM:\\SYSTEM\\CurrentControlSet\\Control\\Session Manager' -Name 'PendingFileRenameOperations' -Force -ErrorAction SilentlyContinue",
"Write-Output '>>> Waiting for Azure Guest Agent...'",
"while ((Get-Service RdAgent).Status -ne 'Running') { Start-Sleep -s 5 }",
"while ((Get-Service WindowsAzureGuestAgent).Status -ne 'Running') { Start-Sleep -s 5 }",
"Write-Output '>>> Running Sysprep...'",
"$global:LASTEXITCODE = 0",
"& $env:SystemRoot\\System32\\Sysprep\\Sysprep.exe /oobe /generalize /quiet /quit /mode:vm",
"$timeout = 300; $elapsed = 0",
"while ($true) {",
" $imageState = (Get-ItemProperty HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State).ImageState",
" Write-Output \"ImageState: $imageState ($${elapsed}s)\"",
" if ($imageState -eq 'IMAGE_STATE_GENERALIZE_RESEAL_TO_OOBE') { break }",
" if ($elapsed -ge $timeout) {",
" Write-Error \"Timed out after $${timeout}s -- stuck at $imageState\"",
" Get-Content \"$env:SystemRoot\\System32\\Sysprep\\Panther\\setupact.log\" -Tail 100 -ErrorAction SilentlyContinue",
" exit 1",
" }",
" Start-Sleep -s 10",
" $elapsed += 10",
"}",
"Write-Output '>>> Sysprep complete.'"
]
}
}

View File

@@ -0,0 +1,144 @@
source "azure-arm" "windows-x64" {
// Authentication (from env vars or -var flags)
client_id = var.client_id
client_secret = var.client_secret
subscription_id = var.subscription_id
tenant_id = var.tenant_id
// Source image Windows Server 2019 Gen2
os_type = "Windows"
image_publisher = "MicrosoftWindowsServer"
image_offer = "WindowsServer"
image_sku = "2019-datacenter-gensecond"
image_version = "latest"
// Build VM only used during image creation, not for CI runners.
// CI runner VM sizes are set in ci.mjs (azureVmSizes).
vm_size = "Standard_D4ds_v6"
// Use existing resource group instead of creating a temp one
build_resource_group_name = var.resource_group
os_disk_size_gb = 150
// Security
security_type = "TrustedLaunch"
secure_boot_enabled = true
vtpm_enabled = true
// Networking Packer creates a temp VNet + public IP + NSG automatically.
// WinRM needs the public IP to connect from CI runners.
// WinRM communicator Packer auto-configures via temp Key Vault
communicator = "winrm"
winrm_use_ssl = true
winrm_insecure = true
winrm_timeout = "15m"
winrm_username = "packer"
// Output Managed Image (x64 supports this)
// Also publish to Compute Gallery
shared_image_gallery_destination {
subscription = var.subscription_id
resource_group = var.gallery_resource_group
gallery_name = var.gallery_name
image_name = var.image_name != "" ? var.image_name : "windows-x64-2019-build-${var.build_number}"
image_version = "1.0.0"
storage_account_type = "Standard_LRS"
target_region { name = var.location }
target_region { name = "australiaeast" }
target_region { name = "brazilsouth" }
target_region { name = "canadacentral" }
target_region { name = "canadaeast" }
target_region { name = "centralindia" }
target_region { name = "centralus" }
target_region { name = "francecentral" }
target_region { name = "germanywestcentral" }
target_region { name = "italynorth" }
target_region { name = "japaneast" }
target_region { name = "japanwest" }
target_region { name = "koreacentral" }
target_region { name = "mexicocentral" }
target_region { name = "northcentralus" }
target_region { name = "northeurope" }
target_region { name = "southcentralus" }
target_region { name = "southeastasia" }
target_region { name = "spaincentral" }
target_region { name = "swedencentral" }
target_region { name = "switzerlandnorth" }
target_region { name = "uaenorth" }
target_region { name = "ukwest" }
target_region { name = "westeurope" }
target_region { name = "westus" }
target_region { name = "westus2" }
target_region { name = "westus3" }
}
azure_tags = {
os = "windows"
arch = "x64"
build = var.build_number
}
}
build {
sources = ["source.azure-arm.windows-x64"]
// Step 1: Run bootstrap installs all build dependencies
provisioner "powershell" {
script = var.bootstrap_script
valid_exit_codes = [0, 3010]
environment_vars = ["CI=true"]
}
// Step 2: Upload agent.mjs
provisioner "file" {
source = var.agent_script
destination = "C:\\buildkite-agent\\agent.mjs"
}
// Step 3: Install agent service via nssm
provisioner "powershell" {
inline = [
"C:\\Scoop\\apps\\nodejs\\current\\node.exe C:\\buildkite-agent\\agent.mjs install"
]
valid_exit_codes = [0]
}
// Step 4: Reboot to clear pending updates (VS Build Tools, Windows Updates)
provisioner "windows-restart" {
restart_timeout = "10m"
}
// Step 5: Sysprep MUST be last provisioner
provisioner "powershell" {
inline = [
"Remove-Item -Recurse -Force C:\\Windows\\Panther -ErrorAction SilentlyContinue",
"Write-Output '>>> Clearing pending reboot flags...'",
"Remove-Item 'HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Component Based Servicing\\RebootPending' -Recurse -Force -ErrorAction SilentlyContinue",
"Remove-ItemProperty 'HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\WindowsUpdate\\Auto Update' -Name 'RebootRequired' -Force -ErrorAction SilentlyContinue",
"Remove-Item 'HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\WindowsUpdate\\Auto Update\\RebootRequired' -Recurse -Force -ErrorAction SilentlyContinue",
"Remove-ItemProperty 'HKLM:\\SYSTEM\\CurrentControlSet\\Control\\Session Manager' -Name 'PendingFileRenameOperations' -Force -ErrorAction SilentlyContinue",
"Write-Output '>>> Waiting for Azure Guest Agent...'",
"while ((Get-Service RdAgent).Status -ne 'Running') { Start-Sleep -s 5 }",
"while ((Get-Service WindowsAzureGuestAgent).Status -ne 'Running') { Start-Sleep -s 5 }",
"Write-Output '>>> Running Sysprep...'",
"$global:LASTEXITCODE = 0",
"& $env:SystemRoot\\System32\\Sysprep\\Sysprep.exe /oobe /generalize /quiet /quit /mode:vm",
"$timeout = 300; $elapsed = 0",
"while ($true) {",
" $imageState = (Get-ItemProperty HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State).ImageState",
" Write-Output \"ImageState: $imageState ($${elapsed}s)\"",
" if ($imageState -eq 'IMAGE_STATE_GENERALIZE_RESEAL_TO_OOBE') { break }",
" if ($elapsed -ge $timeout) {",
" Write-Error \"Timed out after $${timeout}s -- stuck at $imageState\"",
" Get-Content \"$env:SystemRoot\\System32\\Sysprep\\Panther\\setupact.log\" -Tail 100 -ErrorAction SilentlyContinue",
" exit 1",
" }",
" Start-Sleep -s 10",
" $elapsed += 10",
"}",
"Write-Output '>>> Sysprep complete.'"
]
}
}

View File

@@ -1837,6 +1837,13 @@ export function getTailscale() {
}
}
if (isWindows) {
const tailscaleExe = "C:\\Program Files\\Tailscale\\tailscale.exe";
if (existsSync(tailscaleExe)) {
return tailscaleExe;
}
}
return "tailscale";
}
@@ -2043,7 +2050,7 @@ export function getShell() {
}
/**
* @typedef {"aws" | "google"} Cloud
* @typedef {"aws" | "google" | "azure"} Cloud
*/
/** @type {Cloud | undefined} */
@@ -2136,6 +2143,37 @@ export async function isGoogleCloud() {
}
}
/**
* @returns {Promise<boolean | undefined>}
*/
export async function isAzure() {
if (typeof detectedCloud === "string") {
return detectedCloud === "azure";
}
async function detectAzure() {
// Azure IMDS (Instance Metadata Service) — the official way to detect Azure VMs.
// https://learn.microsoft.com/en-us/azure/virtual-machines/instance-metadata-service
const { error, body } = await curl("http://169.254.169.254/metadata/instance?api-version=2021-02-01", {
headers: { "Metadata": "true" },
retries: 1,
});
if (!error && body) {
try {
const metadata = JSON.parse(body);
if (metadata?.compute?.azEnvironment) {
return true;
}
} catch {}
}
}
if (await detectAzure()) {
detectedCloud = "azure";
return true;
}
}
/**
* @returns {Promise<Cloud | undefined>}
*/
@@ -2151,6 +2189,10 @@ export async function getCloud() {
if (await isGoogleCloud()) {
return "google";
}
if (await isAzure()) {
return "azure";
}
}
/**
@@ -2175,6 +2217,10 @@ export async function getCloudMetadata(name, cloud) {
} else if (cloud === "google") {
url = new URL(name, "http://metadata.google.internal/computeMetadata/v1/instance/");
headers = { "Metadata-Flavor": "Google" };
} else if (cloud === "azure") {
// Azure IMDS uses a single JSON endpoint; individual fields are extracted by the caller.
url = new URL("http://169.254.169.254/metadata/instance?api-version=2021-02-01");
headers = { "Metadata": "true" };
} else {
throw new Error(`Unsupported cloud: ${inspect(cloud)}`);
}
@@ -2193,7 +2239,25 @@ export async function getCloudMetadata(name, cloud) {
* @param {Cloud} [cloud]
* @returns {Promise<string | undefined>}
*/
export function getCloudMetadataTag(tag, cloud) {
export async function getCloudMetadataTag(tag, cloud) {
cloud ??= await getCloud();
if (cloud === "azure") {
// Azure IMDS returns all tags in a single JSON response.
// Tags are in compute.tagsList as [{name, value}, ...].
const body = await getCloudMetadata("", cloud);
if (!body) return;
try {
const metadata = JSON.parse(body);
const tags = metadata?.compute?.tagsList;
if (Array.isArray(tags)) {
const entry = tags.find(t => t.name === tag);
return entry?.value;
}
} catch {}
return;
}
const metadata = {
"aws": `tags/instance/${tag}`,
"google": `labels/${tag.replace(":", "-")}`,

View File

@@ -5,22 +5,7 @@ $ErrorActionPreference = "Stop"
# Detect system architecture
$script:IsARM64 = [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture -eq [System.Runtime.InteropServices.Architecture]::Arm64
# Allow overriding the target arch (useful for cross-compiling on x64 -> ARM64)
$script:VsArch = $null
if ($env:BUN_VS_ARCH) {
switch ($env:BUN_VS_ARCH.ToLowerInvariant()) {
"arm64" { $script:VsArch = "arm64" }
"aarch64" { $script:VsArch = "arm64" }
"amd64" { $script:VsArch = "amd64" }
"x64" { $script:VsArch = "amd64" }
default { throw "Invalid BUN_VS_ARCH: $env:BUN_VS_ARCH (expected arm64|amd64)" }
}
}
if (-not $script:VsArch) {
$script:VsArch = if ($script:IsARM64) { "arm64" } else { "amd64" }
}
$script:VsArch = if ($script:IsARM64) { "arm64" } else { "amd64" }
if($env:VSINSTALLDIR -eq $null) {
Write-Host "Loading Visual Studio environment, this may take a second..."
@@ -51,10 +36,15 @@ if($env:VSINSTALLDIR -eq $null) {
Push-Location $vsDir
try {
$vsShell = (Join-Path -Path $vsDir -ChildPath "Common7\Tools\Launch-VsDevShell.ps1")
# Visual Studio's Launch-VsDevShell.ps1 only supports x86/amd64 for HostArch
# For ARM64 builds, use amd64 as HostArch since it can cross-compile to ARM64
# -HostArch only accepts "x86" or "amd64" — even on native ARM64, use "amd64"
$hostArch = if ($script:VsArch -eq "arm64") { "amd64" } else { $script:VsArch }
. $vsShell -Arch $script:VsArch -HostArch $hostArch
# VS dev shell with -HostArch amd64 sets PROCESSOR_ARCHITECTURE=AMD64,
# which causes CMake to misdetect the system as x64. Restore it on ARM64.
if ($script:IsARM64) {
$env:PROCESSOR_ARCHITECTURE = "ARM64"
}
} finally {
Pop-Location
}

View File

@@ -91,6 +91,8 @@ pub const Features = struct {
pub var yaml_parse: usize = 0;
pub var cpu_profile: usize = 0;
pub var heap_snapshot: usize = 0;
pub var fs_watch: usize = 0;
pub var fs_watchfile: usize = 0;
comptime {
@export(&napi_module_register, .{ .name = "Bun__napi_module_register_count" });

View File

@@ -486,9 +486,11 @@ pub const StatWatcher = struct {
/// After a restat found the file changed, this calls the listener function.
pub fn swapAndCallListenerOnMainThread(this: *StatWatcher) void {
bun.analytics.Features.fs_watchfile += 1;
defer this.deref(); // Balance the ref from restat().
const prev_jsvalue = this.last_jsvalue.swap();
const globalThis = this.globalThis;
const current_jsvalue = statToJSStats(globalThis, &this.getLastStat(), this.bigint) catch return; // TODO: properly propagate exception upwards
this.last_jsvalue.set(globalThis, current_jsvalue);

View File

@@ -261,6 +261,7 @@ pub const FSWatcher = struct {
pub fn onPathUpdatePosix(ctx: ?*anyopaque, event: Event, is_file: bool) void {
const this = bun.cast(*FSWatcher, ctx.?);
bun.analytics.Features.fs_watch += 1;
if (this.verbose) {
switch (event) {
@@ -281,6 +282,7 @@ pub const FSWatcher = struct {
pub fn onPathUpdateWindows(ctx: ?*anyopaque, event: Event, is_file: bool) void {
const this = bun.cast(*FSWatcher, ctx.?);
bun.analytics.Features.fs_watch += 1;
if (this.verbose) {
switch (event) {
@@ -299,9 +301,23 @@ pub const FSWatcher = struct {
return;
}
// The event's path comes from PathWatcherManager.onFileUpdate as a
// []const u8 sub-slice of the watchlist's file_path storage, auto-coerced
// to StringOrBytesToDecode{.bytes_to_free}. We must create a properly
// owned copy: either a bun.String for utf8 encoding or a duped []const u8
// for other encodings.
const owned_event: Event = switch (event) {
inline .rename, .change => |path, t| @unionInit(Event, @tagName(t), if (this.encoding == .utf8)
FSWatchTaskWindows.StringOrBytesToDecode{ .string = bun.String.cloneUTF8(path.bytes_to_free) }
else
FSWatchTaskWindows.StringOrBytesToDecode{ .bytes_to_free = bun.default_allocator.dupe(u8, path.bytes_to_free) catch return }),
.@"error" => |err| .{ .@"error" = err.clone(bun.default_allocator) },
inline else => |value, t| @unionInit(Event, @tagName(t), value),
};
const task = bun.new(FSWatchTaskWindows, .{
.ctx = this,
.event = event,
.event = owned_event,
});
this.eventLoop().enqueueTask(jsc.Task.init(task));
}

View File

@@ -14,10 +14,12 @@ param(
[Switch]$DownloadWithoutCurl = $false
);
# filter out 32 bit + ARM
if (-not ((Get-CimInstance Win32_ComputerSystem)).SystemType -match "x64-based") {
# Detect real CPU architecture from registry — works even under x64 emulation on ARM64.
# Win32_ComputerSystem.SystemType can be unreliable under WoW64.
$Arch = (Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment').PROCESSOR_ARCHITECTURE
if (-not ($Arch -eq "AMD64" -or $Arch -eq "ARM64")) {
Write-Output "Install Failed:"
Write-Output "Bun for Windows is currently only available for x86 64-bit Windows.`n"
Write-Output "Bun for Windows is only available for x86 64-bit and ARM64 Windows.`n"
return 1
}
@@ -103,13 +105,17 @@ function Install-Bun {
$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);
$IsARM64 = $Arch -eq "ARM64"
$BunArch = if ($IsARM64) { "aarch64" } else { "x64" }
$IsBaseline = $false
if (-not $IsARM64) {
$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" }
@@ -134,9 +140,9 @@ function Install-Bun {
return 1
}
$Target = "bun-windows-$Arch"
$Target = "bun-windows-$BunArch"
if ($IsBaseline) {
$Target = "bun-windows-$Arch-baseline"
$Target = "bun-windows-$BunArch-baseline"
}
$BaseURL = "https://github.com/oven-sh/bun/releases"
$URL = "$BaseURL/$(if ($Version -eq "latest") { "latest/download" } else { "download/$Version" })/$Target.zip"
@@ -219,7 +225,8 @@ function Install-Bun {
# I want to keep this error message in for a few months to ensure that
# if someone somehow runs into this, it can be reported.
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"
$VCRedistUrl = if ($IsARM64) { "https://aka.ms/vs/17/release/vc_redist.arm64.exe" } else { "https://aka.ms/vs/17/release/vc_redist.x64.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 -> ${VCRedistUrl}`n`n"
Write-Output "The error above should be unreachable as Bun does not depend on this library. Please comment in https://github.com/oven-sh/bun/issues/8598 or open a new issue.`n`n"
Write-Output "The command '${BunBin}\bun.exe --revision' exited with code ${LASTEXITCODE}`n"
return 1

View File

@@ -70,6 +70,9 @@ case $platform in
'Linux aarch64' | 'Linux arm64')
target=linux-aarch64
;;
'MINGW64'*'ARM64'* | 'MINGW64'*'aarch64'*)
target=windows-aarch64
;;
'MINGW64'*)
target=windows-x64
;;

View File

@@ -348,7 +348,7 @@ pub const LifecycleScriptSubprocess = struct {
if (this.optional) {
if (this.ctx) |ctx| {
ctx.installer.store.entries.items(.step)[ctx.entry_id.get()].store(.done, .release);
ctx.installer.onTaskComplete(ctx.entry_id, .fail);
ctx.installer.onTaskComplete(ctx.entry_id, .skipped);
}
this.decrementPendingScriptTasks();
this.deinitAndDeletePackage();
@@ -454,7 +454,7 @@ pub const LifecycleScriptSubprocess = struct {
if (this.optional) {
if (this.ctx) |ctx| {
ctx.installer.store.entries.items(.step)[ctx.entry_id.get()].store(.done, .release);
ctx.installer.onTaskComplete(ctx.entry_id, .fail);
ctx.installer.onTaskComplete(ctx.entry_id, .skipped);
}
this.decrementPendingScriptTasks();
this.deinitAndDeletePackage();

View File

@@ -633,12 +633,12 @@ var access = function access(path, mode, callback) {
const { defineCustomPromisifyArgs } = require("internal/promisify");
var kCustomPromisifiedSymbol = Symbol.for("nodejs.util.promisify.custom");
{
const existsCb = exists;
exists[kCustomPromisifiedSymbol] = function exists(path) {
const existsCb = exists;
exists[kCustomPromisifiedSymbol] = {
exists(path) {
return new Promise(resolve => existsCb(path, resolve));
};
}
},
}.exists;
defineCustomPromisifyArgs(read, ["bytesRead", "buffer"]);
defineCustomPromisifyArgs(readv, ["bytesRead", "buffers"]);
defineCustomPromisifyArgs(write, ["bytesWritten", "buffer"]);

View File

@@ -2226,8 +2226,8 @@ Interface.prototype.question = function question(query, options, cb) {
}
};
{
Interface.prototype.question[promisify.custom] = function question(query, options) {
Interface.prototype.question[promisify.custom] = {
question(query, options) {
if (options === null || typeof options !== "object") {
options = kEmptyObject;
}
@@ -2252,8 +2252,8 @@ Interface.prototype.question = function question(query, options, cb) {
}
this.question(query, options, cb);
});
};
}
},
}.question;
/**
* Creates a new `readline.Interface` instance.

View File

@@ -10,13 +10,12 @@
"@bufbuild/protobuf": "2.10.2",
"@connectrpc/connect": "2.1.1",
"@connectrpc/connect-node": "2.0.0",
"@duckdb/node-api": "1.1.3-alpha.7",
"@electric-sql/pglite": "0.2.17",
"@fastify/websocket": "11.0.2",
"@grpc/grpc-js": "1.12.0",
"@grpc/proto-loader": "0.7.10",
"@happy-dom/global-registrator": "17.0.3",
"@napi-rs/canvas": "0.1.65",
"@napi-rs/canvas": "0.1.91",
"@nestjs/common": "11.0.3",
"@nestjs/core": "11.0.3",
"@prisma/client": "5.8.0",
@@ -40,7 +39,6 @@
"commander": "12.1.0",
"detect-libc": "2.0.3",
"devalue": "5.1.1",
"duckdb": "1.3.1",
"es-module-lexer": "1.3.0",
"esbuild": "0.18.6",
"express": "4.18.2",
@@ -61,7 +59,6 @@
"jws": "4.0.0",
"lodash": "4.17.21",
"mongodb": "6.0.0",
"msgpackr-extract": "3.0.2",
"msw": "2.3.0",
"mysql2": "3.7.0",
"node-gyp": "10.0.1",
@@ -82,7 +79,7 @@
"reflect-metadata": "0.2.2",
"rollup": "4.4.1",
"sass": "1.79.4",
"sharp": "0.33.0",
"sharp": "0.34.5",
"sinon": "6.0.0",
"socket.io": "4.7.1",
"socket.io-adapter": "2.5.5",
@@ -120,6 +117,11 @@
"@types/utf-8-validate": "5.0.0",
"@types/ws": "8.5.10",
},
"optionalDependencies": {
"@duckdb/node-api": "1.1.3-alpha.7",
"duckdb": "1.3.1",
"msgpackr-extract": "3.0.2",
},
},
},
"overrides": {
@@ -227,7 +229,7 @@
"@electric-sql/pglite": ["@electric-sql/pglite@0.2.17", "", {}, "sha512-qEpKRT2oUaWDH6tjRxLHjdzMqRUGYDnGZlKrnL4dJ77JVMcP2Hpo3NYnOSPKdZdeec57B6QPprCUFg0picx5Pw=="],
"@emnapi/runtime": ["@emnapi/runtime@0.44.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-ZX/etZEZw8DR7zAB1eVQT40lNo0jeqpb6dCgOvctB6FIQ5PoXfMuNY8+ayQfu8tNQbAB8gQWSSJupR8NxeiZXw=="],
"@emnapi/runtime": ["@emnapi/runtime@1.8.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg=="],
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.1", "", { "os": "aix", "cpu": "ppc64" }, "sha512-kfYGy8IdzTGy+z0vFGvExZtxkFlA4zAxgKEahG9KE1ScBjpQnFsNOX8KTU5ojNru5ed5CVoJYXFtoxaq5nFbjQ=="],
@@ -305,43 +307,55 @@
"@happy-dom/global-registrator": ["@happy-dom/global-registrator@17.0.3", "", { "dependencies": { "happy-dom": "^17.0.3" } }, "sha512-isCCWywZq8XPE3A5y7pRyFIsAgij+3eVXgQNCbexGRP00/+nctmf4SfQxC3vV3MmEaOXaNj7IiiSC0BtSHQZgg=="],
"@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.0", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.0" }, "os": "darwin", "cpu": "arm64" }, "sha512-070tEheekI1LJWTGPC9WlQEa5UoKTXzzlORBHMX4TbfUxMiL336YHR8vBEUNsjse0RJCX8dZ4ZXwT595aEF1ug=="],
"@img/colour": ["@img/colour@1.0.0", "", {}, "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw=="],
"@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.33.0", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.0" }, "os": "darwin", "cpu": "x64" }, "sha512-pu/nvn152F3qbPeUkr+4e9zVvEhD3jhwzF473veQfMPkOYo9aoWXSfdZH/E6F+nYC3qvFjbxbvdDbUtEbghLqw=="],
"@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w=="],
"@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.0.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-VzYd6OwnUR81sInf3alj1wiokY50DjsHz5bvfnsFpxs5tqQxESoHtJO6xyksDs3RIkyhMWq2FufXo6GNSU9BMw=="],
"@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.4" }, "os": "darwin", "cpu": "x64" }, "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw=="],
"@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.0.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-dD9OznTlHD6aovRswaPNEy8dKtSAmNo4++tO7uuR4o5VxbVAOoEQ1uSmN4iFAdQneTHws1lkTZeiXPrcCkh6IA=="],
"@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g=="],
"@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.0.0", "", { "os": "linux", "cpu": "arm" }, "sha512-VwgD2eEikDJUk09Mn9Dzi1OW2OJFRQK+XlBTkUNmAWPrtj8Ly0yq05DFgu1VCMx2/DqCGQVi5A1dM9hTmxf3uw=="],
"@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg=="],
"@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.0.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-xTYThiqEZEZc0PRU90yVtM3KE7lw1bKdnDQ9kCTHWbqWyHOe4NpPOtMGy27YnN51q0J5dqRrvicfPbALIOeAZA=="],
"@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.2.4", "", { "os": "linux", "cpu": "arm" }, "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A=="],
"@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.0.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-o9E46WWBC6JsBlwU4QyU9578G77HBDT1NInd+aERfxeOPbk0qBZHgoDsQmA2v9TbqJRWzoBPx1aLOhprBMgPjw=="],
"@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw=="],
"@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.0.0", "", { "os": "linux", "cpu": "x64" }, "sha512-naldaJy4hSVhWBgEjfdBY85CAa4UO+W1nx6a1sWStHZ7EUfNiuBTTN2KUYT5dH1+p/xij1t2QSXfCiFJoC5S/Q=="],
"@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.2.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA=="],
"@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.0.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-OdorplCyvmSAPsoJLldtLh3nLxRrkAAAOHsGWGDYfN0kh730gifK+UZb3dWORRa6EusNqCTjfXV4GxvgJ/nPDQ=="],
"@img/sharp-libvips-linux-riscv64": ["@img/sharp-libvips-linux-riscv64@1.2.4", "", { "os": "linux", "cpu": "none" }, "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA=="],
"@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.0.0", "", { "os": "linux", "cpu": "x64" }, "sha512-FW8iK6rJrg+X2jKD0Ajhjv6y74lToIBEvkZhl42nZt563FfxkCYacrXZtd+q/sRQDypQLzY5WdLkVTbJoPyqNg=="],
"@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.2.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ=="],
"@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.33.0", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.0.0" }, "os": "linux", "cpu": "arm" }, "sha512-4horD3wMFd5a0ddbDY8/dXU9CaOgHjEHALAddXgafoR5oWq5s8X61PDgsSeh4Qupsdo6ycfPPSSNBrfVQnwwrg=="],
"@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw=="],
"@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.33.0", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.0.0" }, "os": "linux", "cpu": "arm64" }, "sha512-dcomVSrtgF70SyOr8RCOCQ8XGVThXwe71A1d8MGA+mXEVRJ/J6/TrCbBEJh9ddcEIIsrnrkolaEvYSHqVhswQw=="],
"@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw=="],
"@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.33.0", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.0.0" }, "os": "linux", "cpu": "s390x" }, "sha512-TiVJbx38J2rNVfA309ffSOB+3/7wOsZYQEOlKqOUdWD/nqkjNGrX+YQGz7nzcf5oy2lC+d37+w183iNXRZNngQ=="],
"@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg=="],
"@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.33.0", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.0.0" }, "os": "linux", "cpu": "x64" }, "sha512-PaZM4Zi7/Ek71WgTdvR+KzTZpBqrQOFcPe7/8ZoPRlTYYRe43k6TWsf4GVH6XKRLMYeSp8J89RfAhBrSP4itNA=="],
"@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.2.4" }, "os": "linux", "cpu": "arm" }, "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw=="],
"@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.33.0", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.0.0" }, "os": "linux", "cpu": "arm64" }, "sha512-1QLbbN0zt+32eVrg7bb1lwtvEaZwlhEsY1OrijroMkwAqlHqFj6R33Y47s2XUv7P6Ie1PwCxK/uFnNqMnkd5kg=="],
"@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg=="],
"@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.33.0", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.0.0" }, "os": "linux", "cpu": "x64" }, "sha512-CecqgB/CnkvCWFhmfN9ZhPGMLXaEBXl4o7WtA6U3Ztrlh/s7FUKX4vNxpMSYLIrWuuzjiaYdfU3+Tdqh1xaHfw=="],
"@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.2.4" }, "os": "linux", "cpu": "ppc64" }, "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA=="],
"@img/sharp-wasm32": ["@img/sharp-wasm32@0.33.0", "", { "dependencies": { "@emnapi/runtime": "^0.44.0" }, "cpu": "none" }, "sha512-Hn4js32gUX9qkISlemZBUPuMs0k/xNJebUNl/L6djnU07B/HAA2KaxRVb3HvbU5fL242hLOcp0+tR+M8dvJUFw=="],
"@img/sharp-linux-riscv64": ["@img/sharp-linux-riscv64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-riscv64": "1.2.4" }, "os": "linux", "cpu": "none" }, "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw=="],
"@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.33.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-5HfcsCZi3l5nPRF2q3bllMVMDXBqEWI3Q8KQONfzl0TferFE5lnsIG0A1YrntMAGqvkzdW6y1Ci1A2uTvxhfzg=="],
"@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.2.4" }, "os": "linux", "cpu": "s390x" }, "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg=="],
"@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.33.0", "", { "os": "win32", "cpu": "x64" }, "sha512-i3DtP/2ce1yKFj4OzOnOYltOEL/+dp4dc4dJXJBv6god1AFTcmkaA99H/7SwOmkCOBQkbVvA3lCGm3/5nDtf9Q=="],
"@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ=="],
"@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg=="],
"@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q=="],
"@img/sharp-wasm32": ["@img/sharp-wasm32@0.34.5", "", { "dependencies": { "@emnapi/runtime": "^1.7.0" }, "cpu": "none" }, "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw=="],
"@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.34.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g=="],
"@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.34.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg=="],
"@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.5", "", { "os": "win32", "cpu": "x64" }, "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw=="],
"@inquirer/confirm": ["@inquirer/confirm@3.1.9", "", { "dependencies": { "@inquirer/core": "^8.2.2", "@inquirer/type": "^1.3.3" } }, "sha512-UF09aejxCi4Xqm6N/jJAiFXArXfi9al52AFaSD+2uIHnhZGtd1d6lIGTRMPouVSJxbGEi+HkOWSYaiEY/+szUw=="],
@@ -449,27 +463,29 @@
"@mswjs/interceptors": ["@mswjs/interceptors@0.29.1", "", { "dependencies": { "@open-draft/deferred-promise": "^2.2.0", "@open-draft/logger": "^0.3.0", "@open-draft/until": "^2.0.0", "is-node-process": "^1.2.0", "outvariant": "^1.2.1", "strict-event-emitter": "^0.5.1" } }, "sha512-3rDakgJZ77+RiQUuSK69t1F0m8BQKA8Vh5DCS5V0DWvNY67zob2JhhQrhCO0AKLGINTRSFd1tBaHcJTkhefoSw=="],
"@napi-rs/canvas": ["@napi-rs/canvas@0.1.65", "", { "optionalDependencies": { "@napi-rs/canvas-android-arm64": "0.1.65", "@napi-rs/canvas-darwin-arm64": "0.1.65", "@napi-rs/canvas-darwin-x64": "0.1.65", "@napi-rs/canvas-linux-arm-gnueabihf": "0.1.65", "@napi-rs/canvas-linux-arm64-gnu": "0.1.65", "@napi-rs/canvas-linux-arm64-musl": "0.1.65", "@napi-rs/canvas-linux-riscv64-gnu": "0.1.65", "@napi-rs/canvas-linux-x64-gnu": "0.1.65", "@napi-rs/canvas-linux-x64-musl": "0.1.65", "@napi-rs/canvas-win32-x64-msvc": "0.1.65" } }, "sha512-YcFhXQcp+b2d38zFOJNbpyPHnIL7KAEkhJQ+UeeKI5IpE9B8Cpf/M6RiHPQXSsSqnYbrfFylnW49dyh2oeSblQ=="],
"@napi-rs/canvas": ["@napi-rs/canvas@0.1.91", "", { "optionalDependencies": { "@napi-rs/canvas-android-arm64": "0.1.91", "@napi-rs/canvas-darwin-arm64": "0.1.91", "@napi-rs/canvas-darwin-x64": "0.1.91", "@napi-rs/canvas-linux-arm-gnueabihf": "0.1.91", "@napi-rs/canvas-linux-arm64-gnu": "0.1.91", "@napi-rs/canvas-linux-arm64-musl": "0.1.91", "@napi-rs/canvas-linux-riscv64-gnu": "0.1.91", "@napi-rs/canvas-linux-x64-gnu": "0.1.91", "@napi-rs/canvas-linux-x64-musl": "0.1.91", "@napi-rs/canvas-win32-arm64-msvc": "0.1.91", "@napi-rs/canvas-win32-x64-msvc": "0.1.91" } }, "sha512-eeIe1GoB74P1B0Nkw6pV8BCQ3hfCfvyYr4BntzlCsnFXzVJiPMDnLeIx3gVB0xQMblHYnjK/0nCLvirEhOjr5g=="],
"@napi-rs/canvas-android-arm64": ["@napi-rs/canvas-android-arm64@0.1.65", "", { "os": "android", "cpu": "arm64" }, "sha512-ZYwqFYEKcT5Zr8lbiaJNJj/poLaeK2TncolY914r+gD2TJNeP7ZqvE7A2SX/1C9MB4E3DQEwm3YhL3WEf0x3MQ=="],
"@napi-rs/canvas-android-arm64": ["@napi-rs/canvas-android-arm64@0.1.91", "", { "os": "android", "cpu": "arm64" }, "sha512-SLLzXXgSnfct4zy/BVAfweZQkYkPJsNsJ2e5DOE8DFEHC6PufyUrwb12yqeu2So2IOIDpWJJaDAxKY/xpy6MYQ=="],
"@napi-rs/canvas-darwin-arm64": ["@napi-rs/canvas-darwin-arm64@0.1.65", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Pg1pfiJEyDIsX+V0QaJPRWvXbw5zmWAk3bivFCvt/5pwZb37/sT6E/RqPHT9NnqpDyKW6SriwY9ypjljysUA1Q=="],
"@napi-rs/canvas-darwin-arm64": ["@napi-rs/canvas-darwin-arm64@0.1.91", "", { "os": "darwin", "cpu": "arm64" }, "sha512-bzdbCjIjw3iRuVFL+uxdSoMra/l09ydGNX9gsBxO/zg+5nlppscIpj6gg+nL6VNG85zwUarDleIrUJ+FWHvmuA=="],
"@napi-rs/canvas-darwin-x64": ["@napi-rs/canvas-darwin-x64@0.1.65", "", { "os": "darwin", "cpu": "x64" }, "sha512-3Tr+/HjdJN7Z/VKIcsxV2DvDIibZCExgfYTgljCkUSFuoI7iNkOE6Dc1Q6j212EB9PeO8KmfrViBqHYT6IwWkA=="],
"@napi-rs/canvas-darwin-x64": ["@napi-rs/canvas-darwin-x64@0.1.91", "", { "os": "darwin", "cpu": "x64" }, "sha512-q3qpkpw0IsG9fAS/dmcGIhCVoNxj8ojbexZKWwz3HwxlEWsLncEQRl4arnxrwbpLc2nTNTyj4WwDn7QR5NDAaA=="],
"@napi-rs/canvas-linux-arm-gnueabihf": ["@napi-rs/canvas-linux-arm-gnueabihf@0.1.65", "", { "os": "linux", "cpu": "arm" }, "sha512-3KP+dYObH7CVkZMZWwk1WX9jRjL+EKdQtD43H8MOI+illf+dwqLlecdQ4d9bQRIxELKJ8dyPWY4fOp/Ngufrdg=="],
"@napi-rs/canvas-linux-arm-gnueabihf": ["@napi-rs/canvas-linux-arm-gnueabihf@0.1.91", "", { "os": "linux", "cpu": "arm" }, "sha512-Io3g8wJZVhK8G+Fpg1363BE90pIPqg+ZbeehYNxPWDSzbgwU3xV0l8r/JBzODwC7XHi1RpFEk+xyUTMa2POj6w=="],
"@napi-rs/canvas-linux-arm64-gnu": ["@napi-rs/canvas-linux-arm64-gnu@0.1.65", "", { "os": "linux", "cpu": "arm64" }, "sha512-Ka3StKz7Dq7kjTF3nNJCq43UN/VlANS7qGE3dWkn1d+tQNsCRy/wRmyt1TUFzIjRqcTFMQNRbgYq84+53UBA0A=="],
"@napi-rs/canvas-linux-arm64-gnu": ["@napi-rs/canvas-linux-arm64-gnu@0.1.91", "", { "os": "linux", "cpu": "arm64" }, "sha512-HBnto+0rxx1bQSl8bCWA9PyBKtlk2z/AI32r3cu4kcNO+M/5SD4b0v1MWBWZyqMQyxFjWgy3ECyDjDKMC6tY1A=="],
"@napi-rs/canvas-linux-arm64-musl": ["@napi-rs/canvas-linux-arm64-musl@0.1.65", "", { "os": "linux", "cpu": "arm64" }, "sha512-O4xMASm2JrmqYoiDyxVWi+z5C14H+oVEag2rZ5iIA67dhWqYZB+iO7wCFpBYRj31JPBR29FOsu6X9zL+DwBFdw=="],
"@napi-rs/canvas-linux-arm64-musl": ["@napi-rs/canvas-linux-arm64-musl@0.1.91", "", { "os": "linux", "cpu": "arm64" }, "sha512-/eJtVe2Xw9A86I4kwXpxxoNagdGclu12/NSMsfoL8q05QmeRCbfjhg1PJS7ENAuAvaiUiALGrbVfeY1KU1gztQ=="],
"@napi-rs/canvas-linux-riscv64-gnu": ["@napi-rs/canvas-linux-riscv64-gnu@0.1.65", "", { "os": "linux", "cpu": "none" }, "sha512-dblWDaA59ZU8bPbkfM+riSke7sFbNZ70LEevUdI5rgiFEUzYUQlU34gSBzemTACj5rCWt1BYeu0GfkLSjNMBSw=="],
"@napi-rs/canvas-linux-riscv64-gnu": ["@napi-rs/canvas-linux-riscv64-gnu@0.1.91", "", { "os": "linux", "cpu": "none" }, "sha512-floNK9wQuRWevUhhXRcuis7h0zirdytVxPgkonWO+kQlbvxV7gEUHGUFQyq4n55UHYFwgck1SAfJ1HuXv/+ppQ=="],
"@napi-rs/canvas-linux-x64-gnu": ["@napi-rs/canvas-linux-x64-gnu@0.1.65", "", { "os": "linux", "cpu": "x64" }, "sha512-wsp+atutw13OJXGU3DDkdngtBDoEg01IuK5xMe0L6VFPV8maGkh17CXze078OD5QJOc6kFyw3DDscMLOPF8+oA=="],
"@napi-rs/canvas-linux-x64-gnu": ["@napi-rs/canvas-linux-x64-gnu@0.1.91", "", { "os": "linux", "cpu": "x64" }, "sha512-c3YDqBdf7KETuZy2AxsHFMsBBX1dWT43yFfWUq+j1IELdgesWtxf/6N7csi3VPf6VA3PmnT9EhMyb+M1wfGtqw=="],
"@napi-rs/canvas-linux-x64-musl": ["@napi-rs/canvas-linux-x64-musl@0.1.65", "", { "os": "linux", "cpu": "x64" }, "sha512-odX+nN+IozWzhdj31INcHz3Iy9+EckNw+VqsZcaUxZOTu7/3FmktRNI6aC1qe5minZNv1m05YOS1FVf7fvmjlA=="],
"@napi-rs/canvas-linux-x64-musl": ["@napi-rs/canvas-linux-x64-musl@0.1.91", "", { "os": "linux", "cpu": "x64" }, "sha512-RpZ3RPIwgEcNBHSHSX98adm+4VP8SMT5FN6250s5jQbWpX/XNUX5aLMfAVJS/YnDjS1QlsCgQxFOPU0aCCWgag=="],
"@napi-rs/canvas-win32-x64-msvc": ["@napi-rs/canvas-win32-x64-msvc@0.1.65", "", { "os": "win32", "cpu": "x64" }, "sha512-RZQX3luWnlNWgdMnLMQ1hyfQraeAn9lnxWWVCHuUM4tAWEV8UDdeb7cMwmJW7eyt8kAosmjeHt3cylQMHOxGFg=="],
"@napi-rs/canvas-win32-arm64-msvc": ["@napi-rs/canvas-win32-arm64-msvc@0.1.91", "", { "os": "win32", "cpu": "arm64" }, "sha512-gF8MBp4X134AgVurxqlCdDA2qO0WaDdi9o6Sd5rWRVXRhWhYQ6wkdEzXNLIrmmros0Tsp2J0hQzx4ej/9O8trQ=="],
"@napi-rs/canvas-win32-x64-msvc": ["@napi-rs/canvas-win32-x64-msvc@0.1.91", "", { "os": "win32", "cpu": "x64" }, "sha512-++gtW9EV/neKI8TshD8WFxzBYALSPag2kFRahIJV+LYsyt5kBn21b1dBhEUDHf7O+wiZmuFCeUa7QKGHnYRZBA=="],
"@nestjs/common": ["@nestjs/common@11.0.3", "", { "dependencies": { "iterare": "1.2.1", "tslib": "2.8.1", "uid": "2.0.2" }, "peerDependencies": { "class-transformer": "*", "class-validator": "*", "reflect-metadata": "^0.1.12 || ^0.2.0", "rxjs": "^7.1.0" }, "optionalPeers": ["class-transformer", "class-validator"] }, "sha512-fTkJWQ20+jvPKfrv3A+T3wsPwwYSJyoJ+pcBzyKtv5fCpK/yX/rJalFUIpw1CDmarfqIaMX9SdkplNyxtvH6RA=="],
@@ -1575,7 +1591,7 @@
"is-arguments": ["is-arguments@1.1.1", "", { "dependencies": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" } }, "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA=="],
"is-arrayish": ["is-arrayish@0.3.2", "", {}, "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="],
"is-arrayish": ["is-arrayish@0.2.1", "", {}, "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="],
"is-binary-path": ["is-binary-path@2.1.0", "", { "dependencies": { "binary-extensions": "^2.0.0" } }, "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw=="],
@@ -2339,7 +2355,7 @@
"shallow-clone": ["shallow-clone@3.0.1", "", { "dependencies": { "kind-of": "^6.0.2" } }, "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA=="],
"sharp": ["sharp@0.33.0", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.2", "semver": "^7.5.4" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.33.0", "@img/sharp-darwin-x64": "0.33.0", "@img/sharp-libvips-darwin-arm64": "1.0.0", "@img/sharp-libvips-darwin-x64": "1.0.0", "@img/sharp-libvips-linux-arm": "1.0.0", "@img/sharp-libvips-linux-arm64": "1.0.0", "@img/sharp-libvips-linux-s390x": "1.0.0", "@img/sharp-libvips-linux-x64": "1.0.0", "@img/sharp-libvips-linuxmusl-arm64": "1.0.0", "@img/sharp-libvips-linuxmusl-x64": "1.0.0", "@img/sharp-linux-arm": "0.33.0", "@img/sharp-linux-arm64": "0.33.0", "@img/sharp-linux-s390x": "0.33.0", "@img/sharp-linux-x64": "0.33.0", "@img/sharp-linuxmusl-arm64": "0.33.0", "@img/sharp-linuxmusl-x64": "0.33.0", "@img/sharp-wasm32": "0.33.0", "@img/sharp-win32-ia32": "0.33.0", "@img/sharp-win32-x64": "0.33.0" } }, "sha512-99DZKudjm/Rmz+M0/26t4DKpXyywAOJaayGS9boEn7FvgtG0RYBi46uPE2c+obcJRtA3AZa0QwJot63gJQ1F0Q=="],
"sharp": ["sharp@0.34.5", "", { "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", "semver": "^7.7.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.5", "@img/sharp-darwin-x64": "0.34.5", "@img/sharp-libvips-darwin-arm64": "1.2.4", "@img/sharp-libvips-darwin-x64": "1.2.4", "@img/sharp-libvips-linux-arm": "1.2.4", "@img/sharp-libvips-linux-arm64": "1.2.4", "@img/sharp-libvips-linux-ppc64": "1.2.4", "@img/sharp-libvips-linux-riscv64": "1.2.4", "@img/sharp-libvips-linux-s390x": "1.2.4", "@img/sharp-libvips-linux-x64": "1.2.4", "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", "@img/sharp-libvips-linuxmusl-x64": "1.2.4", "@img/sharp-linux-arm": "0.34.5", "@img/sharp-linux-arm64": "0.34.5", "@img/sharp-linux-ppc64": "0.34.5", "@img/sharp-linux-riscv64": "0.34.5", "@img/sharp-linux-s390x": "0.34.5", "@img/sharp-linux-x64": "0.34.5", "@img/sharp-linuxmusl-arm64": "0.34.5", "@img/sharp-linuxmusl-x64": "0.34.5", "@img/sharp-wasm32": "0.34.5", "@img/sharp-win32-arm64": "0.34.5", "@img/sharp-win32-ia32": "0.34.5", "@img/sharp-win32-x64": "0.34.5" } }, "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg=="],
"shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
@@ -2781,6 +2797,8 @@
"@cypress/request/uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="],
"@emnapi/runtime/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@fastify/ajv-compiler/ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="],
"@fastify/proxy-addr/ipaddr.js": ["ipaddr.js@2.2.0", "", {}, "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA=="],
@@ -3079,8 +3097,6 @@
"engine.io-client/ws": ["ws@8.17.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ=="],
"error-ex/is-arrayish": ["is-arrayish@0.2.1", "", {}, "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="],
"escodegen/estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="],
"escodegen/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
@@ -3299,7 +3315,11 @@
"serve-static/send": ["send@0.18.0", "", { "dependencies": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", "http-errors": "2.0.0", "mime": "1.6.0", "ms": "2.1.3", "on-finished": "2.4.1", "range-parser": "~1.2.1", "statuses": "2.0.1" } }, "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg=="],
"sharp/semver": ["semver@7.6.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w=="],
"sharp/detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
"sharp/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
"simple-swizzle/is-arrayish": ["is-arrayish@0.3.2", "", {}, "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="],
"sinon/diff": ["diff@3.5.0", "", {}, "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA=="],

View File

@@ -203,13 +203,14 @@ console.log(utils());`,
});
expect(await exited).toBe(0);
const { stdout } = Bun.spawn({
await using proc = Bun.spawn({
cmd: [path.join(baseDir, "exe.exe")],
env: bunEnv,
stdout: "pipe",
stderr: "pipe",
});
const text = await stdout.text();
const text = await proc.stdout.text();
await proc.exited;
expect(text).toContain(path.join(baseDir, "我") + "\n");
expect(text).toContain(path.join(baseDir, "我", "我.ts") + "\n");

View File

@@ -178,7 +178,7 @@ describe.skipIf(!isWindows).concurrent("Windows compile metadata", () => {
entrypoints: [join(String(dir), "app.js")],
outdir: String(dir),
compile: {
target: "bun-windows-x64",
target: process.arch === "arm64" ? "bun-windows-aarch64" : "bun-windows-x64",
outfile: "app-api.exe",
windows: {
title: "API App",
@@ -225,7 +225,7 @@ describe.skipIf(!isWindows).concurrent("Windows compile metadata", () => {
entrypoints: [join(String(dir), "app.js")],
outdir: String(dir),
compile: {
target: "bun-windows-x64",
target: process.arch === "arm64" ? "bun-windows-aarch64" : "bun-windows-x64",
outfile: "partial-api.exe",
windows: {
title: "Partial App",
@@ -262,7 +262,7 @@ describe.skipIf(!isWindows).concurrent("Windows compile metadata", () => {
entrypoints: [join(String(dir), "app.js")],
outdir: "./out",
compile: {
target: "bun-windows-x64",
target: process.arch === "arm64" ? "bun-windows-aarch64" : "bun-windows-x64",
outfile: "relative.exe",
windows: {
title: "Relative Path App",

View File

@@ -5081,7 +5081,7 @@ describe.concurrent("bun-install", () => {
stdout: "pipe",
stdin: "ignore",
stderr: "pipe",
env: { ...env, "GIT_ASKPASS": "echo" },
env: { ...env, "GIT_ASKPASS": "echo", "GIT_CONFIG_NOSYSTEM": "1" },
});
const err = await stderr.text();
expect(err.split(/\r?\n/)).toContain('error: "git clone" for "private-install" failed');

View File

@@ -1,5 +1,5 @@
import { describe, expect, test } from "bun:test";
import { bunEnv, bunExe, isBroken, isCI, isIntelMacOS, isMacOS, isWindows, tempDirWithFiles } from "harness";
import { bunEnv, bunExe, isArm64, isBroken, isCI, isIntelMacOS, isMacOS, isWindows, tempDirWithFiles } from "harness";
import { join } from "path";
describe.concurrent("require.cache", () => {
@@ -24,7 +24,8 @@ describe.concurrent("require.cache", () => {
});
// https://github.com/oven-sh/bun/issues/5188
test("require.cache does not include unevaluated modules", async () => {
// msgpackr-extract has no prebuilt binary for win32-arm64, so it's unavailable there
test.skipIf(isWindows && isArm64)("require.cache does not include unevaluated modules", async () => {
await using proc = Bun.spawn({
cmd: [bunExe(), "run", join(import.meta.dir, "require-cache-bug-5188.js")],
env: bunEnv,

View File

@@ -3,6 +3,7 @@
# Tests that are broken
test/cli/create/create-jsx.test.ts [ FAIL ] # false > react spa (no tailwind) > build
[ WINDOWS-AARCH64 ] test/js/node/test/parallel/test-repl-close.js [ FAIL ] # EPIPE on stdin.write to closed child process
test/bundler/native-plugin.test.ts [ FAIL ] # prints name when plugin crashes
test/cli/run/run-crash-handler.test.ts [ FAIL ] # automatic crash reporter > segfault should report

View File

@@ -1,9 +1,12 @@
import { spawn } from "bun";
import { beforeAll, describe, expect, setDefaultTimeout, test } from "bun:test";
import { cp, rm, writeFile } from "fs/promises";
import { bunExe, bunEnv as env, tempDir } from "harness";
import { bunExe, bunEnv as env, isArm64, isWindows, tempDir } from "harness";
import { join } from "path";
// esbuild@0.19.8 does not support win32-arm64 at runtime
const isWindowsArm64 = isWindows && isArm64;
beforeAll(() => {
setDefaultTimeout(1000 * 60 * 5);
});
@@ -49,7 +52,7 @@ describe.concurrent("esbuild integration test", () => {
expect(await exited).toBe(0);
});
test("install and use estrella", async () => {
test.skipIf(isWindowsArm64)("install and use estrella", async () => {
using dir = tempDir("esbuild-estrella-test", {
"package.json": JSON.stringify({
name: "bun-esbuild-estrella-test",

View File

@@ -1,13 +1,16 @@
import { cc, CString, ptr, type FFIFunction, type Library } from "bun:ffi";
import { afterAll, beforeAll, describe, expect, it } from "bun:test";
import { promises as fs } from "fs";
import { bunEnv, bunExe, isASAN, isWindows, tempDirWithFiles } from "harness";
import { bunEnv, bunExe, isArm64, isASAN, isWindows, tempDirWithFiles } from "harness";
import path from "path";
// TinyCC (and all of bun:ffi) is disabled on Windows ARM64
const isFFIUnavailable = isWindows && isArm64;
// TODO: we need to install build-essential and Apple SDK in CI.
// It can't find includes. It can on machines with that enabled.
// TinyCC's setjmp/longjmp error handling conflicts with ASan.
it.todoIf(isWindows || isASAN)("can run a .c file", () => {
it.todoIf(isWindows || isASAN || isFFIUnavailable)("can run a .c file", () => {
const result = Bun.spawnSync({
cmd: [bunExe(), path.join(__dirname, "cc-fixture.js")],
cwd: __dirname,
@@ -19,7 +22,8 @@ it.todoIf(isWindows || isASAN)("can run a .c file", () => {
});
// TinyCC's setjmp/longjmp error handling conflicts with ASan.
describe.skipIf(isASAN)("given an add(a, b) function", () => {
// TinyCC is disabled on Windows ARM64.
describe.skipIf(isASAN || isFFIUnavailable)("given an add(a, b) function", () => {
const source = /* c */ `
int add(int a, int b) {
return a + b;

View File

@@ -1,8 +1,11 @@
import { dlopen, linkSymbols } from "bun:ffi";
import { describe, expect, test } from "bun:test";
import { isMusl } from "harness";
import { isArm64, isMusl, isWindows } from "harness";
describe("FFI error messages", () => {
// TinyCC (and all of bun:ffi) is disabled on Windows ARM64
const isFFIUnavailable = isWindows && isArm64;
describe.skipIf(isFFIUnavailable)("FFI error messages", () => {
test("dlopen shows library name when library cannot be opened", () => {
// Try to open a non-existent library
try {

View File

@@ -1,5 +1,5 @@
import { describe, expect, test } from "bun:test";
import { bunEnv, bunExe, tempDir } from "harness";
import { bunEnv, bunExe, isWindows, tempDir } from "harness";
// Regression test for use-after-poison in builtin OutputTask callbacks
// inside command substitution $().
@@ -14,7 +14,7 @@ import { bunEnv, bunExe, tempDir } from "harness";
// alongside non-existent paths reliably triggers the ASAN
// use-after-poison.
describe("builtins in command substitution with errors should not crash", () => {
describe.skipIf(isWindows)("builtins in command substitution with errors should not crash", () => {
test("ls with errors in command substitution", async () => {
// Create a temp directory with many files to produce output,
// and include non-existent paths to produce errors.

View File

@@ -14,7 +14,7 @@ async function spawn100() {
test("does not leak", async () => {
const before = process.memoryUsage().rss;
console.log("before", (before / 1024 / 1024).toFixed(3), "MB");
for (let index = 0; index < 200; index++) {
for (let index = 0; index < 30; index++) {
await spawn100();
Bun.gc(true);
}

View File

@@ -11,6 +11,14 @@ function repeat(fn: any) {
const interval = setInterval(fn, 20).unref();
return interval;
}
// Write to a temp file then rename, so stat never sees a 0-byte intermediate
// state (writeFileSync uses O_TRUNC which briefly truncates the file to 0
// bytes, visible to concurrent stat on Windows).
function updateFile(filepath: string, data: string) {
const tmp = filepath + ".tmp";
fs.writeFileSync(tmp, data);
fs.renameSync(tmp, filepath);
}
const encodingFileName = `新建文夹件.txt`;
let testDir = "";
beforeEach(() => {
@@ -54,7 +62,7 @@ describe("fs.watchFile", () => {
let increment = 0;
const interval = repeat(() => {
increment++;
fs.writeFileSync(path.join(testDir, "watch.txt"), "hello" + increment);
updateFile(path.join(testDir, "watch.txt"), "hello" + increment);
});
await promise;
clearInterval(interval);
@@ -79,7 +87,7 @@ describe("fs.watchFile", () => {
let increment = 0;
const interval = repeat(() => {
increment++;
fs.writeFileSync(path.join(testDir, encodingFileName), "hello" + increment);
updateFile(path.join(testDir, encodingFileName), "hello" + increment);
});
await promise;
clearInterval(interval);
@@ -105,7 +113,7 @@ describe("fs.watchFile", () => {
let increment = 0;
const interval = repeat(() => {
increment++;
fs.writeFileSync(path.join(testDir, encodingFileName), "hello" + "a".repeat(increment));
updateFile(path.join(testDir, encodingFileName), "hello" + "a".repeat(increment));
});
await promise;
clearInterval(interval);
@@ -144,7 +152,7 @@ describe("fs.watchFile", () => {
let increment = 0;
const interval = repeat(() => {
increment++;
fs.writeFileSync(filepath, "hello" + increment);
updateFile(filepath, "hello" + increment);
});
await promise;
clearInterval(interval);

View File

@@ -14,6 +14,10 @@ if (libcFamily == "musl") {
// @duckdb/node-bindings does not distribute musl binaries, so we skip this test on musl to avoid CI noise
process.exit(0);
}
if (process.platform === "win32" && process.arch === "arm64") {
// @duckdb/node-bindings does not distribute win32-arm64 binaries
process.exit(0);
}
import { describe, test } from "bun:test";
import assert from "node:assert";

View File

@@ -3,6 +3,10 @@ if (libcFamily == "musl") {
// duckdb does not distribute musl binaries, so we skip this test on musl to avoid CI noise
process.exit(0);
}
if (process.platform === "win32" && process.arch === "arm64") {
// duckdb does not distribute win32-arm64 binaries
process.exit(0);
}
import { describe, expect, test } from "bun:test";
// Must be CJS require so that the above code can exit before we attempt to import DuckDB

View File

@@ -1,11 +1,14 @@
import { spawnSync } from "bun";
import { cc, dlopen } from "bun:ffi";
import { beforeAll, describe, expect, it } from "bun:test";
import { bunEnv, bunExe, isASAN, isWindows } from "harness";
import { bunEnv, bunExe, isArm64, isASAN, isWindows } from "harness";
import { join } from "path";
import source from "./napi-app/ffi_addon_1.c" with { type: "file" };
// TinyCC (and all of bun:ffi) is disabled on Windows ARM64
const isFFIUnavailable = isWindows && isArm64;
const symbols = {
set_instance_data: {
args: ["napi_env", "int"],
@@ -24,6 +27,8 @@ const symbols = {
let addon1, addon2, cc1, cc2;
beforeAll(() => {
if (isFFIUnavailable) return;
// build gyp
const install = spawnSync({
cmd: [bunExe(), "install", "--verbose"],
@@ -59,7 +64,7 @@ beforeAll(() => {
}
});
describe("ffi napi integration", () => {
describe.skipIf(isFFIUnavailable)("ffi napi integration", () => {
it("has a different napi_env for each ffi library", () => {
addon1.set_instance_data(undefined, 5);
addon2.set_instance_data(undefined, 6);
@@ -75,7 +80,7 @@ describe("ffi napi integration", () => {
});
});
describe("cc napi integration", () => {
describe.skipIf(isFFIUnavailable)("cc napi integration", () => {
// fails on windows as TCC can't link the napi_ functions
// TinyCC's setjmp/longjmp error handling conflicts with ASan.
it.todoIf(isWindows || isASAN)("has a different napi_env for each cc invocation", () => {

View File

@@ -14,13 +14,12 @@
"@bufbuild/protobuf": "2.10.2",
"@connectrpc/connect": "2.1.1",
"@connectrpc/connect-node": "2.0.0",
"@duckdb/node-api": "1.1.3-alpha.7",
"@electric-sql/pglite": "0.2.17",
"@fastify/websocket": "11.0.2",
"@grpc/grpc-js": "1.12.0",
"@grpc/proto-loader": "0.7.10",
"@happy-dom/global-registrator": "17.0.3",
"@napi-rs/canvas": "0.1.65",
"@napi-rs/canvas": "0.1.91",
"@nestjs/common": "11.0.3",
"@nestjs/core": "11.0.3",
"@prisma/client": "5.8.0",
@@ -44,7 +43,6 @@
"commander": "12.1.0",
"detect-libc": "2.0.3",
"devalue": "5.1.1",
"duckdb": "1.3.1",
"es-module-lexer": "1.3.0",
"esbuild": "0.18.6",
"express": "4.18.2",
@@ -65,7 +63,6 @@
"jws": "4.0.0",
"lodash": "4.17.21",
"mongodb": "6.0.0",
"msgpackr-extract": "3.0.2",
"msw": "2.3.0",
"mysql2": "3.7.0",
"node-gyp": "10.0.1",
@@ -86,7 +83,7 @@
"reflect-metadata": "0.2.2",
"rollup": "4.4.1",
"sass": "1.79.4",
"sharp": "0.33.0",
"sharp": "0.34.5",
"sinon": "6.0.0",
"socket.io": "4.7.1",
"socket.io-adapter": "2.5.5",
@@ -122,6 +119,11 @@
"bd:v": "(bun run --silent --cwd=../ build:debug &> /tmp/bun.debug.build.log || (cat /tmp/bun.debug.build.log && rm -rf /tmp/bun.debug.build.log && exit 1)) && rm -f /tmp/bun.debug.build.log && ../build/debug/bun-debug",
"bd": "BUN_DEBUG_QUIET_LOGS=1 bun --silent bd:v"
},
"optionalDependencies": {
"duckdb": "1.3.1",
"@duckdb/node-api": "1.1.3-alpha.7",
"msgpackr-extract": "3.0.2"
},
"resolutions": {
"react": "../node_modules/react",
"@types/node": "25.0.0"

View File

@@ -1,6 +1,6 @@
import { afterAll, beforeAll, describe, expect, it } from "bun:test";
import { readFileSync, unlinkSync } from "fs";
import { bunEnv, bunExe, isWindows, tempDirWithFiles } from "harness";
import { bunEnv, bunExe, isArm64, isWindows, tempDirWithFiles } from "harness";
import { join } from "path";
describe.if(isWindows)("PE codesigning integrity", () => {
@@ -215,7 +215,7 @@ console.log("Test data:", JSON.stringify(data));
// Validate PE header
expect(validation.pe.signature).toBe(0x00004550); // "PE\0\0"
expect(validation.pe.machine).toBe(0x8664); // x64
expect(validation.pe.machine).toBe(isArm64 ? 0xaa64 : 0x8664); // arm64 or x64
expect(validation.pe.numberOfSections).toBeGreaterThan(0);
// Validate optional header