Unverified Commit 1f815066 authored by Thiago Kenji Okada's avatar Thiago Kenji Okada Committed by GitHub
Browse files

nixos-rebuild-ng: improve developer experience (#356468)

parents cbccc5f1 c66e65cb
Loading
Loading
Loading
Loading
+39 −1
Original line number Diff line number Diff line
@@ -53,6 +53,38 @@ an attempt of the rewrite.

And use `nixos-rebuild-ng` instead of `nixos-rebuild`.

## Development

Run:

```console
nix-build -A nixos-rebuild-ng.tests.ci
```

The command above will run the unit tests and linters, and also check if the
code is formatted. However, sometimes is more convenient to run just a few
tests to debug, in this case you can run:

```console
nix-shell -A nixos-rebuild-ng.devShell
```

The command above should automatically put you inside `src` directory, and you
can run:

```console
# run program
python -m nixos_rebuild
# run tests
python -m pytest
# check types
mypy .
# fix lint issues
ruff check --fix .
# format code
ruff format .
```

## Current caveats

- For now we will install it in `nixos-rebuild-ng` path by default, to avoid
@@ -96,4 +128,10 @@ And use `nixos-rebuild-ng` instead of `nixos-rebuild`.
- [ ] Improve documentation
- [ ] `nixos-rebuild repl` (calling old `nixos-rebuild` for now)
- [ ] `nix` build/bootstrap
- [ ] Reduce build closure
- [ ] Generate tab completion via [`shtab`](https://docs.iterative.ai/shtab/)
- [x] Reduce build closure

## TODON'T

- Reimplement `systemd-run` logic (will be moved to the new
  [`apply`](https://github.com/NixOS/nixpkgs/pull/344407) script)
+43 −15
Original line number Diff line number Diff line
{
  lib,
  installShellFiles,
  mkShell,
  nix,
  nixos-rebuild,
  python3,
  python3Packages,
  runCommand,
  withNgSuffix ? true,
}:
python3.pkgs.buildPythonApplication {
python3Packages.buildPythonApplication rec {
  pname = "nixos-rebuild-ng";
  version = "0.0.0";
  src = ./src;
  pyproject = true;

  build-system = with python3.pkgs; [
  build-system = with python3Packages; [
    setuptools
  ];

  dependencies = with python3.pkgs; [
  dependencies = with python3Packages; [
    tabulate
    types-tabulate
  ];

  nativeBuildInputs = [
@@ -53,22 +55,48 @@ python3.pkgs.buildPythonApplication {
      mv $out/bin/nixos-rebuild $out/bin/nixos-rebuild-ng
    '';

  nativeCheckInputs = with python3.pkgs; [
  nativeCheckInputs = with python3Packages; [
    pytestCheckHook
    mypy
    ruff
  ];

  pytestFlagsArray = [ "-vv" ];

  postCheck = ''
  passthru =
    let
      python-with-pkgs = python3.withPackages (
        ps: with ps; [
          mypy
          pytest
          ruff
          types-tabulate
          # dependencies
          tabulate
        ]
      );
    in
    {
      devShell = mkShell {
        packages = [ python-with-pkgs ];
        shellHook = ''
          cd pkgs/by-name/ni/nixos-rebuild-ng/src || true
        '';
      };

      # NOTE: this is a passthru test rather than a build-time test because we
      # want to keep the build closures small
      tests.ci = runCommand "${pname}-ci" { nativeBuildInputs = [ python-with-pkgs ]; } ''
        export RUFF_CACHE_DIR="$(mktemp -d)"

        echo -e "\x1b[32m## run mypy\x1b[0m"
    mypy nixos_rebuild tests
        mypy ${src}
        echo -e "\x1b[32m## run ruff\x1b[0m"
    ruff check nixos_rebuild tests
        ruff check ${src}
        echo -e "\x1b[32m## run ruff format\x1b[0m"
    ruff format --check nixos_rebuild tests
        ruff format --check ${src}

        touch $out
      '';
    };

  meta = {
    description = "Rebuild your NixOS configuration and switch to it, on local hosts and remote";
+18 −7
Original line number Diff line number Diff line
@@ -7,11 +7,11 @@ import sys
from subprocess import run
from typing import assert_never

from tabulate import tabulate

from .models import Action, Flake, NRError, Profile
from .nix import (
    edit,
    find_file,
    get_nixpkgs_rev,
    list_generations,
    nixos_build,
    nixos_build_flake,
@@ -88,7 +88,20 @@ def execute(argv: list[str]) -> None:
    if args.upgrade or args.upgrade_all:
        upgrade_channels(bool(args.upgrade_all))

    match action := Action(args.action):
    action = Action(args.action)
    # Only run shell scripts from the Nixpkgs tree if the action is
    # "switch", "boot", or "test". With other actions (such as "build"),
    # the user may reasonably expect that no code from the Nixpkgs tree is
    # executed, so it's safe to run nixos-rebuild against a potentially
    # untrusted tree.
    can_run = action in (Action.SWITCH, Action.BOOT, Action.TEST)
    if can_run and not flake:
        nixpkgs_path = find_file("nixpkgs", nix_flags)
        rev = get_nixpkgs_rev(nixpkgs_path)
        if nixpkgs_path and rev:
            (nixpkgs_path / ".version-suffix").write_text(rev)

    match action:
        case Action.SWITCH | Action.BOOT:
            info("building the system configuration...")
            if args.rollback:
@@ -177,6 +190,8 @@ def execute(argv: list[str]) -> None:
            if args.json:
                print(json.dumps(generations, indent=2))
            else:
                from tabulate import tabulate

                headers = {
                    "generation": "Generation",
                    "date": "Build-date",
@@ -216,7 +231,3 @@ def main() -> None:
            raise ex
        else:
            sys.exit(str(ex))


if __name__ == "__main__":
    main()
+4 −0
Original line number Diff line number Diff line
from . import main

if __name__ == "__main__":
    main()
+3 −8
Original line number Diff line number Diff line
@@ -46,9 +46,7 @@ class Action(Enum):
class Flake:
    path: Path
    attr: str
    _re: ClassVar[re.Pattern[str]] = re.compile(
        r"^(?P<path>[^\#]*)\#?(?P<attr>[^\#\"]*)$"
    )
    _re: ClassVar = re.compile(r"^(?P<path>[^\#]*)\#?(?P<attr>[^\#\"]*)$")

    @override
    def __str__(self) -> str:
@@ -59,11 +57,8 @@ class Flake:
        m = cls._re.match(flake_str)
        assert m is not None, f"got no matches for {flake_str}"
        attr = m.group("attr")
        if not attr:
            attr = f"nixosConfigurations.{hostname or "default"}"
        else:
            attr = f"nixosConfigurations.{attr}"
        return Flake(Path(m.group("path")), attr)
        nixos_attr = f"nixosConfigurations.{attr or hostname or "default"}"
        return Flake(Path(m.group("path")), nixos_attr)

    @classmethod
    def from_arg(cls, flake_arg: Any) -> Flake | None:
Loading