nixos-rebuild-ng: don't eval closure before validating variant
Because we want to be able to list variants even if one of them might not eval correctly. The eager evaluation was caused by us querying for the resulting image file name to early and is fixed by calling nix-instantiate/nix eval twice now, once for the variants, once for the image file name. fixes #394626
This commit is contained in:
parent
8e5ac55fb1
commit
ab4f5ac2f1
@ -396,7 +396,7 @@ def execute(argv: list[str]) -> None:
|
||||
raise NRError(
|
||||
"please specify one of the following "
|
||||
+ "supported image variants via --image-variant:\n"
|
||||
+ "\n".join(f"- {v}" for v in variants.keys())
|
||||
+ "\n".join(f"- {v}" for v in variants)
|
||||
)
|
||||
|
||||
match action:
|
||||
@ -518,7 +518,19 @@ def execute(argv: list[str]) -> None:
|
||||
"Done. The virtual machine can be started by running", vm_path
|
||||
)
|
||||
case Action.BUILD_IMAGE:
|
||||
disk_path = path_to_config / variants[args.image_variant]
|
||||
if flake:
|
||||
image_name = nix.get_build_image_name_flake(
|
||||
flake,
|
||||
args.image_variant,
|
||||
eval_flags=flake_common_flags,
|
||||
)
|
||||
else:
|
||||
image_name = nix.get_build_image_name(
|
||||
build_attr,
|
||||
args.image_variant,
|
||||
instantiate_flags=flake_common_flags,
|
||||
)
|
||||
disk_path = path_to_config / image_name
|
||||
print_result("Done. The disk image can be found in", disk_path)
|
||||
|
||||
case Action.EDIT:
|
||||
|
@ -8,7 +8,7 @@ from typing import Any, Callable, ClassVar, Self, TypedDict, override
|
||||
|
||||
from .process import Remote, run_wrapper
|
||||
|
||||
type ImageVariants = dict[str, str]
|
||||
type ImageVariants = list[str]
|
||||
|
||||
|
||||
class NRError(Exception):
|
||||
|
@ -266,6 +266,59 @@ def find_file(file: str, nix_flags: Args | None = None) -> Path | None:
|
||||
return Path(r.stdout.strip())
|
||||
|
||||
|
||||
def get_build_image_name(
|
||||
build_attr: BuildAttr,
|
||||
image_variant: str,
|
||||
instantiate_flags: Args | None = None,
|
||||
) -> str:
|
||||
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
|
||||
set.{build_attr.to_attr("config.system.build.images", image_variant, "passthru", "filePath")}
|
||||
"""),
|
||||
*dict_to_flags(instantiate_flags),
|
||||
],
|
||||
stdout=PIPE,
|
||||
)
|
||||
j: str = json.loads(r.stdout.strip())
|
||||
return j
|
||||
|
||||
|
||||
def get_build_image_name_flake(
|
||||
flake: Flake,
|
||||
image_variant: str,
|
||||
eval_flags: Args | None = None,
|
||||
) -> str:
|
||||
r = run_wrapper(
|
||||
[
|
||||
"nix",
|
||||
"eval",
|
||||
"--json",
|
||||
flake.to_attr(
|
||||
"config.system.build.images", image_variant, "passthru", "filePath"
|
||||
),
|
||||
*dict_to_flags(eval_flags),
|
||||
],
|
||||
stdout=PIPE,
|
||||
)
|
||||
j: str = json.loads(r.stdout.strip())
|
||||
return j
|
||||
|
||||
|
||||
def get_build_image_variants(
|
||||
build_attr: BuildAttr,
|
||||
instantiate_flags: Args | None = None,
|
||||
@ -287,7 +340,7 @@ def get_build_image_variants(
|
||||
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")}
|
||||
builtins.attrNames set.{build_attr.to_attr("config.system.build.images")}
|
||||
"""),
|
||||
*dict_to_flags(instantiate_flags),
|
||||
],
|
||||
@ -308,7 +361,7 @@ def get_build_image_variants_flake(
|
||||
"--json",
|
||||
flake.to_attr("config.system.build.images"),
|
||||
"--apply",
|
||||
"builtins.mapAttrs (n: v: v.passthru.filePath)",
|
||||
"builtins.attrNames",
|
||||
*dict_to_flags(eval_flags),
|
||||
],
|
||||
stdout=PIPE,
|
||||
|
@ -347,12 +347,7 @@ def test_execute_nix_build_image_flake(mock_run: Mock, tmp_path: Path) -> None:
|
||||
return CompletedProcess(
|
||||
[],
|
||||
0,
|
||||
"""
|
||||
{
|
||||
"azure": "nixos-image-azure-25.05.20250102.6df2492-x86_64-linux.vhd",
|
||||
"vmware": "nixos-image-vmware-25.05.20250102.6df2492-x86_64-linux.vmdk"
|
||||
}
|
||||
""",
|
||||
'"nixos-image-azure-25.05.20250102.6df2492-x86_64-linux.vhd"',
|
||||
)
|
||||
elif args[0] == "nix":
|
||||
return CompletedProcess([], 0, str(config_path))
|
||||
@ -372,7 +367,7 @@ def test_execute_nix_build_image_flake(mock_run: Mock, tmp_path: Path) -> None:
|
||||
]
|
||||
)
|
||||
|
||||
assert mock_run.call_count == 2
|
||||
assert mock_run.call_count == 3
|
||||
mock_run.assert_has_calls(
|
||||
[
|
||||
call(
|
||||
@ -382,7 +377,7 @@ def test_execute_nix_build_image_flake(mock_run: Mock, tmp_path: Path) -> None:
|
||||
"--json",
|
||||
"/path/to/config#nixosConfigurations.hostname.config.system.build.images",
|
||||
"--apply",
|
||||
"builtins.mapAttrs (n: v: v.passthru.filePath)",
|
||||
"builtins.attrNames",
|
||||
],
|
||||
check=True,
|
||||
stdout=PIPE,
|
||||
@ -401,6 +396,17 @@ def test_execute_nix_build_image_flake(mock_run: Mock, tmp_path: Path) -> None:
|
||||
stdout=PIPE,
|
||||
**DEFAULT_RUN_KWARGS,
|
||||
),
|
||||
call(
|
||||
[
|
||||
"nix",
|
||||
"eval",
|
||||
"--json",
|
||||
"/path/to/config#nixosConfigurations.hostname.config.system.build.images.azure.passthru.filePath",
|
||||
],
|
||||
check=True,
|
||||
stdout=PIPE,
|
||||
**DEFAULT_RUN_KWARGS,
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
|
@ -353,7 +353,7 @@ def test_get_build_image_variants(mock_run: Mock, tmp_path: Path) -> None:
|
||||
value = import <nixpkgs/nixos>;
|
||||
set = if builtins.isFunction value then value {} else value;
|
||||
in
|
||||
builtins.mapAttrs (n: v: v.passthru.filePath) set.config.system.build.images
|
||||
builtins.attrNames set.config.system.build.images
|
||||
"""),
|
||||
],
|
||||
stdout=PIPE,
|
||||
@ -376,7 +376,7 @@ def test_get_build_image_variants(mock_run: Mock, tmp_path: Path) -> None:
|
||||
value = import "{tmp_path}";
|
||||
set = if builtins.isFunction value then value {{}} else value;
|
||||
in
|
||||
builtins.mapAttrs (n: v: v.passthru.filePath) set.preAttr.config.system.build.images
|
||||
builtins.attrNames set.preAttr.config.system.build.images
|
||||
"""),
|
||||
"--inst-flag",
|
||||
],
|
||||
@ -411,7 +411,7 @@ def test_get_build_image_variants_flake(mock_run: Mock) -> None:
|
||||
"--json",
|
||||
"flake.nix#myAttr.config.system.build.images",
|
||||
"--apply",
|
||||
"builtins.mapAttrs (n: v: v.passthru.filePath)",
|
||||
"builtins.attrNames",
|
||||
"--eval-flag",
|
||||
],
|
||||
stdout=PIPE,
|
||||
|
Loading…
Reference in New Issue
Block a user