@@ -11,7 +11,9 @@ use crate::commands::Command;
1111use crate :: generations;
1212use crate :: installable:: Installable ;
1313use crate :: interface:: OsSubcommand :: { self } ;
14- use crate :: interface:: { self , OsGenerationsArgs , OsRebuildArgs , OsReplArgs , OsRollbackArgs } ;
14+ use crate :: interface:: {
15+ self , OsBuildVmArgs , OsGenerationsArgs , OsRebuildArgs , OsReplArgs , OsRollbackArgs ,
16+ } ;
1517use crate :: update:: update;
1618use crate :: util:: ensure_ssh_key_login;
1719use crate :: util:: get_hostname;
@@ -25,15 +27,16 @@ impl interface::OsArgs {
2527 pub fn run ( self ) -> Result < ( ) > {
2628 use OsRebuildVariant :: * ;
2729 match self . subcommand {
28- OsSubcommand :: Boot ( args) => args. rebuild ( Boot ) ,
29- OsSubcommand :: Test ( args) => args. rebuild ( Test ) ,
30- OsSubcommand :: Switch ( args) => args. rebuild ( Switch ) ,
30+ OsSubcommand :: Boot ( args) => args. rebuild ( Boot , None ) ,
31+ OsSubcommand :: Test ( args) => args. rebuild ( Test , None ) ,
32+ OsSubcommand :: Switch ( args) => args. rebuild ( Switch , None ) ,
3133 OsSubcommand :: Build ( args) => {
3234 if args. common . ask || args. common . dry {
3335 warn ! ( "`--ask` and `--dry` have no effect for `nh os build`" ) ;
3436 }
35- args. rebuild ( Build )
37+ args. rebuild ( Build , None )
3638 }
39+ OsSubcommand :: BuildVm ( args) => args. build_vm ( ) ,
3740 OsSubcommand :: Repl ( args) => args. run ( ) ,
3841 OsSubcommand :: Info ( args) => args. info ( ) ,
3942 OsSubcommand :: Rollback ( args) => args. rollback ( ) ,
@@ -47,10 +50,20 @@ enum OsRebuildVariant {
4750 Switch ,
4851 Boot ,
4952 Test ,
53+ BuildVm ,
54+ }
55+
56+ impl OsBuildVmArgs {
57+ fn build_vm ( self ) -> Result < ( ) > {
58+ let final_attr = get_final_attr ( true , self . with_bootloader ) ;
59+ self . common
60+ . rebuild ( OsRebuildVariant :: BuildVm , Some ( final_attr) )
61+ }
5062}
5163
5264impl OsRebuildArgs {
53- fn rebuild ( self , variant : OsRebuildVariant ) -> Result < ( ) > {
65+ // final_attr is the attribute of config.system.build.X to evaluate.
66+ fn rebuild ( self , variant : OsRebuildVariant , final_attr : Option < String > ) -> Result < ( ) > {
5467 use OsRebuildVariant :: * ;
5568
5669 if self . build_host . is_some ( ) || self . target_host . is_some ( ) {
@@ -72,7 +85,24 @@ impl OsRebuildArgs {
7285 update ( & self . common . installable , self . update_args . update_input ) ?;
7386 }
7487
75- let hostname = self . hostname . ok_or ( ( ) ) . or_else ( |( ) | get_hostname ( ) ) ?;
88+ let system_hostname = match get_hostname ( ) {
89+ Ok ( hostname) => Some ( hostname) ,
90+ Err ( err) => {
91+ tracing:: warn!( "{}" , err. to_string( ) ) ;
92+ None
93+ }
94+ } ;
95+
96+ let target_hostname = match & self . hostname {
97+ Some ( h) => h. to_owned ( ) ,
98+ None => match & system_hostname {
99+ Some ( hostname) => {
100+ tracing:: warn!( "Guessing system is {hostname} for a VM image. If this isn't intended, use --hostname to change." ) ;
101+ hostname. clone ( )
102+ }
103+ None => return Err ( eyre ! ( "Unable to fetch hostname, and no hostname supplied." ) ) ,
104+ } ,
105+ } ;
76106
77107 let out_path: Box < dyn crate :: util:: MaybeTempPath > = match self . common . out_link {
78108 Some ( ref p) => Box :: new ( p. clone ( ) ) ,
@@ -103,14 +133,23 @@ impl OsRebuildArgs {
103133 self . common . installable . clone ( )
104134 } ;
105135
106- let toplevel = toplevel_for ( hostname, installable) ;
136+ let toplevel = toplevel_for (
137+ & target_hostname,
138+ installable,
139+ final_attr. unwrap_or ( String :: from ( "toplevel" ) ) . as_str ( ) ,
140+ ) ;
141+
142+ let message = match variant {
143+ BuildVm => "Building NixOS VM image" ,
144+ _ => "Building NixOS configuration" ,
145+ } ;
107146
108147 commands:: Build :: new ( toplevel)
109148 . extra_arg ( "--out-link" )
110149 . extra_arg ( out_path. get_path ( ) )
111150 . extra_args ( & self . extra_args )
112151 . builder ( self . build_host . clone ( ) )
113- . message ( "Building NixOS configuration" )
152+ . message ( message )
114153 . nom ( !self . common . no_nom )
115154 . run ( ) ?;
116155
@@ -133,16 +172,21 @@ impl OsRebuildArgs {
133172
134173 target_profile. try_exists ( ) . context ( "Doesn't exist" ) ?;
135174
136- if self . build_host . is_none ( ) && self . target_host . is_none ( ) {
175+ if self . build_host . is_none ( )
176+ && self . target_host . is_none ( )
177+ && system_hostname. map_or ( true , |h| h == target_hostname)
178+ {
137179 Command :: new ( "nvd" )
138180 . arg ( "diff" )
139181 . arg ( CURRENT_PROFILE )
140182 . arg ( & target_profile)
141183 . message ( "Comparing changes" )
142184 . run ( ) ?;
185+ } else {
186+ debug ! ( "Not running nvd as the target hostname is different from the system hostname." )
143187 }
144188
145- if self . common . dry || matches ! ( variant, Build ) {
189+ if self . common . dry || matches ! ( variant, Build | BuildVm ) {
146190 if self . common . ask {
147191 warn ! ( "--ask has no effect as dry run was requested" ) ;
148192 }
@@ -466,11 +510,26 @@ fn get_current_generation_number() -> Result<u64> {
466510 . map_err ( |_| eyre ! ( "Invalid generation number" ) )
467511}
468512
469- pub fn toplevel_for < S : AsRef < str > > ( hostname : S , installable : Installable ) -> Installable {
470- let mut res = installable;
513+ pub fn get_final_attr ( build_vm : bool , with_bootloader : bool ) -> String {
514+ let attr = if build_vm && with_bootloader {
515+ "vmWithBootLoader"
516+ } else if build_vm {
517+ "vm"
518+ } else {
519+ "toplevel"
520+ } ;
521+ String :: from ( attr)
522+ }
523+
524+ pub fn toplevel_for < S : AsRef < str > > (
525+ hostname : S ,
526+ installable : Installable ,
527+ final_attr : & str ,
528+ ) -> Installable {
529+ let mut res = installable. clone ( ) ;
471530 let hostname = hostname. as_ref ( ) . to_owned ( ) ;
472531
473- let toplevel = [ "config" , "system" , "build" , "toplevel" ]
532+ let toplevel = [ "config" , "system" , "build" , final_attr ]
474533 . into_iter ( )
475534 . map ( String :: from) ;
476535
0 commit comments