## Summary
- Aligns Bun's temp directory resolution with Node.js's `os.tmpdir()`
behavior
- Checks `TMPDIR`, `TMP`, and `TEMP` environment variables in order
(matching Node.js)
- Uses `bun.once` for lazy initialization instead of mutable static
state
- Removes `setTempdir` function and simplifies the API to use
`RealFS.tmpdirPath()` directly
## Test plan
- [ ] Verify temp directory resolution matches Node.js behavior
- [ ] Test with various environment variable configurations
- [ ] Ensure existing tests pass with `bun bd test`
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
Include the Windows module definition file (symbols.def) in
link-metadata.json, similar to how
symbols.txt and symbols.dyn are already included for macOS and Linux.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
## Summary
- Remove sccache support entirely, use ccache only
- Missing ccache no longer fails the build (just skips caching)
- Remove S3 distributed cache support
## Changes
- Remove `cmake/tools/SetupSccache.cmake` and S3 distributed cache
support
- Simplify `CMakeLists.txt` to only use ccache
- Update `SetupCcache.cmake` to not fail when ccache is missing
- Replace sccache with ccache in bootstrap scripts (sh, ps1)
- Update `.buildkite/Dockerfile` to install ccache instead of sccache
- Update `flake.nix` and `shell.nix` to use ccache
- Update documentation (CONTRIBUTING.md, contributing.mdx,
building-windows.mdx)
- Remove `scripts/build-cache/` directory (was only for sccache S3
access)
## Test plan
- [x] Build completes successfully with `bun bd`
🤖 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>
## Summary
- Fix LLVM installation on Debian Trixie (13) by using the unstable
repository from apt.llvm.org
The `llvm.sh` script doesn't automatically detect that Debian Trixie
needs to use the unstable repository. This is because trixie's `VERSION`
is `13 (trixie)` rather than `testing`, and apt.llvm.org doesn't have a
dedicated trixie repository.
Without this fix, the LLVM installation falls back to Debian's main
repository packages, which don't include `libclang-rt-19-dev` (the
compiler-rt sanitizer libraries) by default. This causes builds with
ASan (AddressSanitizer) to fail with:
```
ld.lld: error: cannot open /usr/lib/llvm-19/lib/clang/19/lib/x86_64-pc-linux-gnu/libclang_rt.asan.a: No such file or directory
```
This was breaking the [Daily Docker
Build](https://github.com/oven-sh/bun-development-docker-image/actions/runs/20437290601)
in the bun-development-docker-image repo.
## Test plan
- [ ] Wait for the PR CI to pass
- [ ] After merging, the next Daily Docker Build should succeed
🤖 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>
## Summary
- Add `bun_version` field to `link-metadata.json`
- Pass `VERSION` CMake variable to the metadata script as `BUN_VERSION`
env var
This ensures the build version is captured in the link metadata JSON
file, which is useful for tracking which version produced a given build
artifact.
## Test plan
- Build with `bun bd` and verify `link-metadata.json` includes
`bun_version`
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
pulled out of https://github.com/oven-sh/bun/pull/21809
- brings the ASAN behavior on linux closer in sync with macos
- fixes some tests to also pass in node
---------
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
## Summary
Fixed two bugs in the auto-close-duplicates bot:
- **Respect 👎 reactions from ANY user**: Previously only the issue
author's thumbs down would prevent auto-closing. Now any user can
indicate disagreement with the duplicate detection.
- **Don't re-close reopened issues**: The bot now checks if an issue was
previously reopened and skips auto-closing to respect user intent.
## Changes
1. Modified `fetchAllReactions` call to check all reactions, not just
the author's
2. Changed `authorThumbsDown` logic to `hasThumbsDown` (checks any
user's reaction)
3. Added `wasIssueReopened()` function to query issue events timeline
4. Added check to skip issues with "reopened" events in their history
## Test plan
- [ ] Manually test the script doesn't close issues with 👎 reactions
from non-authors
- [ ] Verify reopened issues are not auto-closed again
- [ ] Check that legitimate duplicates without objections still get
closed properly
🤖 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>
exploratory look into https://github.com/oven-sh/bun/issues/1524
this still leaves that far off from being closed but an important first
step
this is important because this script is used to spawn our base images
for CI and will provide boxes for local testing
not sure how far i'll get but a rough "road to freebsd" map for anyone
reading:
- [x] this
- [ ] ensure `bootstrap.sh` can run successfully
- [ ] ensure WebKit can build from source
- [ ] ensure other dependencies can build from source
- [ ] add freebsd to our WebKit fork releases
- [ ] add freebsd to our Zig fork releases
- [ ] ensure bun can build from source
- [ ] run `[build images]` and add freebsd to CI
- [ ] fix runtime test failures
<img width="2072" height="956" alt="image"
src="https://github.com/user-attachments/assets/ea1acf45-b746-4ffa-8043-be674b87bb60"
/>
## Summary
This PR adds a Claude Code-powered issue deduplication system to help
reduce duplicate issues in the Bun repository.
### What's included:
1. **`/dedupe` slash command** (`.claude/commands/dedupe.md`)
- Claude Code command to find up to 3 duplicate issues for a given
GitHub issue
- Uses parallel agent searches with diverse keywords
- Filters out false positives
2. **Automatic dedupe on new issues**
(`.github/workflows/claude-dedupe-issues.yml`)
- Runs automatically when a new issue is opened
- Can also be triggered manually via workflow_dispatch
- Uses the Claude Code base action to run the `/dedupe` command
3. **Auto-close workflow**
(`.github/workflows/auto-close-duplicates.yml`)
- Runs daily to close issues marked as duplicates after 3 days
- Only closes if:
- Issue has a duplicate detection comment from bot
- Comment is 3+ days old
- No comments or activity after duplicate comment
- Author hasn't reacted with 👎 to the duplicate comment
4. **Auto-close script** (`scripts/auto-close-duplicates.ts`)
- TypeScript script that handles the auto-closing logic
- Fetches open issues and checks for duplicate markers
- Closes issues with proper labels and notifications
### How it works:
1. When a new issue is opened, the workflow runs Claude Code to analyze
it
2. Claude searches for duplicates and comments on the issue if any are
found
3. Users have 3 days to respond if they disagree
4. After 3 days with no activity, the issue is automatically closed
### Requirements:
- `ANTHROPIC_API_KEY` secret needs to be set in the repository settings
for the dedupe workflow to run
## Test plan
- [x] Verified workflow files have correct syntax
- [x] Verified script references correct repository (oven-sh/bun)
- [x] Verified slash command matches claude-code implementation
- [ ] Test workflow manually with workflow_dispatch (requires
ANTHROPIC_API_KEY)
- [ ] Monitor initial runs to ensure proper behavior
🤖 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>
adds environment variable for proper tmpdir setup
actual fix for
d2a4fb8124
(which was reverted)
this fixes flakyness in node:fs and node:cluster when using
scripts/runner.node.mjs locally with the --parallel flag
### What does this PR do?
Enable compiler flags
Update SQLite amalgamation using https://www.sqlite.org/download.html
source code
[sqlite-src-3500400.zip](https://www.sqlite.org/2025/sqlite-src-3500400.zip)
with:
```bash
./configure CFLAGS="-DSQLITE_ENABLE_UPDATE_DELETE_LIMIT"
make sqlite3.c
```
This is the same version that before just with this adicional flag that
must be enabled when generating the amalgamation so we are actually able
to use this option. You can also see that without this the build will
happen but the feature will not be enable
https://buildkite.com/bun/bun/builds/27940, as informed in
https://www.sqlite.org/howtocompile.html topic 5.
### How did you verify your code works?
Add in CI two tests that check if the feature is enabled on windows
---------
Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude <noreply@anthropic.com>
# bun test
Fixes#8768, Fixes#14624, Fixes#20100, Fixes#19875, Fixes#14135,
Fixes#20980, Fixes#21830, Fixes#5738, Fixes#19758, Fixes#12782,
Fixes#5585, Fixes#9548, Might fix 5996
# New features:
## Concurrent tests
Concurrent tests allow running multiple async tests at the same time.
```ts
// concurrent.test.ts
test.concurrent("this takes a while 1", async () => {
await Bun.sleep(1000);
});
test.concurrent("this takes a while 2", async () => {
await Bun.sleep(1000);
});
test.concurrent("this takes a while 3", async () => {
await Bun.sleep(1000);
});
```
Without `.concurrent`, this test file takes 3 seconds to run because
each one has to wait for the one before it to finish before it can
start.
With `.concurrent`, this file takes 1 second because all three sleeps
can run at once.
```
$> bun-after test concurrent
concurrent.test.js:
✓ this takes a while 1 [1005.36ms]
✓ this takes a while 2 [1012.51ms]
✓ this takes a while 3 [1013.15ms]
3 pass
0 fail
Ran 3 tests across 1 file. [1081.00ms]
```
To run all tests as concurrent, pass the `--concurrent` flag when
running tests.
Limitations:
- concurrent tests cannot attribute `expect()` call counts to the test,
meaning `expect.assertions()` does not function
- concurrent tests cannot use `toMatchSnapshot`. `toMatchInlineSnapshot`
is still supported.
- `beforeAll`/`afterAll` will never be executed concurrently.
`beforeEach`/`afterEach` will.
## Chaining
Chaining multiple describe/test qualifiers is now allowed. Previously,
it would fail.
```ts
// chaining-test-qualifiers.test.ts
test.failing.each([1, 2, 3])("each %i", async i => {
throw new Error(i);
});
```
```
$> bun-after test chaining-test-qualifiers
a.test.js:
✓ each 1
✓ each 2
✓ each 3
```
# Breaking changes:
## Describe ordering
Previously, describe callbacks were called immediately. Now, they are
deferred until the outer callback has finished running. The previous
order matched Jest. The new order is similar to Vitest, but does not
match exactly.
```ts
// describe-ordering.test.ts
describe("outer", () => {
console.log("outer before");
describe("inner", () => {
console.log("inner");
});
console.log("outer after");
});
```
Before, this would print
```
$> bun-before test describe-ordering
outer before
inner
outer after
```
Now, this will print
```
$> bun-after test describe-ordering
outer before
outer after
inner
```
## Test ordering
Describes are no longer always called before tests. They are now in
order.
```ts
// test-ordering.test.ts
test("one", () => {});
describe("scope", () => {
test("two", () => {});
});
test("three", () => {});
```
Before, this would print
```
$> bun-before test test-ordering
✓ scope > two
✓ one
✓ three
```
Now, this will print
```
$> bun-after test test-ordering
✓ one
✓ scope > two
✓ three
```
## Preload hooks
Previously, beforeAll in a preload ran before the first file and
afterAll ran after the last file. Now, beforeAll will run at the start
of each file and afterAll will run at the end of each file. This
behaviour matches Jest and Vitest.
```ts
// preload.ts
beforeAll(() => console.log("preload: beforeAll"));
afterAll(() => console.log("preload: afterAll"));
```
```ts
// preload-ordering-1.test.ts
test("demonstration file 1", () => {});
```
```ts
// preload-ordering-2.test.ts
test("demonstration file 2", () => {});
```
```
$> bun-before test --preload=./preload preload-ordering
preload-ordering-1.test.ts:
preload: beforeAll
✓ demonstration file 1
preload-ordering-2.test.ts:
✓ demonstration file 2
preload: afterAll
```
```
$> bun-after test --preload=./preload preload-ordering
preload-ordering-1.test.ts:
preload: beforeAll
✓ demonstration file 1
preload: afterAll
preload-ordering-2.test.ts:
preload: beforeAll
✓ demonstration file 2
preload: afterAll
```
## Describe failures
Current behaviour is that when an error is thrown inside a describe
callback, none of the tests declared there will run. Now, describes
declared inside will also not run. The new behaviour matches the
behaviour of Jest and Vitest.
```ts
// describe-failures.test.ts
describe("erroring describe", () => {
test("this test does not run because its describe failed", () => {
expect(true).toBe(true);
});
describe("inner describe", () => {
console.log("does the inner describe callback get called?");
test("does the inner test run?", () => {
expect(true).toBe(true);
});
});
throw new Error("uh oh!");
});
```
Before, the inner describe callback would be called and the inner test
would run, although the outer test would not:
```
$> bun-before test describe-failures
describe-failures.test.ts:
does the inner describe callback get called?
# Unhandled error between tests
-------------------------------
11 | throw new Error("uh oh!");
^
error: uh oh!
-------------------------------
✓ erroring describe > inner describe > does the inner test run?
1 pass
0 fail
1 error
1 expect() calls
Ran 1 test across 1 file.
Exited with code [1]
```
Now, the inner describe callback is not called at all.
```
$> bun-after test describe-failures
describe-failures.test.ts:
# Unhandled error between tests
-------------------------------
11 | throw new Error("uh oh!");
^
error: uh oh!
-------------------------------
0 pass
0 fail
1 error
Ran 0 tests across 1 file.
Exited with code [1]
```
## Hook failures
Previously, a beforeAll failure would skip subsequent beforeAll()s, the
test, and the afterAll. Now, a beforeAll failure skips any subsequent
beforeAll()s and the test, but not the afterAll.
```js
beforeAll(() => {
throw new Error("before all: uh oh!");
});
test("my test", () => {
console.log("my test");
});
afterAll(() => console.log("after all"));
```
```
$> bun-before test hook-failures
Error: before all: uh oh!
$> bun-after test hook-failures
Error: before all: uh oh!
after all
```
Previously, an async beforeEach failure would still allow the test to
run. Now, an async beforeEach failure will prevent the test from running
```js
beforeEach(() => {
await 0;
throw "uh oh!";
});
it("the test", async () => {
console.log("does the test run?");
});
```
```
$> bun-before test async-beforeeach-failure
does the test run?
error: uh oh!
uh oh!
✗ the test
$> bun-after test async-beforeeach-failure
error: uh oh!
uh oh!
✗ the test
```
## Hook timeouts
Hooks will now time out, and can have their timeout configured in an
options parameter
```js
beforeAll(async () => {
await Bun.sleep(1000);
}, 500);
test("my test", () => {
console.log("ran my test");
});
```
```
$> bun-before test hook-timeouts
ran my test
Ran 1 test across 1 file. [1011.00ms]
$> bun-after test hook-timeouts
✗ my test [501.15ms]
^ a beforeEach/afterEach hook timed out for this test.
```
## Hook execution order
beforeAll will now execute before the tests in the scope, rather than
immediately when it is called.
```ts
describe("d1", () => {
beforeAll(() => {
console.log("<d1>");
});
test("test", () => {
console.log(" test");
});
afterAll(() => {
console.log("</d1>");
});
});
describe("d2", () => {
beforeAll(() => {
console.log("<d2>");
});
test("test", () => {
console.log(" test");
});
afterAll(() => {
console.log("</d2>");
});
});
```
```
$> bun-before test ./beforeall-ordering.test.ts
<d1>
<d2>
test
</d1>
test
</d2>
$> bun-after test ./beforeall-ordering.test.ts
<d1>
test
</d1>
<d2>
test
</d2>
```
## test inside test
test() inside test() now errors rather than silently failing. Support
for this may be added in the future.
```ts
test("outer", () => {
console.log("outer");
test("inner", () => {
console.log("inner");
});
});
```
```
$> bun-before test
outer
✓ outer [0.06ms]
1 pass
0 fail
Ran 1 test across 1 file. [8.00ms]
$> bun-after test
outer
1 | test("outer", () => {
2 | console.log("outer");
3 | test("inner", () => {
^
error: Cannot call test() inside a test. Call it inside describe() instead.
✗ outer [0.71ms]
0 pass
1 fail
```
## afterAll inside test
afterAll inside a test is no longer allowed
```ts
test("test 1", () => {
afterAll(() => console.log("afterAll"));
console.log("test 1");
});
test("test 2", () => {
console.log("test 2");
});
```
```
$> bun-before
test 1
✓ test 1 [0.05ms]
test 2
✓ test 2
afterAll
$> bun-after
error: Cannot call afterAll() inside a test. Call it inside describe() instead.
✗ test 1 [1.00ms]
test 2
✓ test 2 [0.20ms]
```
# Only inside only
Previously, an outer 'describe.only' would run all tests inside it even
if there was an inner 'test.only'. Now, only the innermost only tests
are executed.
```ts
describe.only("outer", () => {
test("one", () => console.log("should not run"));
test.only("two", () => console.log("should run"));
});
```
```
$> bun-before test
should not run
should run
$> bun-after test
should run
```
With no inner only, the outer only will still run all tests:
```ts
describe.only("outer", () => {
test("test 1", () => console.log("test 1 runs"));
test("test 2", () => console.log("test 2 runs"));
});
```
# Potential follow-up work
- [ ] for concurrent tests, display headers before console.log messages
saying which test it is for
- this will need async context or similar
- refActiveExecutionEntry should also be able to know the current test
even in test.concurrent
- [ ] `test("rerun me", () => { console.log("run one time!"); });`
`--rerun-each=3` <- this runs the first and third time but not the
second time. fix.
- [ ] should to cache the JSValue created from
DoneCallback.callAsFunction
- [ ] implement retry and rerun params for tests.
- [ ] Remove finalizer on ScopeFunctions.zig by storing the data in 3
jsvalues passed in bind rather than using a custom class. We should also
migrate off of the ClassGenerator for ScopeFunctions
- [ ] support concurrent limit, how many concurrent tests are allowed to
run at a time. ie `--concurrent-limit=25`
- [ ] flag to run tests in random order
- [ ] `test.failing` should have its own style in the same way
`test.todo` passing marks as 'todo' insetead of 'passing'. right now
it's `✓` which is confusing.
- [ ] remove all instances of bun.jsc.Jest.Jest.current
- [ ] test options should be in BunTestRoot
- [ ] we will need one global still, stored in the globalobject/vm/?.
but it should not be a Jest instance.
- [ ] consider allowing test() inside test(), as well as afterEach and
afterAll. could even allow describe() too. to do this we would switch
from indices to pointers and they would be in a linked list. they would
be allocated in memorypools for perf/locality. some special
consideration is needed for making sure repeated tests lose their
temporary items. this could also improve memory usage soomewhat.
- [ ] consider using a jsc Bound Function rather than CallbackWithArgs.
bound functions allow adding arguments and they are only one value for
GC instead of many. and this removes our unnecessary three copies.
- [ ] eliminate Strong.Safe. we should be using a C++ class instead.
- [ ] consider modifying the junit reporter to print the whole describe
tree at the end instead of trying to output as test results come in. and
move it into its own file.
- [ ] expect_call_count/expect_assertions is confusing. rename to
`expect_calls`, `assert_expect_calls`. or something.
- [ ] Should make line_no be an enum with a none option and a function
to get if line nombers are enabled
- [ ] looks like we don't need to use file_id anymore (remove
`bun.jsc.Jest.Jest.runner.?.getOrPutFile(file_path).file_id;`, store the
file path directly)
- [ ] 'dot' test reporter like vitest?
- [ ] `test.failing.if(false)` errors because it can't replace mode
'failing' with mode 'skip'. this should probably be allowed instead.
- [ ] trigger timeout termination exception for `while(true) {}`
- [ ] clean up unused callbacks. as soon as we advance to the next
execution group, we can fully clean out the previous one. sometimes
within an execution sequence we can do the same.
- clean by swapping held values with undefined
- [ ] structure cache for performance for donecallback/scopefunctions
- [ ] consider migrating CallbackWithArgs to be a bound function. the
length of the bound function can exclude the specified args.
- [ ] setting both result and maybe_skip is not ideal, maybe there
should be a function to do both at once?
- [ ] try using a linked list rather than arraylist for describe/test
children, see how it affects performance
- [ ] consider a memory pool for describescope/executionentry. test if
it improves performance.
- [ ] consider making RefDataValue methods return the reason for failure
rather than ?value. that way we can improve error messages. the reason
could be a string or it could be a defined error set
- [ ] instead of 'description orelse (unnamed)', let's have description
default to 'unnamed' and not free it if it === the global that defines
that
- [ ] Add a phase before ordering results that inherits properties to
the parents. (eg inherit only from the child and inherit has_callback
from the child. and has_callback can be on describe/test individually
rather than on base). then we won't have that happening in an init()
function (terrible!)
- [ ] this test was incidentally passing because resolves.pass() wasn't
waiting for promise
```
test("fetching with Request object - issue #1527", async () => {
const server = createServer((req, res) => {
res.end();
}).listen(0);
try {
await once(server, "listening");
const body = JSON.stringify({ foo: "bar" });
const request = new Request(`http://localhost:${server.address().port}`,
{
method: "POST",
body,
});
expect(fetch(request)).resolves.pass();
} finally {
server.closeAllConnections();
}
});
```
- [ ] the error "expect.assertions() is not supported in the describe
phase, in concurrent tests, between tests, or after test execution has
completed" is not very good. we should be able to identify which of
those it is and print the right error for the context
- [ ] consider: instead of storing weak pointers to BunTest, we can
instead give the instance an id and check that it is correct when
getting the current bun test instance from the ref
- [ ] auto_killer: add three layers of auto_killer:
- preload (includes file & test)
- file (includes test)
- test
- that way at the end of the test, we kill the test processes. at the
end of the file, we kill the file processes. at the end of all, we kill
anything remaining.
AsyncLocalStorage
- store active_id & refdatavalue. active_id is a replacement for the
above weak pointers thing. refdatavalue is for determining which test it
is. this probably fits in 2×u64
- use for auto_killer so timeouts can kill even in concurrent tests
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
tests not in `test/no-validate-leaksan.txt` will run with leaksanitizer
in CI
leaks documented in `test/leaksan.supp` will not cause a test failure
-- notes about leaksanitizer
- will not catch garbage collected objects accumulated during
long-running processes
- will not catch js objects (eg a strong held to a promise)
- will catch native calls to `malloc` not `free`d
- will catch allocations made in C, Zig, C++, libc, dependencies,
dlopen'd
---------
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>