@@ -10,6 +10,7 @@ use colored::Colorize;
1010use std:: collections:: HashMap ;
1111
1212use super :: project:: { ProjectInfo , WORKER_MANIFEST , load_project_info} ;
13+ #[ cfg( all( target_os = "linux" , not( target_env = "musl" ) ) ) ]
1314use super :: rootfs:: clone_rootfs;
1415
1516async fn detect_lan_ip ( ) -> Option < String > {
@@ -83,6 +84,7 @@ pub async fn handle_worker_dev(
8384 }
8485 } ;
8586
87+ #[ cfg( all( target_os = "linux" , not( target_env = "musl" ) ) ) ]
8688 if let Err ( e) = super :: firmware:: download:: ensure_libkrunfw ( ) . await {
8789 tracing:: warn!( error = %e, "failed to ensure libkrunfw" ) ;
8890 }
@@ -92,7 +94,8 @@ pub async fn handle_worker_dev(
9294 None => {
9395 eprintln ! (
9496 "{} No dev runtime available.\n \
95- Rebuild with --features embed-libkrunfw or place libkrunfw in ~/.iii/lib/",
97+ On Linux: rebuild with --features embed-libkrunfw or place libkrunfw in ~/.iii/lib/\n \
98+ On macOS/musl: VM sandbox is not yet supported on this platform.",
9699 "error:" . red( )
97100 ) ;
98101 return 1 ;
@@ -168,143 +171,150 @@ async fn detect_dev_runtime(explicit: Option<&str>) -> Option<String> {
168171 return Some ( rt. to_string ( ) ) ;
169172 }
170173
171- {
172- if super :: worker_manager:: libkrun:: libkrun_available ( ) {
173- return Some ( "libkrun" . to_string ( ) ) ;
174- }
174+ if super :: worker_manager:: sandbox_available ( ) {
175+ return Some ( "libkrun" . to_string ( ) ) ;
175176 }
176177
177178 None
178179}
179180
180181async fn run_dev_worker (
181182 runtime : & str ,
183+ _sb_name : & str ,
184+ _project_str : & str ,
185+ _project : & ProjectInfo ,
186+ _engine_url : & str ,
187+ _rebuild : bool ,
188+ ) -> i32 {
189+ #[ cfg( all( target_os = "linux" , not( target_env = "musl" ) ) ) ]
190+ if runtime == "libkrun" {
191+ return run_dev_worker_libkrun ( _sb_name, _project_str, _project, _engine_url, _rebuild) . await ;
192+ }
193+
194+ eprintln ! ( "{} Unknown or unsupported runtime: {}" , "error:" . red( ) , runtime) ;
195+ 1
196+ }
197+
198+ #[ cfg( all( target_os = "linux" , not( target_env = "musl" ) ) ) ]
199+ async fn run_dev_worker_libkrun (
182200 sb_name : & str ,
183201 project_str : & str ,
184202 project : & ProjectInfo ,
185203 engine_url : & str ,
186204 rebuild : bool ,
187205) -> i32 {
188- match runtime {
189- "libkrun" => {
190- let language = project. language . as_deref ( ) . unwrap_or ( "typescript" ) ;
191- let mut env = build_dev_env ( engine_url, & project. env ) ;
192-
193- let base_rootfs = match super :: worker_manager:: oci:: prepare_rootfs ( language) . await {
194- Ok ( p) => p,
195- Err ( e) => {
196- eprintln ! ( "{} {}" , "error:" . red( ) , e) ;
197- return 1 ;
198- }
199- } ;
200-
201- let oci_env = super :: worker_manager:: oci:: read_oci_env ( & base_rootfs) ;
202- for ( key, value) in oci_env {
203- env. entry ( key) . or_insert ( value) ;
204- }
205-
206- let dev_dir = match dirs:: home_dir ( ) {
207- Some ( h) => h. join ( ".iii" ) . join ( "dev" ) . join ( sb_name) ,
208- None => {
209- eprintln ! ( "{} Cannot determine home directory" , "error:" . red( ) ) ;
210- return 1 ;
211- }
212- } ;
213- let prepared_marker = dev_dir. join ( "var" ) . join ( ".iii-prepared" ) ;
214-
215- if rebuild && dev_dir. exists ( ) {
216- eprintln ! ( " Rebuilding: clearing cached sandbox..." ) ;
217- let _ = std:: fs:: remove_dir_all ( & dev_dir) ;
218- }
219-
220- if !dev_dir. exists ( ) {
221- eprintln ! ( " Preparing sandbox..." ) ;
222- if let Err ( e) = clone_rootfs ( & base_rootfs, & dev_dir) {
223- eprintln ! ( "{} Failed to create project rootfs: {}" , "error:" . red( ) , e) ;
224- return 1 ;
225- }
226- }
227-
228- let is_prepared = prepared_marker. exists ( ) ;
229- if is_prepared {
230- eprintln ! (
231- " {} Using cached deps {}" ,
232- "✓" . green( ) ,
233- "(use --rebuild to reinstall)" . dimmed( )
234- ) ;
235- }
236-
237- let script = build_libkrun_dev_script ( project, is_prepared) ;
238-
239- let script_path = dev_dir. join ( "tmp" ) . join ( "iii-dev-run.sh" ) ;
240- if let Err ( e) = std:: fs:: write ( & script_path, & script) {
241- eprintln ! ( "{} Failed to write dev script: {}" , "error:" . red( ) , e) ;
242- return 1 ;
243- }
244- #[ cfg( unix) ]
245- {
246- use std:: os:: unix:: fs:: PermissionsExt ;
247- let _ =
248- std:: fs:: set_permissions ( & script_path, std:: fs:: Permissions :: from_mode ( 0o755 ) ) ;
249- }
250-
251- let workspace = dev_dir. join ( "workspace" ) ;
252- std:: fs:: create_dir_all ( & workspace) . ok ( ) ;
253- if let Err ( e) = copy_dir_contents ( std:: path:: Path :: new ( project_str) , & workspace) {
254- eprintln ! ( "{} Failed to copy project to rootfs: {}" , "error:" . red( ) , e) ;
255- return 1 ;
256- }
257-
258- let init_path = match super :: firmware:: download:: ensure_init_binary ( ) . await {
259- Ok ( p) => p,
260- Err ( e) => {
261- eprintln ! ( "{} Failed to provision iii-init: {}" , "error:" . red( ) , e) ;
262- return 1 ;
263- }
264- } ;
265-
266- if !iii_filesystem:: init:: has_init ( ) {
267- let dest = dev_dir. join ( "init.krun" ) ;
268- if let Err ( e) = std:: fs:: copy ( & init_path, & dest) {
269- eprintln ! (
270- "{} Failed to copy iii-init to rootfs: {}" ,
271- "error:" . red( ) ,
272- e
273- ) ;
274- return 1 ;
275- }
276- #[ cfg( unix) ]
277- {
278- use std:: os:: unix:: fs:: PermissionsExt ;
279- let _ = std:: fs:: set_permissions ( & dest, std:: fs:: Permissions :: from_mode ( 0o755 ) ) ;
280- }
281- }
282-
283- let exec_path = "/bin/sh" ;
284- let args = vec ! [
285- "-c" . to_string( ) ,
286- "cd /workspace && exec bash /tmp/iii-dev-run.sh" . to_string( ) ,
287- ] ;
288- let manifest_path = std:: path:: Path :: new ( project_str) . join ( WORKER_MANIFEST ) ;
289- let ( vcpus, ram) = parse_manifest_resources ( & manifest_path) ;
290-
291- super :: worker_manager:: libkrun:: run_dev (
292- language,
293- project_str,
294- exec_path,
295- & args,
296- env,
297- vcpus,
298- ram,
299- dev_dir,
300- )
301- . await
206+ let language = project. language . as_deref ( ) . unwrap_or ( "typescript" ) ;
207+ let mut env = build_dev_env ( engine_url, & project. env ) ;
208+
209+ let base_rootfs = match super :: worker_manager:: oci:: prepare_rootfs ( language) . await {
210+ Ok ( p) => p,
211+ Err ( e) => {
212+ eprintln ! ( "{} {}" , "error:" . red( ) , e) ;
213+ return 1 ;
214+ }
215+ } ;
216+
217+ let oci_env = super :: worker_manager:: oci:: read_oci_env ( & base_rootfs) ;
218+ for ( key, value) in oci_env {
219+ env. entry ( key) . or_insert ( value) ;
220+ }
221+
222+ let dev_dir = match dirs:: home_dir ( ) {
223+ Some ( h) => h. join ( ".iii" ) . join ( "dev" ) . join ( sb_name) ,
224+ None => {
225+ eprintln ! ( "{} Cannot determine home directory" , "error:" . red( ) ) ;
226+ return 1 ;
227+ }
228+ } ;
229+ let prepared_marker = dev_dir. join ( "var" ) . join ( ".iii-prepared" ) ;
230+
231+ if rebuild && dev_dir. exists ( ) {
232+ eprintln ! ( " Rebuilding: clearing cached sandbox..." ) ;
233+ let _ = std:: fs:: remove_dir_all ( & dev_dir) ;
234+ }
235+
236+ if !dev_dir. exists ( ) {
237+ eprintln ! ( " Preparing sandbox..." ) ;
238+ if let Err ( e) = clone_rootfs ( & base_rootfs, & dev_dir) {
239+ eprintln ! ( "{} Failed to create project rootfs: {}" , "error:" . red( ) , e) ;
240+ return 1 ;
241+ }
242+ }
243+
244+ let is_prepared = prepared_marker. exists ( ) ;
245+ if is_prepared {
246+ eprintln ! (
247+ " {} Using cached deps {}" ,
248+ "✓" . green( ) ,
249+ "(use --rebuild to reinstall)" . dimmed( )
250+ ) ;
251+ }
252+
253+ let script = build_libkrun_dev_script ( project, is_prepared) ;
254+
255+ let script_path = dev_dir. join ( "tmp" ) . join ( "iii-dev-run.sh" ) ;
256+ if let Err ( e) = std:: fs:: write ( & script_path, & script) {
257+ eprintln ! ( "{} Failed to write dev script: {}" , "error:" . red( ) , e) ;
258+ return 1 ;
259+ }
260+ #[ cfg( unix) ]
261+ {
262+ use std:: os:: unix:: fs:: PermissionsExt ;
263+ let _ =
264+ std:: fs:: set_permissions ( & script_path, std:: fs:: Permissions :: from_mode ( 0o755 ) ) ;
265+ }
266+
267+ let workspace = dev_dir. join ( "workspace" ) ;
268+ std:: fs:: create_dir_all ( & workspace) . ok ( ) ;
269+ if let Err ( e) = copy_dir_contents ( std:: path:: Path :: new ( project_str) , & workspace) {
270+ eprintln ! ( "{} Failed to copy project to rootfs: {}" , "error:" . red( ) , e) ;
271+ return 1 ;
272+ }
273+
274+ let init_path = match super :: firmware:: download:: ensure_init_binary ( ) . await {
275+ Ok ( p) => p,
276+ Err ( e) => {
277+ eprintln ! ( "{} Failed to provision iii-init: {}" , "error:" . red( ) , e) ;
278+ return 1 ;
302279 }
303- _ => {
304- eprintln ! ( "{} Unknown runtime: {}" , "error:" . red( ) , runtime) ;
305- 1
280+ } ;
281+
282+ if !iii_filesystem:: init:: has_init ( ) {
283+ let dest = dev_dir. join ( "init.krun" ) ;
284+ if let Err ( e) = std:: fs:: copy ( & init_path, & dest) {
285+ eprintln ! (
286+ "{} Failed to copy iii-init to rootfs: {}" ,
287+ "error:" . red( ) ,
288+ e
289+ ) ;
290+ return 1 ;
291+ }
292+ #[ cfg( unix) ]
293+ {
294+ use std:: os:: unix:: fs:: PermissionsExt ;
295+ let _ = std:: fs:: set_permissions ( & dest, std:: fs:: Permissions :: from_mode ( 0o755 ) ) ;
306296 }
307297 }
298+
299+ let exec_path = "/bin/sh" ;
300+ let args = vec ! [
301+ "-c" . to_string( ) ,
302+ "cd /workspace && exec bash /tmp/iii-dev-run.sh" . to_string( ) ,
303+ ] ;
304+ let manifest_path = std:: path:: Path :: new ( project_str) . join ( WORKER_MANIFEST ) ;
305+ let ( vcpus, ram) = parse_manifest_resources ( & manifest_path) ;
306+
307+ super :: worker_manager:: libkrun:: run_dev (
308+ language,
309+ project_str,
310+ exec_path,
311+ & args,
312+ env,
313+ vcpus,
314+ ram,
315+ dev_dir,
316+ )
317+ . await
308318}
309319
310320pub fn parse_manifest_resources ( manifest_path : & std:: path:: Path ) -> ( u32 , u32 ) {
@@ -360,6 +370,7 @@ pub fn copy_dir_contents(src: &std::path::Path, dst: &std::path::Path) -> Result
360370 Ok ( ( ) )
361371}
362372
373+ #[ cfg( all( target_os = "linux" , not( target_env = "musl" ) ) ) ]
363374pub fn build_libkrun_dev_script ( project : & ProjectInfo , prepared : bool ) -> String {
364375 let env_exports = build_env_exports ( & project. env ) ;
365376 let mut parts: Vec < String > = Vec :: new ( ) ;
@@ -466,6 +477,7 @@ mod tests {
466477 assert_eq ! ( url, "ws://localhost:49134" ) ;
467478 }
468479
480+ #[ cfg( all( target_os = "linux" , not( target_env = "musl" ) ) ) ]
469481 #[ test]
470482 fn build_libkrun_dev_script_first_run ( ) {
471483 let project = ProjectInfo {
@@ -483,6 +495,7 @@ mod tests {
483495 assert ! ( script. contains( ".iii-prepared" ) ) ;
484496 }
485497
498+ #[ cfg( all( target_os = "linux" , not( target_env = "musl" ) ) ) ]
486499 #[ test]
487500 fn build_libkrun_dev_script_prepared ( ) {
488501 let project = ProjectInfo {
0 commit comments