mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 10:28:47 +00:00
## Summary This PR improves the correctness of bin linking by atomically normalizing `\r\n` to `\n` in shebang lines when linking bins. ### Changes - **Refactored shebang normalization in `src/install/bin.zig`**: - Extracted logic into separate `tryNormalizeShebang` function - Changed from in-place file modification to atomic file replacement - Reads entire file, creates temporary file with corrected shebang, then atomically renames - Properly cleans up temporary files on errors - **Added test coverage**: - New test file `test/cli/install/shebang-normalize.test.ts` verifies CRLF normalization works correctly - Modified existing test in `bun-link.test.ts` to use Python script with CRLF shebang ### Why The previous implementation modified files in-place by seeking to the `\r` position and overwriting with `\n`. This could potentially corrupt files if interrupted mid-write. The new atomic approach ensures file integrity by writing to a temporary file first, then renaming it to replace the original. ## Test plan - ✅ `bun bd test test/cli/install/shebang-normalize.test.ts` - passes - ✅ Verified bins with CRLF shebangs are normalized to LF during linking - ✅ Code compiles successfully 🤖 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> Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
70 lines
2.0 KiB
TypeScript
70 lines
2.0 KiB
TypeScript
import { spawn } from "bun";
|
|
import { expect, test } from "bun:test";
|
|
import { mkdir, readFile, stat, writeFile } from "fs/promises";
|
|
import { bunExe, bunEnv as env, isWindows, runBunInstall, tmpdirSync } from "harness";
|
|
import { join } from "path";
|
|
|
|
test.skipIf(isWindows)("bin linking normalizes CRLF in shebang", async () => {
|
|
const testDir = tmpdirSync();
|
|
const pkgDir = join(testDir, "pkg");
|
|
const consumerDir = join(testDir, "consumer");
|
|
|
|
await mkdir(pkgDir, { recursive: true });
|
|
await mkdir(consumerDir, { recursive: true });
|
|
|
|
// Create package with bin that has CRLF shebang
|
|
await writeFile(
|
|
join(pkgDir, "package.json"),
|
|
JSON.stringify({
|
|
name: "test-pkg-crlf",
|
|
version: "1.0.0",
|
|
bin: {
|
|
"test-bin": "test-bin.py",
|
|
},
|
|
}),
|
|
);
|
|
|
|
// Write bin file with CRLF shebang
|
|
await writeFile(join(pkgDir, "test-bin.py"), "#!/usr/bin/env python\r\nprint('hello from python')");
|
|
|
|
// Link the package
|
|
const linkResult = spawn({
|
|
cmd: [bunExe(), "link"],
|
|
cwd: pkgDir,
|
|
env,
|
|
stdout: "pipe",
|
|
stderr: "pipe",
|
|
});
|
|
|
|
await linkResult.exited;
|
|
expect(linkResult.exitCode).toBe(0);
|
|
|
|
// Create consumer package
|
|
await writeFile(
|
|
join(consumerDir, "package.json"),
|
|
JSON.stringify({
|
|
name: "consumer",
|
|
version: "1.0.0",
|
|
dependencies: {
|
|
"test-pkg-crlf": "link:test-pkg-crlf",
|
|
},
|
|
}),
|
|
);
|
|
|
|
// Install
|
|
await runBunInstall(env, consumerDir);
|
|
|
|
// Check that the linked bin file has normalized shebang
|
|
const binPath = join(consumerDir, "node_modules", "test-pkg-crlf", "test-bin.py");
|
|
const binContent = await readFile(binPath, "utf-8");
|
|
|
|
console.log("Bin content first 50 chars:", JSON.stringify(binContent.slice(0, 50)));
|
|
|
|
expect(binContent).toStartWith("#!/usr/bin/env python\nprint");
|
|
expect(binContent).not.toContain("\r\n");
|
|
|
|
// Verify that the file is executable (bin linking sets this)
|
|
const binStat = await stat(binPath);
|
|
expect(binStat.mode & 0o111).toBeGreaterThan(0); // At least one execute bit should be set
|
|
});
|