feat: upgrade bun to v1.3.8 with regression tests (#7761)

* test: add bun executor tests with minimal production code changes

- Add comprehensive bun job tests (bun_jobs.rs) covering:
  - Basic execution, error handling, annotation modes
  - Relative imports, deeply nested imports
  - Dedicated worker protocol for both Node.js and Bun runtimes
  - Builder tests for lockfile generation (import scanning)

- Minimize changes to bun_executor.rs by exposing:
  - RELATIVE_BUN_LOADER and RELATIVE_BUN_BUILDER constants
  - build_loader() function and LoaderMode enum
  - BUN_DEDICATED_WORKER_ARGS constant
  - generate_dedicated_worker_wrapper() function

- Tests call production code directly (build_loader) instead of
  duplicating script generation logic

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* nit

* fix: reuse BUN_PATH/NODE_BIN_PATH from windmill-worker, add node to CI

- Tests now use exported BUN_PATH and NODE_BIN_PATH constants instead
  of duplicating env var logic
- Update backend-test.yml:
  - Upgrade bun to v1.3.8
  - Add setup-node action
  - Add NODE_BIN_PATH to cargo test command

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* add private repo test

* fix private repo test

* try fix again

* fix

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
hugocasa
2026-02-04 12:38:15 +01:00
committed by GitHub
parent 74aeeeddec
commit ef89a51f3a
12 changed files with 1873 additions and 78 deletions
+1 -1
View File
@@ -42,7 +42,7 @@ RUN wget https://www.python.org/ftp/python/${PYTHON_VERSION}/Python-${PYTHON_VER
RUN /usr/local/bin/python3 -m pip install pip-tools
# Bun
COPY --from=oven/bun:1.2.23 /usr/local/bin/bun /usr/bin/bun
COPY --from=oven/bun:1.3.8 /usr/local/bin/bun /usr/bin/bun
ARG TARGETPLATFORM
+113 -4
View File
@@ -44,7 +44,10 @@ jobs:
go-version: 1.21.5
- uses: oven-sh/setup-bun@v2
with:
bun-version: 1.1.43
bun-version: 1.3.8
- uses: actions/setup-node@v4
with:
node-version: '20'
- uses: astral-sh/setup-uv@v6.2.1
with:
version: "0.9.24"
@@ -67,6 +70,111 @@ jobs:
- name: Substitute EE code (EE logic is behind feature flag)
run: |
./substitute_ee_code.sh --copy --dir ./windmill-ee-private
- name: Setup private npm registry with test package
working-directory: /tmp
run: |
set -e
# Install Verdaccio globally
npm install -g verdaccio
# Create Verdaccio config that requires authentication for @windmill-test packages
mkdir -p /tmp/verdaccio/storage
cat > /tmp/verdaccio/config.yaml << 'VERDACCIO_CONFIG'
storage: /tmp/verdaccio/storage
auth:
htpasswd:
file: /tmp/verdaccio/htpasswd
max_users: 100
uplinks:
npmjs:
url: https://registry.npmjs.org/
packages:
'@windmill-test/*':
access: $authenticated
publish: $authenticated
'@*/*':
access: $all
publish: $authenticated
proxy: npmjs
'**':
access: $all
publish: $authenticated
proxy: npmjs
server:
keepAliveTimeout: 60
middlewares:
audit:
enabled: true
log: { type: stdout, format: pretty, level: warn }
VERDACCIO_CONFIG
# Create empty htpasswd file (users will be created via API)
touch /tmp/verdaccio/htpasswd
# Start Verdaccio in background
verdaccio --config /tmp/verdaccio/config.yaml &
VERDACCIO_PID=$!
# Wait for Verdaccio to be ready
echo "Waiting for Verdaccio to start..."
for i in {1..30}; do
if curl -s http://localhost:4873/-/ping > /dev/null 2>&1; then
echo "Verdaccio is ready"
break
fi
sleep 1
done
# Login to get a token
echo "Getting auth token..."
RESPONSE=$(curl -s -X PUT \
-H "Content-Type: application/json" \
-d '{"name":"testuser","password":"testpass123"}' \
http://localhost:4873/-/user/org.couchdb.user:testuser)
echo "Auth response: $RESPONSE"
NPM_TOKEN=$(echo "$RESPONSE" | jq -r '.token')
if [ -z "$NPM_TOKEN" ] || [ "$NPM_TOKEN" = "null" ]; then
echo "Failed to get NPM token from response"
exit 1
fi
echo "NPM_TOKEN=${NPM_TOKEN}" >> $GITHUB_ENV
echo "Got NPM token successfully: ${NPM_TOKEN:0:10}..."
# Configure npm globally with the auth token
echo "//localhost:4873/:_authToken=${NPM_TOKEN}" > ~/.npmrc
echo "Configured ~/.npmrc with auth token"
# Create a simple test package
mkdir -p /tmp/windmill-test-private-pkg
cat > /tmp/windmill-test-private-pkg/package.json << 'PKG_JSON'
{
"name": "@windmill-test/private-pkg",
"version": "1.0.0",
"main": "index.js"
}
PKG_JSON
cat > /tmp/windmill-test-private-pkg/index.js << 'PKG_JS'
module.exports.greet = (name) => `Hello from private package, ${name}!`;
PKG_JS
# Publish to Verdaccio with auth
cd /tmp/windmill-test-private-pkg
echo "Publishing package..."
npm publish --registry http://localhost:4873
echo "Package published successfully"
# Verify the package requires auth by trying anonymous access (should fail)
rm -f ~/.npmrc
echo "Testing anonymous access (should fail)..."
if npm view @windmill-test/private-pkg --registry http://localhost:4873 2>/dev/null; then
echo "ERROR: Package should require authentication but anonymous access worked"
exit 1
fi
echo "Verified: Package requires authentication for @windmill-test/private-pkg"
- name: Cache DuckDB FFI module build
uses: actions/cache@v3
with:
@@ -84,9 +192,10 @@ jobs:
RUST_LOG_STYLE: never
CARGO_NET_GIT_FETCH_WITH_CLI: true
WMDEBUG_FORCE_V0_WORKSPACE_DEPENDENCIES: 1
WMDEBUG_FORCE_RUNNABLE_SETTINGS_V0: 1
WMDEBUG_FORCE_RUNNABLE_SETTINGS_V0: 1
WMDEBUG_FORCE_NO_LEGACY_DEBOUNCING_COMPAT: 1
TEST_NPM_REGISTRY: "http://localhost:4873/:_authToken=${{ env.NPM_TOKEN }}"
run: |
deno --version && bun -v && go version && python3 --version
deno --version && bun -v && node --version && go version && python3 --version
cd windmill-duckdb-ffi-internal && ./build_dev.sh && cd ..
DENO_PATH=$(which deno) BUN_PATH=$(which bun) GO_PATH=$(which go) UV_PATH=$(which uv) cargo test --features enterprise,deno_core,duckdb,license,python,rust,scoped_cache,parquet,private --all -- --nocapture
DENO_PATH=$(which deno) BUN_PATH=$(which bun) NODE_BIN_PATH=$(which node) GO_PATH=$(which go) UV_PATH=$(which uv) cargo test --features enterprise,deno_core,duckdb,license,python,rust,scoped_cache,parquet,private,private_registry_test --all -- --nocapture
+1 -1
View File
@@ -234,7 +234,7 @@ COPY --from=windmill_duckdb_ffi_internal_builder /windmill-duckdb-ffi-internal/t
COPY --from=denoland/deno:2.2.1 --chmod=755 /usr/bin/deno /usr/bin/deno
COPY --from=oven/bun:1.2.23 /usr/local/bin/bun /usr/bin/bun
COPY --from=oven/bun:1.3.8 /usr/local/bin/bun /usr/bin/bun
COPY --from=php:8.3.7-cli /usr/local/bin/php /usr/bin/php
COPY --from=composer:2.7.6 /usr/bin/composer /usr/bin/composer
+1
View File
@@ -15527,6 +15527,7 @@ dependencies = [
"sqlx",
"strum 0.27.2",
"systemstat",
"tempfile",
"tikv-jemalloc-ctl",
"tikv-jemalloc-sys",
"tikv-jemallocator",
+2
View File
@@ -90,6 +90,7 @@ zip = ["windmill-api/zip"]
static_frontend = ["windmill-api/static_frontend"]
scoped_cache = ["windmill-common/scoped_cache"]
test_job_debouncing = []
private_registry_test = []
# Languages
python = ["windmill-worker/python", "windmill-api/python"]
rust = ["windmill-worker/rust"]
@@ -182,6 +183,7 @@ axum.workspace = true
serde.workspace = true
windmill-api-client.workspace = true
deno_core = { workspace = true, features = ["include_js_files_for_snapshotting", "unsafe_use_unprotected_platform"] }
tempfile.workspace = true
[workspace.dependencies]
File diff suppressed because it is too large Load Diff
+44
View File
@@ -803,3 +803,47 @@ pub async fn rebuild_dmap(client: &windmill_api_client::Client) -> bool {
.status()
.is_success()
}
// ============================================================================
// Dedicated Worker Protocol Helpers
// ============================================================================
/// Result from parsing a dedicated worker stdout line
#[derive(Debug, Clone, PartialEq)]
pub enum DedicatedWorkerResult {
/// Worker printed "start" indicating it's ready
Start,
/// Worker returned a successful result
Success(serde_json::Value),
/// Worker returned an error result
Error(serde_json::Value),
/// Line is not a protocol message (e.g., logs)
Other(String),
}
/// Parse a line from dedicated worker stdout according to the protocol:
/// - "start" -> Ready signal
/// - "wm_res[success]:JSON" -> Success with result
/// - "wm_res[error]:JSON" -> Error with details
/// - anything else -> Other (logs)
pub fn parse_dedicated_worker_line(line: &str) -> DedicatedWorkerResult {
if line == "start" {
return DedicatedWorkerResult::Start;
}
if let Some(json_str) = line.strip_prefix("wm_res[success]:") {
match serde_json::from_str(json_str) {
Ok(value) => return DedicatedWorkerResult::Success(value),
Err(_) => return DedicatedWorkerResult::Other(line.to_string()),
}
}
if let Some(json_str) = line.strip_prefix("wm_res[error]:") {
match serde_json::from_str(json_str) {
Ok(value) => return DedicatedWorkerResult::Error(value),
Err(_) => return DedicatedWorkerResult::Other(line.to_string()),
}
}
DedicatedWorkerResult::Other(line.to_string())
}
+152
View File
@@ -0,0 +1,152 @@
-- Fixture for Bun edge case tests
-- Tests deeply nested imports (level1 -> level2 -> level3)
-- Level 3: Base script (deepest level)
INSERT INTO public.script(workspace_id, created_by, content, schema, summary, description, path, hash, language, lock) VALUES (
'test-workspace',
'test-user',
'
export function main() {
return "level3";
}
',
'{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{},"required":[],"type":"object"}',
'',
'',
'f/nested/level3', 20001, 'bun', '');
-- Level 2: Imports level3 using RELATIVE path (./level3.ts)
INSERT INTO public.script(workspace_id, created_by, content, schema, summary, description, path, hash, language, lock) VALUES (
'test-workspace',
'test-user',
'
import { main as level3 } from "./level3.ts";
export function main() {
return "level2 -> " + level3();
}
',
'{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{},"required":[],"type":"object"}',
'',
'',
'f/nested/level2', 20002, 'bun', '');
-- Level 1: Imports level2 using RELATIVE path (./level2.ts)
INSERT INTO public.script(workspace_id, created_by, content, schema, summary, description, path, hash, language, lock) VALUES (
'test-workspace',
'test-user',
'
import { main as level2 } from "./level2.ts";
export function main() {
return "level1 -> " + level2();
}
',
'{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{},"required":[],"type":"object"}',
'',
'',
'f/nested/level1', 20003, 'bun', '');
-- Script with preprocessor function
INSERT INTO public.script(workspace_id, created_by, content, schema, summary, description, path, hash, language, lock, has_preprocessor) VALUES (
'test-workspace',
'test-user',
'
export function preprocessor(value: number) {
return { value: value * 2 };
}
export function main(value: number) {
return value + 100;
}
',
'{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"value":{"type":"number"}},"required":["value"],"type":"object"}',
'Script with preprocessor',
'',
'f/edge_cases/with_preprocessor', 20004, 'bun', '', true);
-- Script with nodejs annotation
INSERT INTO public.script(workspace_id, created_by, content, schema, summary, description, path, hash, language, lock) VALUES (
'test-workspace',
'test-user',
'//nodejs
export function main() {
return process.version;
}
',
'{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{},"required":[],"type":"object"}',
'NodeJS mode script',
'',
'f/edge_cases/nodejs_mode', 20005, 'bun', '');
-- Script with nobundling annotation
INSERT INTO public.script(workspace_id, created_by, content, schema, summary, description, path, hash, language, lock) VALUES (
'test-workspace',
'test-user',
'//nobundling
export function main() {
return "no bundle";
}
',
'{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{},"required":[],"type":"object"}',
'No bundling mode script',
'',
'f/edge_cases/nobundling_mode', 20006, 'bun', '');
-- Script that uses circular-ish import pattern (A imports B, B imports C, test imports A and C)
INSERT INTO public.script(workspace_id, created_by, content, schema, summary, description, path, hash, language, lock) VALUES (
'test-workspace',
'test-user',
'
export const SHARED_VALUE = "shared";
export function main() {
return SHARED_VALUE;
}
',
'{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{},"required":[],"type":"object"}',
'',
'',
'f/circular/shared', 20007, 'bun', '');
-- module_a uses RELATIVE path import (./shared.ts)
INSERT INTO public.script(workspace_id, created_by, content, schema, summary, description, path, hash, language, lock) VALUES (
'test-workspace',
'test-user',
'
import { SHARED_VALUE } from "./shared.ts";
export function getValue() {
return "from_a_" + SHARED_VALUE;
}
export function main() {
return getValue();
}
',
'{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{},"required":[],"type":"object"}',
'',
'',
'f/circular/module_a', 20008, 'bun', '');
-- module_b uses ABSOLUTE path import (/f/circular/shared.ts)
INSERT INTO public.script(workspace_id, created_by, content, schema, summary, description, path, hash, language, lock) VALUES (
'test-workspace',
'test-user',
'
import { SHARED_VALUE } from "/f/circular/shared.ts";
export function getValue() {
return "from_b_" + SHARED_VALUE;
}
export function main() {
return getValue();
}
',
'{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{},"required":[],"type":"object"}',
'',
'',
'f/circular/module_b', 20009, 'bun', '');
+196 -68
View File
@@ -16,13 +16,13 @@ use crate::{
common::{
build_command_with_isolation, create_args_and_out_file, get_reserved_variables,
parse_npm_config, read_file, read_file_content, read_result, start_child_process,
write_file_binary, MaybeLock, OccupancyMetrics, StreamNotifier,
DEV_CONF_NSJAIL,
write_file_binary, MaybeLock, OccupancyMetrics, StreamNotifier, DEV_CONF_NSJAIL,
},
get_proxy_envs_for_lang,
handle_child::handle_child,
BUNFIG_INSTALL_SCOPES, BUN_BUNDLE_CACHE_DIR, BUN_CACHE_DIR, BUN_NO_CACHE, BUN_PATH,
DISABLE_NSJAIL, DISABLE_NUSER, HOME_ENV, NODE_BIN_PATH, NODE_PATH, NPM_CONFIG_REGISTRY,
NPM_PATH, NSJAIL_PATH, PATH_ENV, PROXY_ENVS, TRACING_PROXY_CA_CERT_PATH, TZ_ENV, get_proxy_envs_for_lang,
NPM_PATH, NSJAIL_PATH, PATH_ENV, PROXY_ENVS, TRACING_PROXY_CA_CERT_PATH, TZ_ENV,
};
use windmill_common::{
client::AuthedClient,
@@ -51,9 +51,9 @@ use windmill_common::s3_helpers::attempt_fetch_bytes;
use windmill_parser::Typ;
const RELATIVE_BUN_LOADER: &str = include_str!("../loader.bun.js");
pub const RELATIVE_BUN_LOADER: &str = include_str!("../loader.bun.js");
const RELATIVE_BUN_BUILDER: &str = include_str!("../loader_builder.bun.js");
pub const RELATIVE_BUN_BUILDER: &str = include_str!("../loader_builder.bun.js");
const NSJAIL_CONFIG_RUN_BUN_CONTENT: &str = include_str!("../nsjail/run.bun.config.proto");
@@ -64,6 +64,62 @@ pub const BUN_LOCKB_SPLIT_WINDOWS: &str = "\r\n//bun.lockb\r\n";
pub const EMPTY_FILE: &str = "<empty>";
/// Bun args for dedicated worker (without the script path)
pub const BUN_DEDICATED_WORKER_ARGS: &[&str] = &["run", "-i", "--prefer-offline"];
/// Generate the dedicated worker wrapper content.
/// - `arg_names`: The argument names for the main function (e.g., ["x", "y"])
/// - `main_import`: The import path for the main module (e.g., "./main.ts")
/// - `date_conversions`: Optional date conversion statements for Datetime args
pub fn generate_dedicated_worker_wrapper(
arg_names: &[&str],
main_import: &str,
date_conversions: Option<&str>,
) -> String {
let spread = arg_names.join(",");
let dates = date_conversions.unwrap_or("");
let is_debug = std::env::var("RUST_LOG").is_ok_and(|x| x == "windmill=debug");
let print_lines = if is_debug {
r#"console.log(line);"#
} else {
""
};
format!(
r#"
import * as Main from "{main_import}";
import * as Readline from "node:readline"
BigInt.prototype.toJSON = function () {{
return this.toString();
}};
console.log('start');
function getArgs(line) {{
let {{ {spread} }} = JSON.parse(line)
{dates}
return [ {spread} ];
}}
for await (const line of Readline.createInterface({{ input: process.stdin }})) {{
{print_lines}
if (line === "end") {{
process.exit(0);
}}
try {{
const args = getArgs(line);
const res = await Main.main(...args);
console.log("wm_res[success]:" + JSON.stringify(res ?? null, (key, value) => typeof value === 'undefined' ? null : value));
}} catch (e) {{
console.log("wm_res[error]:" + JSON.stringify({{ message: e.message, name: e.name, stack: e.stack, line: line }}));
}}
}}
"#
)
}
/// Returns (package.json, bun.lock(b), is_empty, is_binary)
fn split_lockfile(lockfile: &str) -> (&str, Option<&str>, bool, bool) {
if let Some(index) = lockfile.find(BUN_LOCK_SPLIT) {
@@ -115,24 +171,25 @@ pub async fn gen_bun_lockfile(
gen_bunfig(job_dir).await?;
write_file(job_dir, "package.json", package_json_content.as_str())?;
} else {
let loader = RELATIVE_BUN_LOADER
.replace("W_ID", w_id)
.replace("BASE_INTERNAL_URL", base_internal_url)
.replace("TOKEN", token)
.replace(
"CURRENT_PATH",
&crate::common::use_flow_root_path(script_path),
)
.replace("RAW_GET_ENDPOINT", "raw");
write_file(
&job_dir,
"build.js",
&format!(
r#"
{}
{loader}
{RELATIVE_BUN_BUILDER}
"#,
RELATIVE_BUN_LOADER
.replace("W_ID", w_id)
.replace("BASE_INTERNAL_URL", base_internal_url)
.replace("TOKEN", token)
.replace(
"CURRENT_PATH",
&crate::common::use_flow_root_path(script_path)
)
.replace("RAW_GET_ENDPOINT", "raw")
"#
),
)?;
@@ -382,14 +439,14 @@ pub async fn install_bun_lockfile(
}
#[derive(PartialEq)]
enum LoaderMode {
pub enum LoaderMode {
Node,
Bun,
BunBundle,
NodeBundle,
BrowserBundle,
}
async fn build_loader(
pub async fn build_loader(
job_dir: &str,
base_internal_url: &str,
token: &str,
@@ -406,13 +463,14 @@ async fn build_loader(
&crate::common::use_flow_root_path(current_path),
)
.replace("RAW_GET_ENDPOINT", "raw_unpinned");
if mode == LoaderMode::Node {
write_file(
&job_dir,
"node_builder.ts",
&format!(
r#"
{}
{loader}
import {{ readdir }} from "node:fs/promises";
@@ -420,7 +478,6 @@ let fileNames = []
try {{
fileNames = await readdir("{job_dir}/node_modules")
}} catch (e) {{
}}
try {{
@@ -437,8 +494,7 @@ try {{
console.log("Failed to build node bundle");
process.exit(1);
}}
"#,
loader
"#
),
)?;
} else if mode == LoaderMode::Bun {
@@ -449,11 +505,10 @@ try {{
r#"
import {{ plugin }} from "bun";
{}
{loader}
plugin(p)
"#,
loader
"#
),
)?;
} else if mode == LoaderMode::BunBundle
@@ -465,7 +520,7 @@ plugin(p)
"node_builder.ts",
&format!(
r#"
{}
{loader}
try {{
await Bun.build({{
@@ -486,7 +541,6 @@ try {{
process.exit(1);
}}
"#,
loader,
if mode == LoaderMode::BunBundle {
"bun"
} else if mode == LoaderMode::NodeBundle {
@@ -1726,55 +1780,21 @@ pub async fn start_worker(
.map(|x| return format!("{x} = {x} ? new Date({x}) : undefined"))
.join("\n");
let spread = args.into_iter().map(|x| x.name).join(",");
let arg_names: Vec<&str> = args.iter().map(|x| x.name.as_str()).collect();
// logs.push_str(format!("infer args: {:?}\n", start.elapsed().as_micros()).as_str());
// we cannot use Bun.read and Bun.write because it results in an EBADF error on cloud
let is_debug = std::env::var("RUST_LOG").is_ok_and(|x| x == "windmill=debug");
let print_lines = if is_debug {
r#"console.log(line);"#
} else {
""
};
let main_import = if codebase.is_some() {
"./main.js"
} else {
"./main.ts"
};
let wrapper_content: String = format!(
r#"
import * as Main from "{main_import}";
import * as Readline from "node:readline"
BigInt.prototype.toJSON = function () {{
return this.toString();
}};
console.log('start');
function getArgs(line) {{
let {{ {spread} }} = JSON.parse(line)
{dates}
return [ {spread} ];
}}
for await (const line of Readline.createInterface({{ input: process.stdin }})) {{
{print_lines}
if (line === "end") {{
process.exit(0);
}}
try {{
const args = getArgs(line);
const res = await Main.main(...args);
console.log("wm_res[success]:" + JSON.stringify(res ?? null, (key, value) => typeof value === 'undefined' ? null : value));
}} catch (e) {{
console.log("wm_res[error]:" + JSON.stringify({{ message: e.message, name: e.name, stack: e.stack, line: line }}));
}}
}}
"#,
);
let dates_opt = if dates.is_empty() {
None
} else {
Some(dates.as_str())
};
let wrapper_content = generate_dedicated_worker_wrapper(&arg_names, main_import, dates_opt);
write_file(job_dir, "wrapper.mjs", &wrapper_content)?;
}
@@ -1865,3 +1885,111 @@ for await (const line of Readline.createInterface({{ input: process.stdin }})) {
.await
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_split_lockfile_text_unix() {
let lockfile = r#"{"dependencies":{"lodash":"^4.17.21"}}
//bun.lock
lockfile-content-here"#;
let (pkg, lock, is_empty, is_binary) = split_lockfile(lockfile);
assert_eq!(pkg, r#"{"dependencies":{"lodash":"^4.17.21"}}"#);
assert_eq!(lock, Some("lockfile-content-here"));
assert!(!is_empty);
assert!(!is_binary);
}
#[test]
fn test_split_lockfile_text_windows() {
let lockfile = "{\"dependencies\":{}}\r\n//bun.lock\r\nlockfile-content";
let (pkg, lock, is_empty, is_binary) = split_lockfile(lockfile);
assert_eq!(pkg, "{\"dependencies\":{}}");
assert_eq!(lock, Some("lockfile-content"));
assert!(!is_empty);
assert!(!is_binary);
}
#[test]
fn test_split_lockfile_binary_unix() {
let lockfile = r#"{"dependencies":{}}
//bun.lockb
YmluYXJ5LWNvbnRlbnQ="#; // base64 encoded "binary-content"
let (pkg, lock, is_empty, is_binary) = split_lockfile(lockfile);
assert_eq!(pkg, r#"{"dependencies":{}}"#);
assert_eq!(lock, Some("YmluYXJ5LWNvbnRlbnQ="));
assert!(!is_empty);
assert!(is_binary);
}
#[test]
fn test_split_lockfile_binary_windows() {
let lockfile = "{\"dependencies\":{}}\r\n//bun.lockb\r\nYmluYXJ5LWNvbnRlbnQ=";
let (pkg, lock, is_empty, is_binary) = split_lockfile(lockfile);
assert_eq!(pkg, "{\"dependencies\":{}}");
assert_eq!(lock, Some("YmluYXJ5LWNvbnRlbnQ="));
assert!(!is_empty);
assert!(is_binary);
}
#[test]
fn test_split_lockfile_empty() {
let lockfile = r#"{"dependencies":{}}
//bun.lock
<empty>"#;
let (pkg, lock, is_empty, is_binary) = split_lockfile(lockfile);
assert_eq!(pkg, r#"{"dependencies":{}}"#);
assert_eq!(lock, Some(EMPTY_FILE));
assert!(is_empty);
assert!(!is_binary);
}
#[test]
fn test_split_lockfile_no_lock() {
let lockfile = r#"{"dependencies":{"lodash":"^4.17.21"}}"#;
let (pkg, lock, is_empty, is_binary) = split_lockfile(lockfile);
assert_eq!(pkg, r#"{"dependencies":{"lodash":"^4.17.21"}}"#);
assert!(lock.is_none());
assert!(!is_empty);
assert!(!is_binary);
}
#[test]
fn test_split_lockfile_multiline_package_json() {
let lockfile = r#"{
"dependencies": {
"lodash": "^4.17.21"
}
}
//bun.lock
lockfile-content"#;
let (pkg, lock, is_empty, is_binary) = split_lockfile(lockfile);
assert_eq!(
pkg,
r#"{
"dependencies": {
"lodash": "^4.17.21"
}
}"#
);
assert_eq!(lock, Some("lockfile-content"));
assert!(!is_empty);
assert!(!is_binary);
}
}
+3 -2
View File
@@ -95,8 +95,9 @@ pub use otel_tracing_proxy_ee::{load_internal_otel_exporter, DENO_OTEL_INITIALIZ
pub use result_processor::handle_job_error;
pub use bun_executor::{
compute_bundle_local_and_remote_path, get_common_bun_proc_envs, install_bun_lockfile,
prebundle_bun_script, prepare_job_dir,
build_loader, compute_bundle_local_and_remote_path, generate_dedicated_worker_wrapper,
get_common_bun_proc_envs, install_bun_lockfile, prebundle_bun_script, prepare_job_dir,
BUN_DEDICATED_WORKER_ARGS, LoaderMode, RELATIVE_BUN_BUILDER, RELATIVE_BUN_LOADER,
};
pub use deno_executor::generate_deno_lock;
pub use prepare_deps::run_prepare_deps_cli;
+1 -1
View File
@@ -34,7 +34,7 @@ RUN mkdir -p /tmp/windmill/cache && \
rm -rf /tmp/build_cache && \
mkdir -p -m 777 /tmp/windmill/cache/uv
COPY --from=oven/bun:1.2.23 /usr/local/bin/bun /usr/bin/bun
COPY --from=oven/bun:1.3.8 /usr/local/bin/bun /usr/bin/bun
# add the docker client to call docker from a worker if enabled
COPY --from=docker:dind /usr/local/bin/docker /usr/local/bin/
+1 -1
View File
@@ -34,7 +34,7 @@ RUN mkdir -p /tmp/windmill/cache && \
rm -rf /tmp/build_cache && \
mkdir -p -m 777 /tmp/windmill/cache/uv
COPY --from=oven/bun:1.2.23 /usr/local/bin/bun /usr/bin/bun
COPY --from=oven/bun:1.3.8 /usr/local/bin/bun /usr/bin/bun
# add the docker client to call docker from a worker if enabled
COPY --from=docker:dind /usr/local/bin/docker /usr/local/bin/