mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-21 16:02:28 +00:00
fix(bun): pass --preserve-symlinks on unbundled execution (#9147)
* fix(bun): pass --preserve-symlinks on unbundled execution Bun 1.2/1.3 moved its global package cache to a content-addressed layout and the installer now creates a single directory symlink from node_modules/<pkg> to the cache entry. Without --preserve-symlinks, Bun resolves modules from each file's realpath, so any require/import inside an installed package walks up from cache_nomount/bun/... and never finds the sibling deps living under <job_dir>/node_modules/. This manifested as e.g. ENOENT while resolving package 'zod/v3' from '/tmp/windmill/cache_nomount/bun/@langchain/core@1.1.44@@@1/dist/...' on //nobundling scripts that pull @langchain/core, even though zod is correctly installed alongside it in node_modules. The bundled execution path already had --preserve-symlinks since #4132 (needed because we symlink the cached bundle file into the job dir). The unbundled path didn't, because at the time Bun installed via per- file hardlinks and the realpath of node_modules entries was the job dir itself. The Bun installer's layout change made the flag necessary on the unbundled path as well. Add the flag to all three unbundled `bun run` invocations: - nsjail unbundled path - non-nsjail unbundled path - dedicated worker (always unbundled) This also fixes a latent bug on the first run of any bun script that imports a package whose internals reference siblings (the build_cache path runs unbundled this round while it builds the bundle for next time). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * test(bun): regression test for nobundling + transitive require resolution Adds an integration test that mirrors the original failure: a //nobundling script importing @langchain/core, which (in its CJS internals) does require('zod/v3'). Before --preserve-symlinks was added to the unbundled bun run invocations, this failed with: ENOENT while resolving package 'zod/v3' from '.../cache_nomount/bun/@langchain/core@<ver>@@@1/dist/runnables/base.js' The test covers the non-nsjail unbundled path. Reproducibility of the pre-fix failure depends on Bun's installer choosing the directory-symlink layout for the node_modules entry (the default on Bun 1.2/1.3+ with the new content-addressed global cache that produced the user's error). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -577,6 +577,63 @@ export function main() {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Regression test: a `//nobundling` script that pulls a package whose CJS
|
||||
/// internals do bare-specifier `require()` of a sibling dependency.
|
||||
///
|
||||
/// Before the `--preserve-symlinks` fix, Bun 1.2/1.3+ would follow the
|
||||
/// directory symlink in `node_modules/@langchain/core` to its global cache
|
||||
/// entry, walk parent dirs from the cache realpath, and fail to find
|
||||
/// `node_modules/zod` — producing:
|
||||
/// ENOENT while resolving package 'zod/v3' from
|
||||
/// '.../cache_nomount/bun/@langchain/core@<ver>@@@1/dist/runnables/base.js'
|
||||
///
|
||||
/// The fix passes `--preserve-symlinks` so Bun resolves from the
|
||||
/// symlink path under `<job_dir>/node_modules/`, where `zod` is a sibling.
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
async fn test_bun_nobundling_transitive_require(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
initialize_tracing().await;
|
||||
let server = ApiServer::start(db.clone()).await?;
|
||||
let port = server.addr.port();
|
||||
|
||||
let content = r#"//nobundling
|
||||
import { ChatPromptTemplate } from "@langchain/core/prompts";
|
||||
|
||||
export async function main() {
|
||||
const tpl = ChatPromptTemplate.fromMessages([
|
||||
["system", "you are a {role}"],
|
||||
["human", "{input}"],
|
||||
]);
|
||||
const out = await tpl.formatMessages({ role: "tester", input: "ping" });
|
||||
return out.length;
|
||||
}
|
||||
"#
|
||||
.to_owned();
|
||||
|
||||
let job = JobPayload::Code(RawCode {
|
||||
hash: None,
|
||||
content,
|
||||
path: None,
|
||||
language: ScriptLang::Bun,
|
||||
lock: None,
|
||||
concurrency_settings: windmill_common::runnable_settings::ConcurrencySettings::default()
|
||||
.into(),
|
||||
debouncing_settings: windmill_common::runnable_settings::DebouncingSettings::default(),
|
||||
cache_ttl: None,
|
||||
cache_ignore_s3_path: None,
|
||||
dedicated_worker: None,
|
||||
modules: None,
|
||||
tag: None,
|
||||
});
|
||||
|
||||
let result = run_job_in_new_worker_until_complete(&db, false, job, port)
|
||||
.await
|
||||
.json_result()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(result, serde_json::json!(2));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Native Mode Tests (requires deno_core feature)
|
||||
// ============================================================================
|
||||
|
||||
@@ -2172,6 +2172,7 @@ try {{
|
||||
"--",
|
||||
&BUN_PATH,
|
||||
"run",
|
||||
"--preserve-symlinks",
|
||||
"-i",
|
||||
"--prefer-offline",
|
||||
"-r",
|
||||
@@ -2238,6 +2239,7 @@ try {{
|
||||
} else {
|
||||
vec![
|
||||
"run",
|
||||
"--preserve-symlinks",
|
||||
"-i",
|
||||
"--prefer-offline",
|
||||
"-r",
|
||||
@@ -3895,6 +3897,7 @@ pub async fn start_worker(
|
||||
common_bun_proc_envs,
|
||||
vec![
|
||||
"run",
|
||||
"--preserve-symlinks",
|
||||
"-i",
|
||||
"--prefer-offline",
|
||||
"-r",
|
||||
|
||||
Reference in New Issue
Block a user