mirror of
https://github.com/oven-sh/bun
synced 2026-02-16 22:01:47 +00:00
## Summary This PR adds a new `--compile-argv` option to `bun build --compile` that allows developers to embed runtime arguments into standalone executables. The specified arguments are stored in the executable metadata during compilation and provide **dual functionality**: 1. **🔧 Actually processed by Bun runtime** (like passing them on command line) 2. **📊 Available in `process.execArgv`** (for application inspection) This means flags like `--user-agent`, `--smol`, `--max-memory` will actually take effect AND be visible to your application! ## Motivation & Use Cases ### 1. **Global User Agent for Web Scraping** Perfect for @thdxr's opencode use case - the user agent actually gets applied: ```bash # Compile with custom user agent that ACTUALLY works bun build --compile --compile-argv="--user-agent='OpenCode/1.0'" ./scraper.ts --outfile=opencode # The user agent is applied by Bun runtime AND visible in execArgv ./opencode # All HTTP requests use the custom user agent! ``` ### 2. **Memory-Optimized Builds** Create builds with actual runtime memory optimizations: ```bash # Compile with memory optimization that ACTUALLY takes effect bun build --compile --compile-argv="--smol --max-memory=512mb" ./app.ts --outfile=app-optimized # Bun runtime actually runs in smol mode with memory limit ``` ### 3. **Performance & Debug Builds** Different builds with different runtime characteristics: ```bash # Production: optimized for memory bun build --compile --compile-argv="--smol --gc-frequency=high" ./app.ts --outfile=app-prod # Debug: with inspector enabled bun build --compile --compile-argv="--inspect=0.0.0.0:9229" ./app.ts --outfile=app-debug ``` ### 4. **Security & Network Configuration** Embed security settings that actually apply: ```bash # TLS and network settings that work bun build --compile --compile-argv="--tls-min-version=1.3 --dns-timeout=5000" ./secure-app.ts ``` ## How It Works ### Dual Processing Architecture The implementation provides both behaviors: ```bash # Compiled with: --compile-argv="--smol --user-agent=Bot/1.0" ./my-app --config=prod.json ``` **What happens:** 1. **🔧 Runtime Processing**: Bun processes `--smol` and `--user-agent=Bot/1.0` as if passed on command line 2. **📊 Application Access**: Your app can inspect these via `process.execArgv` ```javascript // In your compiled application: // 1. The flags actually took effect: // - Bun is running in smol mode (--smol processed) // - All HTTP requests use Bot/1.0 user agent (--user-agent processed) // 2. You can also inspect what flags were used: console.log(process.execArgv); // ["--smol", "--user-agent=Bot/1.0"] console.log(process.argv); // ["./my-app", "--config=prod.json"] // 3. Your application logic can adapt: if (process.execArgv.includes("--smol")) { console.log("Running in memory-optimized mode"); } ``` ### Implementation Details 1. **Build Time**: Arguments stored in executable metadata 2. **Runtime Startup**: - Arguments prepended to actual argv processing (so Bun processes them) - Arguments also populate `process.execArgv` (so app can inspect them) 3. **Result**: Flags work as if passed on command line + visible to application ## Example Usage ```bash # User agent that actually works bun build --compile --compile-argv="--user-agent='MyBot/1.0'" ./scraper.ts --outfile=scraper # Memory optimization that actually applies bun build --compile --compile-argv="--smol --max-memory=256mb" ./microservice.ts --outfile=micro # Debug build with working inspector bun build --compile --compile-argv="--inspect=127.0.0.1:9229" ./app.ts --outfile=app-debug # Multiple working flags bun build --compile --compile-argv="--smol --user-agent=Bot/1.0 --tls-min-version=1.3" ./secure-scraper.ts ``` ## Runtime Verification ```javascript // Check what runtime flags are active const hasSmol = process.execArgv.includes("--smol"); const userAgent = process.execArgv.find(arg => arg.startsWith("--user-agent="))?.split("=")[1]; const maxMemory = process.execArgv.find(arg => arg.startsWith("--max-memory="))?.split("=")[1]; console.log("Memory optimized:", hasSmol); console.log("User agent:", userAgent); console.log("Memory limit:", maxMemory); // These flags also actually took effect in the runtime! ``` ## Changes Made ### Core Implementation - **Arguments.zig**: Added `--compile-argv <STR>` flag with validation - **StandaloneModuleGraph.zig**: Serialization/deserialization for `compile_argv` - **build_command.zig**: Pass `compile_argv` to module graph - **cli.zig**: **Prepend arguments to actual argv processing** (so Bun processes them) - **node_process.zig**: **Populate `process.execArgv`** from stored arguments - **bun.zig**: Made `appendOptionsEnv()` public for reuse ### Testing - **expectBundled.ts**: Added `compileArgv` test support - **compile-argv.test.ts**: Tests verifying dual behavior ## Behavior ### Complete Dual Functionality ```javascript // With --compile-argv="--smol --user-agent=TestBot/1.0": // ✅ Runtime flags actually processed by Bun: // - Memory usage optimized (--smol effect) // - HTTP requests use TestBot/1.0 user agent (--user-agent effect) // ✅ Flags visible to application: process.execArgv // ["--smol", "--user-agent=TestBot/1.0"] process.argv // ["./app", ...script-args] (unchanged) ``` ## Backward Compatibility - ✅ Purely additive feature - no breaking changes - ✅ Optional flag - existing behavior unchanged when not used - ✅ No impact on non-compile builds ## Perfect for @thdxr's Use Case! ```bash # Compile opencode with working user agent bun build --compile --compile-argv="--user-agent='OpenCode/1.0'" ./opencode.ts --outfile=opencode # Results in: # 1. All HTTP requests actually use OpenCode/1.0 user agent ✨ # 2. process.execArgv contains ["--user-agent=OpenCode/1.0"] for inspection ✨ ``` The user agent will actually work in all HTTP requests made by the compiled executable, not just be visible as metadata! 🚀 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude Bot <claude-bot@bun.sh> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Claude <claude@anthropic.ai>
72 lines
2.4 KiB
TypeScript
72 lines
2.4 KiB
TypeScript
import { expect, test } from "bun:test";
|
|
import { bunEnv, bunExe, tempDirWithFiles } from "harness";
|
|
import { join } from "path";
|
|
|
|
test("process.execArgv should be empty in compiled executables and argv should work correctly", async () => {
|
|
const dir = tempDirWithFiles("process-execargv-compile", {
|
|
"check-execargv.js": `
|
|
console.log(JSON.stringify({
|
|
argv: process.argv,
|
|
execArgv: process.execArgv,
|
|
}));
|
|
`,
|
|
});
|
|
|
|
// First test regular execution - execArgv should be empty for script args
|
|
{
|
|
await using proc = Bun.spawn({
|
|
cmd: [bunExe(), join(dir, "check-execargv.js"), "-a", "--b", "arg1", "arg2"],
|
|
env: bunEnv,
|
|
cwd: dir,
|
|
stdout: "pipe",
|
|
});
|
|
|
|
const result = JSON.parse(await proc.stdout.text());
|
|
expect(result.execArgv).toEqual([]);
|
|
|
|
// Verify argv structure: [executable, script, ...userArgs]
|
|
expect(result.argv.length).toBeGreaterThanOrEqual(4);
|
|
expect(result.argv[result.argv.length - 4]).toBe("-a");
|
|
expect(result.argv[result.argv.length - 3]).toBe("--b");
|
|
expect(result.argv[result.argv.length - 2]).toBe("arg1");
|
|
expect(result.argv[result.argv.length - 1]).toBe("arg2");
|
|
}
|
|
|
|
// Build compiled executable
|
|
{
|
|
await using buildProc = Bun.spawn({
|
|
cmd: [bunExe(), "build", "--compile", "check-execargv.js", "--outfile=check-execargv"],
|
|
env: bunEnv,
|
|
cwd: dir,
|
|
});
|
|
|
|
expect(await buildProc.exited).toBe(0);
|
|
}
|
|
|
|
// Test compiled executable - execArgv should be empty, argv should work normally
|
|
{
|
|
await using proc = Bun.spawn({
|
|
cmd: [join(dir, "check-execargv"), "-a", "--b", "arg1", "arg2"],
|
|
env: bunEnv,
|
|
cwd: dir,
|
|
stdout: "pipe",
|
|
});
|
|
|
|
const result = JSON.parse(await proc.stdout.text());
|
|
|
|
// The fix: execArgv should be empty in compiled executables (no --compile-argv was used)
|
|
expect(result.execArgv).toEqual([]);
|
|
|
|
// argv should contain: ["bun", script_path, ...userArgs]
|
|
expect(result.argv.length).toBe(6);
|
|
expect(result.argv[0]).toBe("bun");
|
|
// The script path contains "check-execargv" and uses platform-specific virtual paths
|
|
// Windows: B:\~BUN\..., Unix: /$bunfs/...
|
|
expect(result.argv[1]).toContain("check-execargv");
|
|
expect(result.argv[2]).toBe("-a");
|
|
expect(result.argv[3]).toBe("--b");
|
|
expect(result.argv[4]).toBe("arg1");
|
|
expect(result.argv[5]).toBe("arg2");
|
|
}
|
|
});
|