11use std:: env;
2+ use std:: ffi:: OsString ;
23use std:: path:: PathBuf ;
34
45use color_eyre:: eyre:: bail;
@@ -45,7 +46,7 @@ impl HomeRebuildArgs {
4546 let out_path: Box < dyn crate :: util:: MaybeTempPath > = match self . common . out_link {
4647 Some ( ref p) => Box :: new ( p. clone ( ) ) ,
4748 None => Box :: new ( {
48- let dir = tempfile:: Builder :: new ( ) . prefix ( "nh-os " ) . tempdir ( ) ?;
49+ let dir = tempfile:: Builder :: new ( ) . prefix ( "nh-home " ) . tempdir ( ) ?;
4950 ( dir. as_ref ( ) . join ( "result" ) , dir)
5051 } ) ,
5152 } ;
@@ -71,7 +72,12 @@ impl HomeRebuildArgs {
7172 self . common . installable . clone ( )
7273 } ;
7374
74- let toplevel = toplevel_for ( installable, true , & self . extra_args ) ?;
75+ let toplevel = toplevel_for (
76+ installable,
77+ true ,
78+ & self . extra_args ,
79+ self . configuration . clone ( ) ,
80+ ) ?;
7581
7682 commands:: Build :: new ( toplevel)
7783 . extra_arg ( "--out-link" )
@@ -106,7 +112,7 @@ impl HomeRebuildArgs {
106112
107113 debug ! ( "target_specialisation: {target_specialisation:?}" ) ;
108114
109- let target_profile = match & target_specialisation {
115+ let target_profile: Box < dyn crate :: util :: MaybeTempPath > = match & target_specialisation {
110116 None => out_path,
111117 Some ( spec) => Box :: new ( out_path. get_path ( ) . join ( "specialisation" ) . join ( spec) ) ,
112118 } ;
@@ -158,13 +164,14 @@ fn toplevel_for<I, S>(
158164 installable : Installable ,
159165 push_drv : bool ,
160166 extra_args : I ,
167+ configuration_name : Option < String > ,
161168) -> Result < Installable >
162169where
163170 I : IntoIterator < Item = S > ,
164171 S : AsRef < std:: ffi:: OsStr > ,
165172{
166173 let mut res = installable. clone ( ) ;
167- let extra_args = {
174+ let extra_args: Vec < OsString > = {
168175 let mut vec = Vec :: new ( ) ;
169176 for elem in extra_args. into_iter ( ) {
170177 vec. push ( elem. as_ref ( ) . to_owned ( ) ) ;
@@ -180,25 +187,27 @@ where
180187 Installable :: Flake {
181188 ref reference,
182189 ref mut attribute,
183- } => ' flake: {
184- // If user explicitly selects some other attribute, don't push homeConfigurations
190+ } => {
191+ // If user explicitly selects some other attribute in the installable itself
192+ // then don't push homeConfigurations
185193 if !attribute. is_empty ( ) {
186- break ' flake;
194+ debug ! (
195+ "Using explicit attribute path from installable: {:?}" ,
196+ attribute
197+ ) ;
198+ return Ok ( res) ;
187199 }
188200
189201 attribute. push ( String :: from ( "homeConfigurations" ) ) ;
190202
191- // check for <user> and <user@hostname>
192- let username = std:: env:: var ( "USER" ) . expect ( "Couldn't get username" ) ;
193- let hostname = get_hostname ( ) ?;
194-
195203 let flake_reference = reference. clone ( ) ;
204+ let mut found_config = false ;
196205
197- let mut tried = vec ! [ ] ;
198-
199- for attr in [ format ! ( "{username}@{hostname}" ) , username . to_string ( ) ] {
200- let func = format ! ( r#" x: x ? "{}" "# , attr ) ;
201- let res = commands:: Command :: new ( "nix" )
206+ // Check if an explicit configuration name was provided via the flag
207+ if let Some ( config_name ) = configuration_name {
208+ // Verify the provided configuration exists
209+ let func = format ! ( r#" x: x ? "{}" "# , config_name ) ;
210+ let check_res = commands:: Command :: new ( "nix" )
202211 . arg ( "eval" )
203212 . args ( & extra_args)
204213 . arg ( "--apply" )
@@ -211,41 +220,109 @@ where
211220 . to_args ( ) ,
212221 )
213222 . run_capture ( )
214- . expect ( "Checking home-manager output" ) ;
215-
216- tried . push ( {
217- let mut attribute = attribute . clone ( ) ;
218- attribute . push ( attr . clone ( ) ) ;
219- attribute
220- } ) ;
221-
222- match res . map ( |s| s. trim ( ) . to_owned ( ) ) . as_deref ( ) {
223+ . map_err ( |e| {
224+ color_eyre :: eyre :: eyre! (
225+ "Failed running nix eval to check for explicit configuration '{}': {}" ,
226+ config_name ,
227+ e
228+ )
229+ } ) ? ;
230+
231+ match check_res . map ( |s| s. trim ( ) . to_owned ( ) ) . as_deref ( ) {
223232 Some ( "true" ) => {
224- attribute. push ( attr. clone ( ) ) ;
233+ debug ! ( "Using explicit configuration from flag: {}" , config_name) ;
234+ attribute. push ( config_name. clone ( ) ) ;
225235 if push_drv {
226- attribute. extend ( toplevel) ;
236+ attribute. extend ( toplevel. clone ( ) ) ;
227237 }
228- break ' flake ;
238+ found_config = true ;
229239 }
230240 _ => {
231- continue ;
241+ // Explicit config provided but not found
242+ let tried_attr_path = {
243+ let mut attr_path = attribute. clone ( ) ;
244+ attr_path. push ( config_name. clone ( ) ) ;
245+ Installable :: Flake {
246+ reference : flake_reference. clone ( ) ,
247+ attribute : attr_path,
248+ }
249+ . to_args ( )
250+ . join ( " " )
251+ } ;
252+ bail ! ( "Explicitly specified home-manager configuration not found: {tried_attr_path}" ) ;
232253 }
233254 }
234255 }
235256
236- let tried_str = tried
237- . into_iter ( )
238- . map ( |a| {
239- let f = Installable :: Flake {
240- reference : flake_reference. clone ( ) ,
241- attribute : a,
257+ // If no explicit config was found via flag, try automatic detection
258+ if !found_config {
259+ let username = std:: env:: var ( "USER" ) . expect ( "Couldn't get username" ) ;
260+ let hostname = get_hostname ( ) ?;
261+ let mut tried = vec ! [ ] ;
262+
263+ for attr_name in [ format ! ( "{username}@{hostname}" ) , username. to_string ( ) ] {
264+ let func = format ! ( r#" x: x ? "{}" "# , attr_name) ;
265+ let check_res = commands:: Command :: new ( "nix" )
266+ . arg ( "eval" )
267+ . args ( & extra_args)
268+ . arg ( "--apply" )
269+ . arg ( func)
270+ . args (
271+ ( Installable :: Flake {
272+ reference : flake_reference. clone ( ) ,
273+ attribute : attribute. clone ( ) ,
274+ } )
275+ . to_args ( ) ,
276+ )
277+ . run_capture ( )
278+ . map_err ( |e| {
279+ color_eyre:: eyre:: eyre!(
280+ "Failed running nix eval to check for automatic configuration '{}': {}" ,
281+ attr_name,
282+ e
283+ )
284+ } ) ?;
285+
286+ let current_try_attr = {
287+ let mut attr_path = attribute. clone ( ) ;
288+ attr_path. push ( attr_name. clone ( ) ) ;
289+ attr_path
242290 } ;
243- f. to_args ( ) . join ( " " )
244- } )
245- . collect :: < Vec < _ > > ( )
246- . join ( ", " ) ;
291+ tried. push ( current_try_attr. clone ( ) ) ;
292+
293+ match check_res. map ( |s| s. trim ( ) . to_owned ( ) ) . as_deref ( ) {
294+ Some ( "true" ) => {
295+ debug ! ( "Using automatically detected configuration: {}" , attr_name) ;
296+ attribute. push ( attr_name. clone ( ) ) ;
297+ if push_drv {
298+ attribute. extend ( toplevel. clone ( ) ) ;
299+ }
300+ found_config = true ;
301+ break ;
302+ }
303+ _ => {
304+ continue ;
305+ }
306+ }
307+ }
247308
248- bail ! ( "Couldn't find home-manager configuration, tried {tried_str}" ) ;
309+ // If still not found after automatic detection, error out
310+ if !found_config {
311+ let tried_str = tried
312+ . into_iter ( )
313+ . map ( |a| {
314+ Installable :: Flake {
315+ reference : flake_reference. clone ( ) ,
316+ attribute : a,
317+ }
318+ . to_args ( )
319+ . join ( " " )
320+ } )
321+ . collect :: < Vec < _ > > ( )
322+ . join ( ", " ) ;
323+ bail ! ( "Couldn't find home-manager configuration automatically, tried: {tried_str}" ) ;
324+ }
325+ }
249326 }
250327 Installable :: File {
251328 ref mut attribute, ..
@@ -288,7 +365,12 @@ impl HomeReplArgs {
288365 self . installable
289366 } ;
290367
291- let toplevel = toplevel_for ( installable, false , & self . extra_args ) ?;
368+ let toplevel = toplevel_for (
369+ installable,
370+ false ,
371+ & self . extra_args ,
372+ self . configuration . clone ( ) ,
373+ ) ?;
292374
293375 Command :: new ( "nix" )
294376 . arg ( "repl" )
0 commit comments