Compare commits

...

1 Commits

Author SHA1 Message Date
Claude Bot
fa746d0710 fix(install): remove overly strict assertion for Windows bin path generation
Fixes a crash where bin linking would fail with "Internal assertion failure
at install/bin.zig:690:83" when the relative path from destination to target
doesn't start with "..\" (backslash).

This assertion assumed all Windows bin links would require going up from
the .bin directory, but this isn't true for:
- Global installations with custom BUN_INSTALL paths
- Cross-drive installations
- Same-directory targets

The fix conditionally strips the "..\" prefix when present, but handles
cases where it's not present gracefully.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-13 22:49:33 +00:00
2 changed files with 90 additions and 2 deletions

View File

@@ -687,9 +687,15 @@ pub const Bin = extern struct {
defer bunx_file.close();
const rel_target = path.relativeBufZ(this.rel_buf, path.dirname(abs_dest, .auto), abs_target);
bun.assertWithLocation(strings.hasPrefixComptime(rel_target, "..\\"), @src());
const rel_target_w = strings.toWPathNormalized(&target_buf, rel_target["..\\".len..]);
// For Windows shims, we need a path relative to node_modules, not relative to .bin
// Most cases will start with "..\" to go up from .bin, but global installs may have different structure
const rel_target_for_shim = if (strings.hasPrefixComptime(rel_target, "..\\"))
rel_target["..\\".len..]
else
rel_target;
const rel_target_w = strings.toWPathNormalized(&target_buf, rel_target_for_shim);
const shebang = shebang: {
const first_content_chunk = contents: {

View File

@@ -0,0 +1,82 @@
import { test, expect } from "bun:test";
import { bunEnv, bunExe, tempDirWithFiles } from "harness";
import { join } from "path";
// Regression test for Windows bin linking assertion crash
// https://github.com/oven-sh/bun/issues/XXXX
test("bin linking should not crash on Windows with non-standard paths", async () => {
const testDir = tempDirWithFiles("bin-link-crash", {
"package.json": JSON.stringify({
name: "test-pkg",
version: "1.0.0",
dependencies: {
"dep-with-file-bin": "1.0.0",
},
}),
});
// Install the package - this should not crash with assertion failure
await using proc = Bun.spawn({
cmd: [bunExe(), "install"],
env: bunEnv,
cwd: testDir,
stdout: "pipe",
stderr: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([
proc.stdout.text(),
proc.stderr.text(),
proc.exited,
]);
// Should not crash with "Internal assertion failure at install/bin.zig:690:83"
expect(stderr).not.toContain("Internal assertion failure");
expect(stderr).not.toContain("assertWithLocation");
expect(exitCode).toBe(0);
// Verify the bin was created properly
const binDir = join(testDir, "node_modules", ".bin");
const binExists = await Bun.file(join(binDir, "dep-with-file-bin")).exists();
const bunxExists = await Bun.file(join(binDir, "dep-with-file-bin.bunx")).exists();
if (process.platform === "win32") {
expect(bunxExists).toBe(true);
} else {
expect(binExists).toBe(true);
}
});
// Test with custom global install directory to trigger edge case paths
test("global bin linking should not crash with custom BUN_INSTALL", async () => {
const testDir = tempDirWithFiles("global-bin-crash", {});
const globalInstallDir = join(testDir, "custom-global");
// Try to install a package globally with custom BUN_INSTALL
await using proc = Bun.spawn({
cmd: [bunExe(), "install", "-g", "dep-with-file-bin"],
env: {
...bunEnv,
BUN_INSTALL: globalInstallDir
},
cwd: testDir,
stdout: "pipe",
stderr: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([
proc.stdout.text(),
proc.stderr.text(),
proc.exited,
]);
// Should not crash with assertion failure
expect(stderr).not.toContain("Internal assertion failure");
expect(stderr).not.toContain("assertWithLocation");
// Note: exit code might be non-zero due to network/registry issues,
// but it should not be an assertion crash
if (exitCode !== 0) {
expect(stderr).not.toContain("panic:");
}
});