@@ -64,10 +64,27 @@ enum OsRebuildVariant {
6464impl OsBuildVmArgs {
6565 fn build_vm ( self , elevation : ElevationStrategy ) -> Result < ( ) > {
6666 let final_attr = get_final_attr ( true , self . with_bootloader ) ;
67- debug ! ( "Building VM with attribute: {}" , final_attr) ;
68- self
67+ let should_run = self . run ;
68+ let out_path = self
69+ . common
6970 . common
70- . rebuild ( & OsRebuildVariant :: BuildVm , Some ( final_attr) , elevation)
71+ . out_link
72+ . clone ( )
73+ . unwrap_or_else ( || PathBuf :: from ( "result" ) ) ;
74+
75+ debug ! ( "Building VM with attribute: {}" , final_attr) ;
76+ self . common . rebuild (
77+ & OsRebuildVariant :: BuildVm ,
78+ Some ( final_attr) ,
79+ elevation,
80+ ) ?;
81+
82+ // If --run flag is set, execute the VM
83+ if should_run {
84+ run_vm ( & out_path) ?;
85+ }
86+
87+ Ok ( ( ) )
7188 }
7289}
7390
@@ -261,6 +278,12 @@ impl OsRebuildArgs {
261278 if self . common . ask {
262279 warn ! ( "--ask has no effect as dry run was requested" ) ;
263280 }
281+
282+ // For VM builds, print instructions on how to run the VM
283+ if matches ! ( variant, BuildVm ) && !self . common . dry {
284+ print_vm_instructions ( & out_path) ?;
285+ }
286+
264287 return Ok ( ( ) ) ;
265288 }
266289
@@ -540,6 +563,122 @@ impl OsRollbackArgs {
540563 }
541564}
542565
566+ /// Finds the VM runner script in the given build output directory.
567+ ///
568+ /// Searches for a file matching `run-*-vm` in the `bin` subdirectory of
569+ /// `out_path`.
570+ ///
571+ /// # Arguments
572+ ///
573+ /// * `out_path` - The path to the build output directory (usually `result`).
574+ ///
575+ /// # Returns
576+ ///
577+ /// * `Ok(PathBuf)` with the path to the VM runner script if found.
578+ /// * `Err` if the script cannot be found or the bin directory is missing.
579+ ///
580+ /// # Errors
581+ ///
582+ /// Returns an error if the bin directory does not exist or if no matching
583+ /// script is found.
584+ fn find_vm_script ( out_path : & Path ) -> Result < PathBuf > {
585+ let bin_dir = out_path. join ( "bin" ) ;
586+
587+ if !bin_dir. exists ( ) {
588+ bail ! (
589+ "VM build output missing bin directory at {}" ,
590+ bin_dir. display( )
591+ ) ;
592+ }
593+
594+ let entries = fs:: read_dir ( & bin_dir) . wrap_err_with ( || {
595+ format ! ( "Failed to read bin directory at {}" , bin_dir. display( ) )
596+ } ) ?;
597+
598+ let vm_script = entries
599+ . filter_map ( std:: result:: Result :: ok)
600+ . find ( |entry| {
601+ entry
602+ . file_name ( )
603+ . to_str ( )
604+ . is_some_and ( |name| name. starts_with ( "run-" ) && name. ends_with ( "-vm" ) )
605+ } )
606+ . map ( |entry| entry. path ( ) )
607+ . ok_or_else ( || {
608+ eyre ! ( "Could not find VM runner script in {}" , bin_dir. display( ) )
609+ } ) ?;
610+
611+ Ok ( vm_script)
612+ }
613+
614+ /// Prints instructions for running the built VM to the user.
615+ ///
616+ /// Attempts to locate the VM runner script in the build output directory and
617+ /// prints a message with the path to the script. If the script cannot be found,
618+ /// prints a warning and a generic path pattern.
619+ ///
620+ /// # Arguments
621+ ///
622+ /// * `out_path` - The path to the build output directory (usually `result`).
623+ ///
624+ /// # Returns
625+ ///
626+ /// * `Ok(())` on success.
627+ /// * `Err` if there is an error searching for the VM script.
628+ fn print_vm_instructions ( out_path : & Path ) -> Result < ( ) > {
629+ match find_vm_script ( out_path) {
630+ Ok ( script) => {
631+ info ! (
632+ "Done. The virtual machine can be started by running {}" ,
633+ script. display( )
634+ ) ;
635+ } ,
636+ Err ( e) => {
637+ warn ! ( "VM build completed, but could not find run script: {}" , e) ;
638+ info ! (
639+ "Done. The virtual machine script should be at {}/bin/run-*-vm" ,
640+ out_path. display( )
641+ ) ;
642+ } ,
643+ }
644+
645+ Ok ( ( ) )
646+ }
647+
648+ /// Runs the built NixOS VM by executing the VM runner script.
649+ ///
650+ /// Locates the VM runner script in the build output directory and executes it,
651+ /// streaming its output to the user. Returns an error if the script cannot be
652+ /// found or if execution fails.
653+ ///
654+ /// # Arguments
655+ ///
656+ /// * `out_path` - The path to the build output directory (usually `result`).
657+ ///
658+ /// # Returns
659+ ///
660+ /// * `Ok(())` if the VM was started successfully.
661+ /// * `Err` if the script cannot be found or execution fails.
662+ fn run_vm ( out_path : & Path ) -> Result < ( ) > {
663+ let vm_script = find_vm_script ( out_path) ?;
664+
665+ info ! (
666+ "Running VM... Starting virtual machine with {}" ,
667+ vm_script. display( )
668+ ) ;
669+
670+ Command :: new ( & vm_script)
671+ . message ( "Running VM" )
672+ . show_output ( true )
673+ . with_required_env ( )
674+ . run ( )
675+ . wrap_err_with ( || {
676+ format ! ( "Failed to run VM script at {}" , vm_script. display( ) )
677+ } ) ?;
678+
679+ Ok ( ( ) )
680+ }
681+
543682fn find_previous_generation ( ) -> Result < generations:: GenerationInfo > {
544683 let profile_path = PathBuf :: from ( SYSTEM_PROFILE ) ;
545684
0 commit comments