nixos-rebuild-ng: add repl

This commit is contained in:
Thiago Kenji Okada 2024-11-27 19:07:16 +00:00
parent d325edd627
commit 88b4eb3aeb
8 changed files with 102 additions and 10 deletions

View File

@ -126,7 +126,7 @@ ruff format .
- [ ] Change module system to allow easier opt-in, like
`system.switch.enableNg` for `switch-to-configuration-ng`
- [ ] Improve documentation
- [ ] `nixos-rebuild repl` (calling old `nixos-rebuild` for now)
- [x] `nixos-rebuild repl`
- [ ] `nix` build/bootstrap
- [ ] Generate tab completion via [`shtab`](https://docs.iterative.ai/shtab/)
- [x] Reduce build closure

View File

@ -39,11 +39,6 @@ python3Packages.buildPythonApplication rec {
nix
];
preBuild = ''
substituteInPlace nixos_rebuild/__init__.py \
--subst-var-by nixos_rebuild ${lib.getExe nixos-rebuild}
'';
postInstall =
''
installManPage ${nixos-rebuild}/share/man/man8/nixos-rebuild.8

View File

@ -1,7 +1,6 @@
import argparse
import atexit
import json
import os
import sys
from pathlib import Path
from subprocess import run

View File

@ -1,6 +1,8 @@
import os
from datetime import datetime
from importlib.resources import files
from pathlib import Path
from string import Template
from subprocess import PIPE, CalledProcessError
from typing import Final
@ -18,6 +20,7 @@ from .process import run_wrapper
from .utils import Args, dict_to_flags, info
FLAKE_FLAGS: Final = ["--extra-experimental-features", "nix-command flakes"]
FLAKE_REPL_TEMPLATE: Final = "repl.template.nix"
def copy_closure(
@ -279,6 +282,41 @@ def nixos_build_flake(
return Path(r.stdout.strip())
def repl(attr: str, build_attr: BuildAttr | None, **nix_flags: Args) -> None:
run_args: list[str | Path] = ["nix", "repl", "--file"]
if build_attr:
run_args.append(build_attr.path)
if build_attr.attr:
run_args.append(build_attr.attr)
run_wrapper([*run_args, *dict_to_flags(nix_flags)])
else:
run_wrapper([*run_args, "<nixpkgs/nixos>", *dict_to_flags(nix_flags)])
def repl_flake(attr: str, flake: Flake, **flake_flags: Args) -> None:
expr = Template(
files(__package__).joinpath(FLAKE_REPL_TEMPLATE).read_text()
).substitute(
flake_path=flake.path,
flake_attr=flake.attr,
bold="\033[1m",
blue="\033[34;1m",
attention="\033[35;1m",
reset="\033[0m",
)
run_wrapper(
[
"nix",
*FLAKE_FLAGS,
"repl",
"--impure",
"--expr",
expr,
*dict_to_flags(flake_flags),
]
)
def rollback(profile: Profile, target_host: Remote | None, sudo: bool) -> Path:
"Rollback Nix profile, like one created by `nixos-rebuild switch`."
run_wrapper(

View File

@ -0,0 +1,40 @@
let
flake = builtins.getFlake ''${flake_path}'';
configuration = flake.${flake_attr};
motd = ''
$${"\n"}
Hello and welcome to the NixOS configuration
${flake_attr}
in ${flake_path}
The following is loaded into nix repl's scope:
- ${blue}config${reset} All option values
- ${blue}options${reset} Option data and metadata
- ${blue}pkgs${reset} Nixpkgs package set
- ${blue}lib${reset} Nixpkgs library functions
- other module arguments
- ${blue}flake${reset} Flake outputs, inputs and source info of ${flake_path}
Use tab completion to browse around ${blue}config${reset}.
Use ${bold}:r${reset} to ${bold}reload${reset} everything after making a change in the flake.
(assuming ${flake_path} is a mutable flake ref)
See ${bold}:?${reset} for more repl commands.
${attention}warning:${reset} nixos-rebuild repl does not currently enforce pure evaluation.
'';
scope =
assert configuration._type or null == ''configuration'';
assert configuration.class or ''nixos'' == ''nixos'';
configuration._module.args
// configuration._module.specialArgs
// {
inherit (configuration) config options;
lib = configuration.lib or configuration.pkgs.lib;
inherit flake;
};
in
builtins.seq scope builtins.trace motd scope

View File

@ -9,6 +9,9 @@ version = "0.0.0"
[project.scripts]
nixos-rebuild = "nixos_rebuild:main"
[tool.setuptools.package-data]
nixos_rebuild = ["*.template.nix"]
[tool.mypy]
# `--strict` config, but explicit options to avoid breaking build when mypy is
# updated

View File

@ -17,9 +17,7 @@ def test_building_attr_from_arg() -> None:
assert m.BuildAttr.from_arg("attr", "file.nix") == m.BuildAttr(
Path("file.nix"), "attr"
)
assert m.BuildAttr.from_arg(None, "file.nix") == m.BuildAttr(
Path("file.nix"), None
)
assert m.BuildAttr.from_arg(None, "file.nix") == m.BuildAttr(Path("file.nix"), None)
def test_flake_parse() -> None:

View File

@ -248,6 +248,25 @@ def test_nixos_build(mock_run: Any, monkeypatch: Any) -> None:
)
@patch(get_qualified_name(n.run_wrapper, n), autospec=True)
def test_repl(mock_run: Any) -> None:
n.repl("attr", None, nix_flag=True)
mock_run.assert_called_with(
["nix", "repl", "--file", "<nixpkgs/nixos>", "--nix-flag"]
)
n.repl("attr", m.BuildAttr(Path("file.nix"), "myAttr"))
mock_run.assert_called_with(["nix", "repl", "--file", Path("file.nix"), "myAttr"])
@patch(get_qualified_name(n.run_wrapper, n), autospec=True)
def test_repl_flake(mock_run: Any) -> None:
n.repl_flake("attr", m.Flake(Path("flake.nix"), "myAttr"), nix_flag=True)
# This method would be really annoying to test, and it is not that important
# So just check that we are at least calling it
assert mock_run.called
@patch(get_qualified_name(n.run_wrapper, n), autospec=True)
def test_rollback(mock_run: Any, tmp_path: Path) -> None:
path = tmp_path / "test"