Unverified Commit 13c5ed98 authored by Thiago Kenji Okada's avatar Thiago Kenji Okada Committed by GitHub
Browse files

nixos-rebuild-ng: move more code to services.py (#420826)

parents 80028195 c2ba67af
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -133,7 +133,8 @@ python3Packages.buildPythonApplication rec {
        };

        inherit (nixosTests)
          nixos-rebuild-install-bootloader-ng
          # FIXME: this test is disabled since it times out in @ofborg
          # nixos-rebuild-install-bootloader-ng
          nixos-rebuild-specialisations-ng
          nixos-rebuild-target-host-ng
          ;
+13 −29
Original line number Diff line number Diff line
import argparse
import json
import logging
import os
import sys
from subprocess import CalledProcessError, run
from typing import Final, assert_never

from . import nix
from . import nix, services
from .constants import EXECUTABLE, WITH_NIX_2_18, WITH_REEXEC, WITH_SHELL_FILES
from .models import Action, BuildAttr, Flake, Profile
from .process import Remote
from .services import build_and_activate_system, reexec
from .utils import LogFormatter, tabulate
from .utils import LogFormatter

logger: Final = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
@@ -301,7 +299,7 @@ def execute(argv: list[str]) -> None:
        and not args.no_reexec
        and not os.environ.get("_NIXOS_REBUILD_REEXEC")
    ):
        reexec(argv, args, build_flags, flake_build_flags)
        services.reexec(argv, args, build_flags, flake_build_flags)

    profile = Profile.from_arg(args.profile_name)
    target_host = Remote.from_arg(args.target_host, args.ask_sudo_password)
