55import sys
66from pathlib import Path
77from subprocess import CalledProcessError , run
8+ from textwrap import dedent
89from typing import Final , assert_never
910
1011from . import nix , tmpdir
1112from .constants import EXECUTABLE , WITH_NIX_2_18 , WITH_REEXEC , WITH_SHELL_FILES
12- from .models import Action , BuildAttr , Flake , ImageVariants , NRError , Profile
13+ from .models import Action , BuildAttr , Flake , ImageVariants , NixOSRebuildError , Profile
1314from .process import Remote , cleanup_ssh
1415from .utils import Args , LogFormatter , tabulate
1516
@@ -99,7 +100,7 @@ def get_parser() -> tuple[argparse.ArgumentParser, dict[str, argparse.ArgumentPa
99100 "--attr" ,
100101 "-A" ,
101102 help = "Enable and build the NixOS system from nix file and use the "
102- + "specified attribute path from file specified by the --file option" ,
103+ "specified attribute path from file specified by the --file option" ,
103104 )
104105 main_parser .add_argument (
105106 "--flake" ,
@@ -117,7 +118,7 @@ def get_parser() -> tuple[argparse.ArgumentParser, dict[str, argparse.ArgumentPa
117118 "--install-bootloader" ,
118119 action = "store_true" ,
119120 help = "Causes the boot loader to be (re)installed on the device specified "
120- + "by the relevant configuration options" ,
121+ "by the relevant configuration options" ,
121122 )
122123 main_parser .add_argument (
123124 "--install-grub" ,
@@ -142,7 +143,7 @@ def get_parser() -> tuple[argparse.ArgumentParser, dict[str, argparse.ArgumentPa
142143 "--upgrade" ,
143144 action = "store_true" ,
144145 help = "Update the root user's channel named 'nixos' before rebuilding "
145- + "the system and channels which have a file named '.update-on-nixos-rebuild'" ,
146+ "the system and channels which have a file named '.update-on-nixos-rebuild'" ,
146147 )
147148 main_parser .add_argument (
148149 "--upgrade-all" ,
@@ -186,7 +187,7 @@ def get_parser() -> tuple[argparse.ArgumentParser, dict[str, argparse.ArgumentPa
186187 main_parser .add_argument (
187188 "--image-variant" ,
188189 help = "Selects an image variant to build from the "
189- + "config.system.build.images attribute of the given configuration" ,
190+ "config.system.build.images attribute of the given configuration" ,
190191 )
191192 main_parser .add_argument ("action" , choices = Action .values (), nargs = "?" )
192193
@@ -321,14 +322,45 @@ def reexec(
321322 # - Exec format error (e.g.: another OS/CPU arch)
322323 logger .warning (
323324 "could not re-exec in a newer version of nixos-rebuild, "
324- + "using current version" ,
325+ "using current version" ,
325326 exc_info = logger .isEnabledFor (logging .DEBUG ),
326327 )
327328 # We already run clean-up, let's re-exec in the current version
328329 # to avoid issues
329330 os .execve (current , argv , os .environ | {"_NIXOS_REBUILD_REEXEC" : "1" })
330331
331332
333+ def validate_image_variant (image_variant : str , variants : ImageVariants ) -> None :
334+ if image_variant not in variants :
335+ raise NixOSRebuildError (
336+ "please specify one of the following supported image variants via "
337+ "--image-variant:\n " + "\n " .join (f"- { v } " for v in variants )
338+ )
339+
340+
341+ def validate_nixos_config (path_to_config : Path ) -> None :
342+ if not (path_to_config / "nixos-version" ).exists () and not os .environ .get (
343+ "NIXOS_REBUILD_I_UNDERSTAND_THE_CONSEQUENCES_PLEASE_BREAK_MY_SYSTEM"
344+ ):
345+ msg = dedent (
346+ # the lowercase for the first letter below is proposital
347+ f"""
348+ your NixOS configuration path seems to be missing essential files.
349+ To avoid corrupting your current NixOS installation, the activation will abort.
350+
351+ This could be caused by Nix bug: https://github.com/NixOS/nix/issues/13367.
352+ This is the evaluated NixOS configuration path: { path_to_config } .
353+ Change the directory to somewhere else (e.g., `cd $HOME`) before trying again.
354+
355+ If you think this is a mistake, you can set the environment variable
356+ NIXOS_REBUILD_I_UNDERSTAND_THE_CONSEQUENCES_PLEASE_BREAK_MY_SYSTEM to 1
357+ and re-run the command to continue.
358+ Please open an issue if this is the case.
359+ """
360+ ).strip ()
361+ raise NixOSRebuildError (msg )
362+
363+
332364def execute (argv : list [str ]) -> None :
333365 args , args_groups = parse_args (argv )
334366
@@ -393,28 +425,20 @@ def execute(argv: list[str]) -> None:
393425 no_link = action in (Action .SWITCH , Action .BOOT )
394426 rollback = bool (args .rollback )
395427
396- def validate_image_variant (variants : ImageVariants ) -> None :
397- if args .image_variant not in variants :
398- raise NRError (
399- "please specify one of the following "
400- + "supported image variants via --image-variant:\n "
401- + "\n " .join (f"- { v } " for v in variants )
402- )
403-
404428 match action :
405429 case Action .BUILD_IMAGE if flake :
406430 variants = nix .get_build_image_variants_flake (
407431 flake ,
408432 eval_flags = flake_common_flags ,
409433 )
410- validate_image_variant (variants )
434+ validate_image_variant (args . image_variant , variants )
411435 attr = f"config.system.build.images.{ args .image_variant } "
412436 case Action .BUILD_IMAGE :
413437 variants = nix .get_build_image_variants (
414438 build_attr ,
415439 instantiate_flags = common_flags ,
416440 )
417- validate_image_variant (variants )
441+ validate_image_variant (args . image_variant , variants )
418442 attr = f"config.system.build.images.{ args .image_variant } "
419443 case Action .BUILD_VM :
420444 attr = "config.system.build.vm"
@@ -435,9 +459,11 @@ def validate_image_variant(variants: ImageVariants) -> None:
435459 if maybe_path_to_config : # kinda silly but this makes mypy happy
436460 path_to_config = maybe_path_to_config
437461 else :
438- raise NRError ("could not find previous generation" )
462+ raise NixOSRebuildError ("could not find previous generation" )
439463 case (_, True , _, _):
440- raise NRError (f"--rollback is incompatible with '{ action } '" )
464+ raise NixOSRebuildError (
465+ f"--rollback is incompatible with '{ action } '"
466+ )
441467 case (_, False , Remote (_), Flake (_)):
442468 path_to_config = nix .build_remote_flake (
443469 attr ,
@@ -488,6 +514,7 @@ def validate_image_variant(variants: ImageVariants) -> None:
488514 copy_flags = copy_flags ,
489515 )
490516 if action in (Action .SWITCH , Action .BOOT ):
517+ validate_nixos_config (path_to_config )
491518 nix .set_profile (
492519 profile ,
493520 path_to_config ,
0 commit comments