Loading pkgs/by-name/ni/nixos-rebuild-ng/nixos-rebuild.8.scd +14 −1 Original line number Diff line number Diff line Loading @@ -22,8 +22,9 @@ _nixos-rebuild_ \[--verbose] [--max-jobs MAX_JOBS] [--cores CORES] [--log-format \[--no-update-lock-file] [--no-write-lock-file] [--no-registries] [--commit-lock-file] [--update-input UPDATE_INPUT] [--override-input OVERRIDE_INPUT OVERRIDE_INPUT]++ \[--no-build-output] [--use-substitutes] [--help] [--file FILE] [--attr ATTR] [--flake [FLAKE]] [--no-flake] [--install-bootloader] [--profile-name PROFILE_NAME]++ \[--specialisation SPECIALISATION] [--rollback] [--upgrade] [--upgrade-all] [--json] [--ask-sudo-password] [--sudo] [--fast]++ \[--image-variant VARIANT]++ \[--build-host BUILD_HOST] [--target-host TARGET_HOST]++ \[{switch,boot,test,build,edit,repl,dry-build,dry-run,dry-activate,build-vm,build-vm-with-bootloader,list-generations}] \[{switch,boot,test,build,edit,repl,dry-build,dry-run,dry-activate,build-image,build-vm,build-vm-with-bootloader,list-generations}] # DESCRIPTION Loading Loading @@ -106,6 +107,13 @@ It must be one of the following: *repl* Opens the configuration in *nix repl*. *build-image* Build a disk-image variant, pre-configured for the given platform/provider. Select a variant with the *--image-variant* option or run without any options to get a list of available variants. $ nixos-rebuild build-image --image-variant proxmox *build-vm* Build a script that starts a NixOS virtual machine with the desired configuration. It leaves a symlink _result_ in the current directory that Loading Loading @@ -206,6 +214,11 @@ It must be one of the following: Activates given specialisation; when not specified, switching and testing will activate the base, unspecialised system. *--image-variant* _variant_ Selects an image variant to build from the _config.system.build.images_ attribute of the given configuration. A list of variants is printed if this option remains unset. *--build-host* _host_ Instead of building the new configuration locally, use the specified host to perform the build. The host needs to be accessible with ssh, and must Loading pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/__init__.py +60 −19 Original line number Diff line number Diff line Loading @@ -9,7 +9,7 @@ from typing import assert_never from . import nix, tmpdir from .constants import EXECUTABLE, WITH_NIX_2_18, WITH_REEXEC, WITH_SHELL_FILES from .models import Action, BuildAttr, Flake, NRError, Profile from .models import Action, BuildAttr, Flake, ImageVariants, NRError, Profile from .process import Remote, cleanup_ssh from .utils import Args, LogFormatter, tabulate Loading Loading @@ -176,6 +176,11 @@ def get_parser() -> tuple[argparse.ArgumentParser, dict[str, argparse.ArgumentPa "--target-host", help="Specifies host to activate the configuration" ) main_parser.add_argument("--no-build-nix", action="store_true", help="Deprecated") main_parser.add_argument( "--image-variant", help="Selects an image variant to build from the " + "config.system.build.images attribute of the given configuration", ) main_parser.add_argument("action", choices=Action.values(), nargs="?") return main_parser, sub_parsers Loading Loading @@ -285,8 +290,7 @@ def reexec( ) except CalledProcessError: logger.warning( "could not build a newer version of nixos-rebuild, " + "using current version" "could not build a newer version of nixos-rebuild, using current version" ) if drv: Loading Loading @@ -372,6 +376,7 @@ def execute(argv: list[str]) -> None: | Action.BUILD | Action.DRY_BUILD | Action.DRY_ACTIVATE | Action.BUILD_IMAGE | Action.BUILD_VM | Action.BUILD_VM_WITH_BOOTLOADER ): Loading @@ -383,7 +388,29 @@ def execute(argv: list[str]) -> None: flake_build_flags |= {"no_link": no_link, "dry_run": dry_run} rollback = bool(args.rollback) def validate_image_variant(variants: ImageVariants) -> None: if args.image_variant not in variants: raise NRError( "please specify one of the following " + "supported image variants via --image-variant:\n" + "\n".join(f"- {v}" for v in variants.keys()) ) match action: case Action.BUILD_IMAGE if flake: variants = nix.get_build_image_variants_flake( flake, eval_flags=flake_common_flags, ) validate_image_variant(variants) attr = f"config.system.build.images.{args.image_variant}" case Action.BUILD_IMAGE: variants = nix.get_build_image_variants( build_attr, instantiate_flags=common_flags, ) validate_image_variant(variants) attr = f"config.system.build.images.{args.image_variant}" case Action.BUILD_VM: attr = "config.system.build.vm" case Action.BUILD_VM_WITH_BOOTLOADER: Loading Loading @@ -460,7 +487,13 @@ def execute(argv: list[str]) -> None: sudo=args.sudo, ) if action in (Action.SWITCH, Action.BOOT, Action.TEST, Action.DRY_ACTIVATE): # Print only the result to stdout to make it easier to script def print_result(msg: str, result: str | Path) -> None: print(msg, end=" ", file=sys.stderr, flush=True) print(result, flush=True) match action: case Action.SWITCH | Action.BOOT | Action.TEST | Action.DRY_ACTIVATE: nix.switch_to_configuration( path_to_config, action, Loading @@ -469,16 +502,22 @@ def execute(argv: list[str]) -> None: specialisation=args.specialisation, install_bootloader=args.install_bootloader, ) elif action in (Action.BUILD_VM, Action.BUILD_VM_WITH_BOOTLOADER): case Action.BUILD_VM | Action.BUILD_VM_WITH_BOOTLOADER: # If you get `not-found`, please open an issue vm_path = next(path_to_config.glob("bin/run-*-vm"), "not-found") print( f"Done. The virtual machine can be started by running '{vm_path}'" print_result( "Done. The virtual machine can be started by running", vm_path ) case Action.BUILD_IMAGE: disk_path = path_to_config / variants[args.image_variant] print_result("Done. The disk image can be found in", disk_path) case Action.EDIT: nix.edit(flake, flake_build_flags) case Action.DRY_RUN: assert False, "DRY_RUN should be a DRY_BUILD alias" raise AssertionError("DRY_RUN should be a DRY_BUILD alias") case Action.LIST_GENERATIONS: generations = nix.list_generations(profile) if args.json: Loading @@ -494,11 +533,13 @@ def execute(argv: list[str]) -> None: "current": "Current", } print(tabulate(generations, headers=headers)) case Action.REPL: if flake: nix.repl_flake("toplevel", flake, flake_build_flags) else: nix.repl("system", build_attr, build_flags) case _: assert_never(action) Loading pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/models.py +3 −0 Original line number Diff line number Diff line Loading @@ -8,6 +8,8 @@ from typing import Any, Callable, ClassVar, Self, TypedDict, override from .process import Remote, run_wrapper type ImageVariants = dict[str, str] class NRError(Exception): "nixos-rebuild general error." Loading @@ -30,6 +32,7 @@ class Action(Enum): DRY_BUILD = "dry-build" DRY_RUN = "dry-run" DRY_ACTIVATE = "dry-activate" BUILD_IMAGE = "build-image" BUILD_VM = "build-vm" BUILD_VM_WITH_BOOTLOADER = "build-vm-with-bootloader" LIST_GENERATIONS = "list-generations" Loading pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/nix.py +54 −0 Original line number Diff line number Diff line import json import logging import os import textwrap from concurrent.futures import ThreadPoolExecutor from datetime import datetime from importlib.resources import files Loading @@ -17,6 +19,7 @@ from .models import ( Flake, Generation, GenerationJson, ImageVariants, NRError, Profile, Remote, Loading Loading @@ -263,6 +266,57 @@ def find_file(file: str, nix_flags: Args | None = None) -> Path | None: return Path(r.stdout.strip()) def get_build_image_variants( build_attr: BuildAttr, instantiate_flags: Args | None = None, ) -> ImageVariants: path = ( f'"{build_attr.path.resolve()}"' if isinstance(build_attr.path, Path) else build_attr.path ) r = run_wrapper( [ "nix-instantiate", "--eval", "--strict", "--json", "--expr", textwrap.dedent(f""" let value = import {path}; set = if builtins.isFunction value then value {{}} else value; in builtins.mapAttrs (n: v: v.passthru.filePath) set.{build_attr.to_attr("config.system.build.images")} """), *dict_to_flags(instantiate_flags), ], stdout=PIPE, ) j: ImageVariants = json.loads(r.stdout.strip()) return j def get_build_image_variants_flake( flake: Flake, eval_flags: Args | None = None, ) -> ImageVariants: r = run_wrapper( [ "nix", "eval", "--json", flake.to_attr("config.system.build.images"), "--apply", "builtins.mapAttrs (n: v: v.passthru.filePath)", *dict_to_flags(eval_flags), ], stdout=PIPE, ) j: ImageVariants = json.loads(r.stdout.strip()) return j def get_nixpkgs_rev(nixpkgs_path: Path | None) -> str | None: """Get Nixpkgs path as a Git revision. Loading pkgs/by-name/ni/nixos-rebuild-ng/src/pyproject.toml +1 −1 Original line number Diff line number Diff line Loading @@ -52,4 +52,4 @@ extend-select = [ [tool.pytest.ini_options] pythonpath = ["."] addopts = ["--import-mode=importlib"] addopts = "--import-mode=importlib" Loading
pkgs/by-name/ni/nixos-rebuild-ng/nixos-rebuild.8.scd +14 −1 Original line number Diff line number Diff line Loading @@ -22,8 +22,9 @@ _nixos-rebuild_ \[--verbose] [--max-jobs MAX_JOBS] [--cores CORES] [--log-format \[--no-update-lock-file] [--no-write-lock-file] [--no-registries] [--commit-lock-file] [--update-input UPDATE_INPUT] [--override-input OVERRIDE_INPUT OVERRIDE_INPUT]++ \[--no-build-output] [--use-substitutes] [--help] [--file FILE] [--attr ATTR] [--flake [FLAKE]] [--no-flake] [--install-bootloader] [--profile-name PROFILE_NAME]++ \[--specialisation SPECIALISATION] [--rollback] [--upgrade] [--upgrade-all] [--json] [--ask-sudo-password] [--sudo] [--fast]++ \[--image-variant VARIANT]++ \[--build-host BUILD_HOST] [--target-host TARGET_HOST]++ \[{switch,boot,test,build,edit,repl,dry-build,dry-run,dry-activate,build-vm,build-vm-with-bootloader,list-generations}] \[{switch,boot,test,build,edit,repl,dry-build,dry-run,dry-activate,build-image,build-vm,build-vm-with-bootloader,list-generations}] # DESCRIPTION Loading Loading @@ -106,6 +107,13 @@ It must be one of the following: *repl* Opens the configuration in *nix repl*. *build-image* Build a disk-image variant, pre-configured for the given platform/provider. Select a variant with the *--image-variant* option or run without any options to get a list of available variants. $ nixos-rebuild build-image --image-variant proxmox *build-vm* Build a script that starts a NixOS virtual machine with the desired configuration. It leaves a symlink _result_ in the current directory that Loading Loading @@ -206,6 +214,11 @@ It must be one of the following: Activates given specialisation; when not specified, switching and testing will activate the base, unspecialised system. *--image-variant* _variant_ Selects an image variant to build from the _config.system.build.images_ attribute of the given configuration. A list of variants is printed if this option remains unset. *--build-host* _host_ Instead of building the new configuration locally, use the specified host to perform the build. The host needs to be accessible with ssh, and must Loading
pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/__init__.py +60 −19 Original line number Diff line number Diff line Loading @@ -9,7 +9,7 @@ from typing import assert_never from . import nix, tmpdir from .constants import EXECUTABLE, WITH_NIX_2_18, WITH_REEXEC, WITH_SHELL_FILES from .models import Action, BuildAttr, Flake, NRError, Profile from .models import Action, BuildAttr, Flake, ImageVariants, NRError, Profile from .process import Remote, cleanup_ssh from .utils import Args, LogFormatter, tabulate Loading Loading @@ -176,6 +176,11 @@ def get_parser() -> tuple[argparse.ArgumentParser, dict[str, argparse.ArgumentPa "--target-host", help="Specifies host to activate the configuration" ) main_parser.add_argument("--no-build-nix", action="store_true", help="Deprecated") main_parser.add_argument( "--image-variant", help="Selects an image variant to build from the " + "config.system.build.images attribute of the given configuration", ) main_parser.add_argument("action", choices=Action.values(), nargs="?") return main_parser, sub_parsers Loading Loading @@ -285,8 +290,7 @@ def reexec( ) except CalledProcessError: logger.warning( "could not build a newer version of nixos-rebuild, " + "using current version" "could not build a newer version of nixos-rebuild, using current version" ) if drv: Loading Loading @@ -372,6 +376,7 @@ def execute(argv: list[str]) -> None: | Action.BUILD | Action.DRY_BUILD | Action.DRY_ACTIVATE | Action.BUILD_IMAGE | Action.BUILD_VM | Action.BUILD_VM_WITH_BOOTLOADER ): Loading @@ -383,7 +388,29 @@ def execute(argv: list[str]) -> None: flake_build_flags |= {"no_link": no_link, "dry_run": dry_run} rollback = bool(args.rollback) def validate_image_variant(variants: ImageVariants) -> None: if args.image_variant not in variants: raise NRError( "please specify one of the following " + "supported image variants via --image-variant:\n" + "\n".join(f"- {v}" for v in variants.keys()) ) match action: case Action.BUILD_IMAGE if flake: variants = nix.get_build_image_variants_flake( flake, eval_flags=flake_common_flags, ) validate_image_variant(variants) attr = f"config.system.build.images.{args.image_variant}" case Action.BUILD_IMAGE: variants = nix.get_build_image_variants( build_attr, instantiate_flags=common_flags, ) validate_image_variant(variants) attr = f"config.system.build.images.{args.image_variant}" case Action.BUILD_VM: attr = "config.system.build.vm" case Action.BUILD_VM_WITH_BOOTLOADER: Loading Loading @@ -460,7 +487,13 @@ def execute(argv: list[str]) -> None: sudo=args.sudo, ) if action in (Action.SWITCH, Action.BOOT, Action.TEST, Action.DRY_ACTIVATE): # Print only the result to stdout to make it easier to script def print_result(msg: str, result: str | Path) -> None: print(msg, end=" ", file=sys.stderr, flush=True) print(result, flush=True) match action: case Action.SWITCH | Action.BOOT | Action.TEST | Action.DRY_ACTIVATE: nix.switch_to_configuration( path_to_config, action, Loading @@ -469,16 +502,22 @@ def execute(argv: list[str]) -> None: specialisation=args.specialisation, install_bootloader=args.install_bootloader, ) elif action in (Action.BUILD_VM, Action.BUILD_VM_WITH_BOOTLOADER): case Action.BUILD_VM | Action.BUILD_VM_WITH_BOOTLOADER: # If you get `not-found`, please open an issue vm_path = next(path_to_config.glob("bin/run-*-vm"), "not-found") print( f"Done. The virtual machine can be started by running '{vm_path}'" print_result( "Done. The virtual machine can be started by running", vm_path ) case Action.BUILD_IMAGE: disk_path = path_to_config / variants[args.image_variant] print_result("Done. The disk image can be found in", disk_path) case Action.EDIT: nix.edit(flake, flake_build_flags) case Action.DRY_RUN: assert False, "DRY_RUN should be a DRY_BUILD alias" raise AssertionError("DRY_RUN should be a DRY_BUILD alias") case Action.LIST_GENERATIONS: generations = nix.list_generations(profile) if args.json: Loading @@ -494,11 +533,13 @@ def execute(argv: list[str]) -> None: "current": "Current", } print(tabulate(generations, headers=headers)) case Action.REPL: if flake: nix.repl_flake("toplevel", flake, flake_build_flags) else: nix.repl("system", build_attr, build_flags) case _: assert_never(action) Loading
pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/models.py +3 −0 Original line number Diff line number Diff line Loading @@ -8,6 +8,8 @@ from typing import Any, Callable, ClassVar, Self, TypedDict, override from .process import Remote, run_wrapper type ImageVariants = dict[str, str] class NRError(Exception): "nixos-rebuild general error." Loading @@ -30,6 +32,7 @@ class Action(Enum): DRY_BUILD = "dry-build" DRY_RUN = "dry-run" DRY_ACTIVATE = "dry-activate" BUILD_IMAGE = "build-image" BUILD_VM = "build-vm" BUILD_VM_WITH_BOOTLOADER = "build-vm-with-bootloader" LIST_GENERATIONS = "list-generations" Loading
pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/nix.py +54 −0 Original line number Diff line number Diff line import json import logging import os import textwrap from concurrent.futures import ThreadPoolExecutor from datetime import datetime from importlib.resources import files Loading @@ -17,6 +19,7 @@ from .models import ( Flake, Generation, GenerationJson, ImageVariants, NRError, Profile, Remote, Loading Loading @@ -263,6 +266,57 @@ def find_file(file: str, nix_flags: Args | None = None) -> Path | None: return Path(r.stdout.strip()) def get_build_image_variants( build_attr: BuildAttr, instantiate_flags: Args | None = None, ) -> ImageVariants: path = ( f'"{build_attr.path.resolve()}"' if isinstance(build_attr.path, Path) else build_attr.path ) r = run_wrapper( [ "nix-instantiate", "--eval", "--strict", "--json", "--expr", textwrap.dedent(f""" let value = import {path}; set = if builtins.isFunction value then value {{}} else value; in builtins.mapAttrs (n: v: v.passthru.filePath) set.{build_attr.to_attr("config.system.build.images")} """), *dict_to_flags(instantiate_flags), ], stdout=PIPE, ) j: ImageVariants = json.loads(r.stdout.strip()) return j def get_build_image_variants_flake( flake: Flake, eval_flags: Args | None = None, ) -> ImageVariants: r = run_wrapper( [ "nix", "eval", "--json", flake.to_attr("config.system.build.images"), "--apply", "builtins.mapAttrs (n: v: v.passthru.filePath)", *dict_to_flags(eval_flags), ], stdout=PIPE, ) j: ImageVariants = json.loads(r.stdout.strip()) return j def get_nixpkgs_rev(nixpkgs_path: Path | None) -> str | None: """Get Nixpkgs path as a Git revision. Loading
pkgs/by-name/ni/nixos-rebuild-ng/src/pyproject.toml +1 −1 Original line number Diff line number Diff line Loading @@ -52,4 +52,4 @@ extend-select = [ [tool.pytest.ini_options] pythonpath = ["."] addopts = ["--import-mode=importlib"] addopts = "--import-mode=importlib"