@@ -310,10 +308,7 @@ def execute(argv: list[str]) -> None:
    flake = Flake.from_arg(args.flake, target_host)

    if can_run and not flake:
        nixpkgs_path = nix.find_file("nixpkgs", build_flags)
        rev = nix.get_nixpkgs_rev(nixpkgs_path)
        if nixpkgs_path and rev:
            (nixpkgs_path / ".version-suffix").write_text(rev)
        services.write_version_suffix(build_flags)

    match action:
        case (
@@ -327,7 +322,7 @@ def execute(argv: list[str]) -> None:
            | Action.BUILD_VM
            | Action.BUILD_VM_WITH_BOOTLOADER
        ):
            build_and_activate_system(
            services.build_and_activate_system(
                action=action,
                args=args,
                build_host=build_host,
@@ -343,32 +338,21 @@ def execute(argv: list[str]) -> None:
            )

        case Action.EDIT:
            nix.edit(flake, flake_build_flags)
            services.edit(flake=flake, flake_build_flags=flake_build_flags)

        case Action.DRY_RUN:
            raise AssertionError("DRY_RUN should be a DRY_BUILD alias")

        case Action.LIST_GENERATIONS:
            generations = nix.list_generations(profile)
            if args.json:
                print(json.dumps(generations, indent=2))
            else:
                headers = {
                    "generation": "Generation",
                    "date": "Build-date",
                    "nixosVersion": "NixOS version",
                    "kernelVersion": "Kernel",
                    "configurationRevision": "Configuration Revision",
                    "specialisations": "Specialisation",
                    "current": "Current",
                }
                print(tabulate(generations, headers=headers))
            services.list_generations(args=args, profile=profile)

        case Action.REPL:
            if flake:
                nix.repl_flake(flake, flake_build_flags)
            else:
                nix.repl(build_attr, build_flags)
            services.repl(
                flake=flake,
                build_attr=build_attr,
                flake_build_flags=flake_build_flags,
                build_flags=build_flags,
            )

        case _:
            assert_never(action)
+24 −24
Original line number Diff line number Diff line
@@ -242,9 +242,22 @@ def copy_closure(
                nix_copy_closure(to_host, to=True)


def edit(flake: Flake | None, flake_flags: Args | None = None) -> None:
def edit() -> None:
    "Try to find and open NixOS configuration file in editor."
    if flake:
    nixos_config = Path(
        os.getenv("NIXOS_CONFIG") or find_file("nixos-config") or "/etc/nixos"
    )
    if nixos_config.is_dir():
        nixos_config /= "default.nix"

    if nixos_config.exists():
        run_wrapper([os.getenv("EDITOR", "nano"), nixos_config], check=False)
    else:
        raise NixOSRebuildError("cannot find NixOS config file")


def edit_flake(flake: Flake | None, flake_flags: Args | None = None) -> None:
    "Try to find and open NixOS configuration file in editor for Flake config."
    run_wrapper(
        [
            "nix",
@@ -256,19 +269,6 @@ def edit(flake: Flake | None, flake_flags: Args | None = None) -> None:
        ],
        check=False,
    )
    else:
        if flake_flags:
            raise NixOSRebuildError("'edit' does not support extra Nix flags")
        nixos_config = Path(
            os.getenv("NIXOS_CONFIG") or find_file("nixos-config") or "/etc/nixos"
        )
        if nixos_config.is_dir():
            nixos_config /= "default.nix"

        if nixos_config.exists():
            run_wrapper([os.getenv("EDITOR", "nano"), nixos_config], check=False)
        else:
            raise NixOSRebuildError("cannot find NixOS config file")


def find_file(file: str, nix_flags: Args | None = None) -> Path | None:
+48 −1
Original line number Diff line number Diff line
import argparse
import json
import logging
import os
import sys
@@ -10,7 +11,7 @@ from . import nix, tmpdir
from .constants import EXECUTABLE
from .models import Action, BuildAttr, Flake, ImageVariants, NixOSRebuildError, Profile
from .process import Remote, cleanup_ssh
from .utils import Args
from .utils import Args, tabulate

NIXOS_REBUILD_ATTR: Final = "config.system.build.nixos-rebuild"

@@ -315,3 +316,49 @@ def build_and_activate_system(
        common_flags=common_flags,
        flake_common_flags=flake_common_flags,
    )


def edit(flake: Flake | None, flake_build_flags: Args | None = None) -> None:
    if flake:
        nix.edit_flake(flake, flake_build_flags)
    else:
        nix.edit()


def list_generations(
    args: argparse.Namespace,
    profile: Profile,
) -> None:
    generations = nix.list_generations(profile)
    if args.json:
        print(json.dumps(generations, indent=2))
    else:
        headers = {
            "generation": "Generation",
            "date": "Build-date",
            "nixosVersion": "NixOS version",
            "kernelVersion": "Kernel",
            "configurationRevision": "Configuration Revision",
            "specialisations": "Specialisation",
            "current": "Current",
        }
        print(tabulate(generations, headers=headers))


def repl(
    flake: Flake | None,
    build_attr: BuildAttr,
    flake_build_flags: Args,
    build_flags: Args,
) -> None:
    if flake:
        nix.repl_flake(flake, flake_build_flags)
    else:
        nix.repl(build_attr, build_flags)


def write_version_suffix(build_flags: Args) -> None:
    nixpkgs_path = nix.find_file("nixpkgs", build_flags)
    rev = nix.get_nixpkgs_rev(nixpkgs_path)
    if nixpkgs_path and rev:
        (nixpkgs_path / ".version-suffix").write_text(rev)
+0 −87
Original line number Diff line number Diff line
@@ -8,7 +8,6 @@ from typing import Any
from unittest.mock import ANY, Mock, call, patch

import pytest
from pytest import MonkeyPatch

import nixos_rebuild as nr
from nixos_rebuild.constants import WITH_NIX_2_18
@@ -128,92 +127,6 @@ def test_parse_args() -> None:
    ]


@patch.dict(os.environ, {}, clear=True)
@patch("os.execve", autospec=True)
@patch(get_qualified_name(nr.nix.build), autospec=True)
def test_reexec(mock_build: Mock, mock_execve: Mock, monkeypatch: MonkeyPatch) -> None:
    monkeypatch.setattr(nr.services, "EXECUTABLE", "nixos-rebuild-ng")
    argv = ["/path/bin/nixos-rebuild-ng", "switch", "--no-flake"]
    args, _ = nr.parse_args(argv)
    mock_build.return_value = Path("/path")

    nr.reexec(argv, args, {"build": True}, {"flake": True})
    mock_build.assert_has_calls(
        [
            call(
                nr.services.NIXOS_REBUILD_ATTR,
                nr.models.BuildAttr(ANY, ANY),
                {"build": True, "no_out_link": True},
            )
        ]
    )
    # do not exec if there is no new version
    mock_execve.assert_not_called()

    mock_build.return_value = Path("/path/new")

    nr.reexec(argv, args, {}, {})
    # exec in the new version successfully
    mock_execve.assert_called_once_with(
        Path("/path/new/bin/nixos-rebuild-ng"),
        ["/path/bin/nixos-rebuild-ng", "switch", "--no-flake"],
        {"_NIXOS_REBUILD_REEXEC": "1"},
    )

    mock_execve.reset_mock()
    mock_execve.side_effect = [OSError("BOOM"), None]

    nr.reexec(argv, args, {}, {})
    # exec in the previous version if the new version fails
    mock_execve.assert_any_call(
        Path("/path/bin/nixos-rebuild-ng"),
        ["/path/bin/nixos-rebuild-ng", "switch", "--no-flake"],
        {"_NIXOS_REBUILD_REEXEC": "1"},
    )


@patch.dict(os.environ, {}, clear=True)
@patch("os.execve", autospec=True)
@patch(get_qualified_name(nr.nix.build_flake), autospec=True)
def test_reexec_flake(
    mock_build: Mock, mock_execve: Mock, monkeypatch: MonkeyPatch
) -> None:
    monkeypatch.setattr(nr.services, "EXECUTABLE", "nixos-rebuild-ng")
    argv = ["/path/bin/nixos-rebuild-ng", "switch", "--flake"]
    args, _ = nr.parse_args(argv)
    mock_build.return_value = Path("/path")

    nr.reexec(argv, args, {"build": True}, {"flake": True})
    mock_build.assert_called_once_with(
        nr.services.NIXOS_REBUILD_ATTR,
        nr.models.Flake(ANY, ANY),
        {"flake": True, "no_link": True},
    )
    # do not exec if there is no new version
    mock_execve.assert_not_called()

    mock_build.return_value = Path("/path/new")

    nr.reexec(argv, args, {}, {})
    # exec in the new version successfully
    mock_execve.assert_called_once_with(
        Path("/path/new/bin/nixos-rebuild-ng"),
        ["/path/bin/nixos-rebuild-ng", "switch", "--flake"],
        {"_NIXOS_REBUILD_REEXEC": "1"},
    )

    mock_execve.reset_mock()
    mock_execve.side_effect = [OSError("BOOM"), None]

    nr.reexec(argv, args, {}, {})
    # exec in the previous version if the new version fails
    mock_execve.assert_any_call(
        Path("/path/bin/nixos-rebuild-ng"),
        ["/path/bin/nixos-rebuild-ng", "switch", "--flake"],
        {"_NIXOS_REBUILD_REEXEC": "1"},
    )


@patch.dict(
    os.environ,
    {"NIXOS_REBUILD_I_UNDERSTAND_THE_CONSEQUENCES_PLEASE_BREAK_MY_SYSTEM": "1"},
Loading