Skip to content

Commit fcfad69

Browse files
authored
feat: Migrate to bun.lock (#5112)
* bun: Migrate to bun.lock (In backwards compatible way) Read more: https://bun.sh/blog/bun-lock-text-lockfile * Clean up * More clean up * Mount bun.lock to jailed process
1 parent 669a1ff commit fcfad69

File tree

2 files changed

+68
-41
lines changed

2 files changed

+68
-41
lines changed

backend/windmill-worker/nsjail/run.bun.config.proto

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,12 @@ mount {
7777
mandatory: false
7878
}
7979

80+
mount {
81+
src: "{JOB_DIR}/bun.lock"
82+
dst: "/tmp/{LANG}/bun.lock"
83+
is_bind: true
84+
mandatory: false
85+
}
8086

8187
mount {
8288
src: "{JOB_DIR}/wrapper.mjs"

backend/windmill-worker/src/bun_executor.rs

Lines changed: 62 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -62,24 +62,37 @@ const RELATIVE_BUN_BUILDER: &str = include_str!("../loader_builder.bun.js");
6262

6363
const NSJAIL_CONFIG_RUN_BUN_CONTENT: &str = include_str!("../nsjail/run.bun.config.proto");
6464

65+
pub const BUN_LOCK_SPLIT: &str = "\n//bun.lock\n";
6566
pub const BUN_LOCKB_SPLIT: &str = "\n//bun.lockb\n";
67+
pub const BUN_LOCK_SPLIT_WINDOWS: &str = "\r\n//bun.lock\r\n";
6668
pub const BUN_LOCKB_SPLIT_WINDOWS: &str = "\r\n//bun.lockb\r\n";
6769

6870
pub const EMPTY_FILE: &str = "<empty>";
6971

70-
fn split_lockfile(lockfile: &str) -> (&str, Option<&str>, bool) {
71-
if let Some(index) = lockfile.find(BUN_LOCKB_SPLIT) {
72+
/// Returns (package.json, bun.lock(b), is_empty, is_binary)
73+
fn split_lockfile(lockfile: &str) -> (&str, Option<&str>, bool, bool) {
74+
if let Some(index) = lockfile.find(BUN_LOCK_SPLIT) {
75+
// Split using "\n//bun.lock\n"
76+
let (before, after_with_sep) = lockfile.split_at(index);
77+
let after = &after_with_sep[BUN_LOCK_SPLIT.len()..];
78+
(before, Some(after), after == EMPTY_FILE, false)
79+
} else if let Some(index) = lockfile.find(BUN_LOCKB_SPLIT) {
7280
// Split using "\n//bun.lockb\n"
7381
let (before, after_with_sep) = lockfile.split_at(index);
7482
let after = &after_with_sep[BUN_LOCKB_SPLIT.len()..];
75-
(before, Some(after), after == EMPTY_FILE)
83+
(before, Some(after), after == EMPTY_FILE, true)
84+
} else if let Some(index) = lockfile.find(BUN_LOCK_SPLIT_WINDOWS) {
85+
// Split using "\r\n//bun.lock\r\n"
86+
let (before, after_with_sep) = lockfile.split_at(index);
87+
let after = &after_with_sep[BUN_LOCK_SPLIT_WINDOWS.len()..];
88+
(before, Some(after), after == EMPTY_FILE, false)
7689
} else if let Some(index) = lockfile.find(BUN_LOCKB_SPLIT_WINDOWS) {
7790
// Split using "\r\n//bun.lockb\r\n"
7891
let (before, after_with_sep) = lockfile.split_at(index);
7992
let after = &after_with_sep[BUN_LOCKB_SPLIT_WINDOWS.len()..];
80-
(before, Some(after), after == EMPTY_FILE)
93+
(before, Some(after), after == EMPTY_FILE, true)
8194
} else {
82-
(lockfile, None, false)
95+
(lockfile, None, false, false)
8396
}
8497
}
8598

@@ -199,18 +212,18 @@ pub async fn gen_bun_lockfile(
199212
}
200213
if !npm_mode {
201214
#[cfg(any(target_os = "linux", target_os = "macos"))]
202-
content.push_str(BUN_LOCKB_SPLIT);
215+
content.push_str(BUN_LOCK_SPLIT);
203216

204217
#[cfg(target_os = "windows")]
205-
content.push_str(BUN_LOCKB_SPLIT_WINDOWS);
218+
content.push_str(BUN_LOCK_SPLIT_WINDOWS);
206219

207220
{
208-
let file = format!("{job_dir}/bun.lockb");
221+
let file = format!("{job_dir}/bun.lock");
209222
if !empty_deps && tokio::fs::metadata(&file).await.is_ok() {
210223
let mut file = File::open(&file).await?;
211-
let mut buf = vec![];
212-
file.read_to_end(&mut buf).await?;
213-
content.push_str(&base64::engine::general_purpose::STANDARD.encode(&buf));
224+
let mut buf = String::default();
225+
file.read_to_string(&mut buf).await?;
226+
content.push_str(&buf);
214227
} else {
215228
content.push_str(&EMPTY_FILE);
216229
}
@@ -277,7 +290,7 @@ pub async fn install_bun_lockfile(
277290
.env_clear()
278291
.envs(PROXY_ENVS.clone())
279292
.envs(common_bun_proc_envs)
280-
.args(vec!["install"])
293+
.args(vec!["install", "--save-text-lockfile"])
281294
.stdout(Stdio::piped())
282295
.stderr(Stdio::piped());
283296

@@ -785,26 +798,30 @@ async fn compute_bundle_local_and_remote_path(
785798
}
786799

787800
pub async fn prepare_job_dir(reqs: &str, job_dir: &str) -> Result<()> {
788-
let (pkg, lock, empty) = split_lockfile(reqs);
801+
let (pkg, lock, empty, is_binary) = split_lockfile(reqs);
789802
let _ = write_file(job_dir, "package.json", pkg)?;
790803

791804
if !empty {
792805
if let Some(lock) = lock {
793-
let _ = write_lockb(lock, job_dir).await?;
806+
let _ = write_lock(lock, job_dir, is_binary).await?;
794807
}
795808
}
796809

797810
Ok(())
798811
}
799-
async fn write_lockb(splitted_lockb_2: &str, job_dir: &str) -> Result<()> {
800-
write_file_binary(
801-
job_dir,
802-
"bun.lockb",
803-
&base64::engine::general_purpose::STANDARD
804-
.decode(splitted_lockb_2)
805-
.map_err(|_| error::Error::InternalErr("Could not decode bun.lockb".to_string()))?,
806-
)
807-
.await?;
812+
async fn write_lock(splitted_lockb_2: &str, job_dir: &str, is_binary: bool) -> Result<()> {
813+
if is_binary {
814+
write_file_binary(
815+
job_dir,
816+
"bun.lockb",
817+
&base64::engine::general_purpose::STANDARD
818+
.decode(splitted_lockb_2)
819+
.map_err(|_| error::Error::InternalErr(format!("Could not decode bun.lockb")))?,
820+
)
821+
.await?;
822+
} else {
823+
write_file(job_dir, "bun.lock", splitted_lockb_2)?;
824+
};
808825
Ok(())
809826
}
810827

@@ -900,26 +917,26 @@ pub async fn handle_bun_job(
900917
} else if let Some(codebase) = codebase.as_ref() {
901918
pull_codebase(&job.workspace_id, codebase, job_dir).await?;
902919
} else if let Some(reqs) = requirements_o.as_ref() {
903-
let (pkg, lock, empty) = split_lockfile(reqs);
920+
let (pkg, lock, empty, is_binary) = split_lockfile(reqs);
904921

905922
if lock.is_none() && !annotation.npm {
906923
return Err(error::Error::ExecutionErr(
907-
format!("Invalid requirements, expected to find //bun.lockb split pattern in reqs. Found: |{reqs}|")
924+
format!("Invalid requirements, expected to find //bun.lock{} split pattern in reqs. Found: |{reqs}|", if is_binary {"b"} else {""})
908925
));
909926
}
910927

911928
let _ = write_file(job_dir, "package.json", pkg)?;
912-
let lockb = if annotation.npm { "" } else { lock.unwrap() };
929+
let lock = if annotation.npm { "" } else { lock.unwrap() };
913930
if !empty {
914931
let mut skip_install = false;
915932
let mut create_buntar = false;
916933
let mut buntar_path = "".to_string();
917934

918935
if !annotation.npm {
919-
let _ = write_lockb(lockb, job_dir).await?;
936+
let _ = write_lock(lock, job_dir, is_binary).await?;
920937

921938
let mut sha_path = sha2::Sha256::new();
922-
sha_path.update(lockb.as_bytes());
939+
sha_path.update(lock.as_bytes());
923940

924941
let buntar_name =
925942
base64::engine::general_purpose::URL_SAFE.encode(sha_path.finalize());
@@ -962,7 +979,7 @@ pub async fn handle_bun_job(
962979
Some(&vec![
963980
"main.ts".to_string(),
964981
"package.json".to_string(),
965-
"bun.lockb".to_string(),
982+
if is_binary { "bun.lockb" } else { "bun.lock" }.to_string(),
966983
"shared".to_string(),
967984
"bunfig.toml".to_string(),
968985
]),
@@ -1592,25 +1609,29 @@ pub async fn start_worker(
15921609
if let Some(codebase) = codebase.as_ref() {
15931610
pull_codebase(w_id, codebase, job_dir).await?;
15941611
} else if let Some(reqs) = requirements_o {
1595-
let (pkg, lock, empty) = split_lockfile(&reqs);
1612+
let (pkg, lock, empty, is_binary) = split_lockfile(&reqs);
15961613
if lock.is_none() {
15971614
return Err(error::Error::ExecutionErr(
15981615
format!("Invalid requirements, expected to find //bun.lockb split pattern in reqs. Found: |{reqs}|")
15991616
));
16001617
}
16011618
let _ = write_file(job_dir, "package.json", pkg)?;
1602-
let lockb = lock.unwrap();
1619+
let lock = lock.unwrap();
16031620
if !empty {
1604-
let _ = write_file_binary(
1605-
job_dir,
1606-
"bun.lockb",
1607-
&base64::engine::general_purpose::STANDARD
1608-
.decode(lockb)
1609-
.map_err(|_| {
1610-
error::Error::InternalErr("Could not decode bun.lockb".to_string())
1611-
})?,
1612-
)
1613-
.await?;
1621+
if is_binary {
1622+
let _ = write_file_binary(
1623+
job_dir,
1624+
"bun.lockb",
1625+
&base64::engine::general_purpose::STANDARD
1626+
.decode(lock)
1627+
.map_err(|_| {
1628+
error::Error::InternalErr("Could not decode bun.lockb".to_string())
1629+
})?,
1630+
)
1631+
.await?;
1632+
} else {
1633+
write_file(job_dir, "bun.lock", lock)?;
1634+
}
16141635

16151636
install_bun_lockfile(
16161637
&mut mem_peak,

0 commit comments

Comments
 (0)