nixos-rebuild-ng: generate .version-suffix
for classic Nix
This commit is contained in:
parent
d55f8c84a5
commit
4def107627
@ -10,6 +10,8 @@ from typing import assert_never
|
||||
from .models import Action, Flake, NRError, Profile
|
||||
from .nix import (
|
||||
edit,
|
||||
find_file,
|
||||
get_nixpkgs_rev,
|
||||
list_generations,
|
||||
nixos_build,
|
||||
nixos_build_flake,
|
||||
@ -86,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:
|
||||
|
@ -57,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:
|
||||
|
@ -1,6 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import shutil
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from subprocess import PIPE, CalledProcessError, run
|
||||
@ -14,7 +15,7 @@ from .models import (
|
||||
NRError,
|
||||
Profile,
|
||||
)
|
||||
from .utils import dict_to_flags
|
||||
from .utils import dict_to_flags, info
|
||||
|
||||
FLAKE_FLAGS: Final = ["--extra-experimental-features", "nix-command flakes"]
|
||||
|
||||
@ -48,6 +49,49 @@ def edit(flake: Flake | None, nix_flags: list[str] | None = None) -> None:
|
||||
raise NRError("cannot find NixOS config file")
|
||||
|
||||
|
||||
def find_file(file: str, nix_flags: list[str] | None = None) -> Path | None:
|
||||
"Find classic Nixpkgs location."
|
||||
r = run(
|
||||
["nix-instantiate", "--find-file", file, *(nix_flags or [])],
|
||||
stdout=PIPE,
|
||||
check=False,
|
||||
text=True,
|
||||
)
|
||||
if r.returncode:
|
||||
return None
|
||||
return Path(r.stdout.strip())
|
||||
|
||||
|
||||
def get_nixpkgs_rev(nixpkgs_path: Path | None) -> str | None:
|
||||
"""Get Nixpkgs path as a Git revision.
|
||||
|
||||
Can be used to generate `.version-suffix` file."""
|
||||
if not nixpkgs_path:
|
||||
return None
|
||||
|
||||
# Git is not included in the closure for nixos-rebuild so we need to check
|
||||
if not shutil.which("git"):
|
||||
info(f"warning: Git not found; cannot figure out revision of '{nixpkgs_path}'")
|
||||
return None
|
||||
|
||||
# Get current revision
|
||||
r = run(
|
||||
["git", "-C", nixpkgs_path, "rev-parse", "--short", "HEAD"],
|
||||
check=False,
|
||||
stdout=PIPE,
|
||||
text=True,
|
||||
)
|
||||
rev = r.stdout.strip()
|
||||
|
||||
if rev:
|
||||
# Check if repo is dirty
|
||||
if run(["git", "-C", nixpkgs_path, "diff", "--quiet"], check=False).returncode:
|
||||
rev += "M"
|
||||
return f".git.{rev}"
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def _parse_generation_from_nix_store(path: Path, profile: Profile) -> Generation:
|
||||
entry_id = path.name.split("-")[1]
|
||||
current = path.name == profile.path.readlink().name
|
||||
|
@ -67,10 +67,17 @@ def test_parse_args() -> None:
|
||||
|
||||
|
||||
@patch(get_qualified_name(nr.nix.run, nr.nix), autospec=True)
|
||||
def test_execute_nix_boot(mock_run: Any, tmp_path: Path) -> None:
|
||||
@patch(get_qualified_name(nr.nix.shutil.which), autospec=True, return_value="/bin/git")
|
||||
def test_execute_nix_boot(mock_which: Any, mock_run: Any, tmp_path: Path) -> None:
|
||||
nixpkgs_path = tmp_path / "nixpkgs"
|
||||
nixpkgs_path.mkdir()
|
||||
config_path = tmp_path / "test"
|
||||
config_path.touch()
|
||||
mock_run.side_effect = [
|
||||
# update_nixpkgs_rev
|
||||
CompletedProcess([], 0, str(nixpkgs_path)),
|
||||
CompletedProcess([], 0, "nixpkgs-rev"),
|
||||
CompletedProcess([], 0),
|
||||
# nixos_build
|
||||
CompletedProcess([], 0, str(config_path)),
|
||||
# set_profile
|
||||
@ -82,9 +89,25 @@ def test_execute_nix_boot(mock_run: Any, tmp_path: Path) -> None:
|
||||
nr.execute(["nixos-rebuild", "boot", "--no-flake", "-vvv"])
|
||||
|
||||
assert nr.VERBOSE is True
|
||||
assert mock_run.call_count == 3
|
||||
assert mock_run.call_count == 6
|
||||
mock_run.assert_has_calls(
|
||||
[
|
||||
call(
|
||||
["nix-instantiate", "--find-file", "nixpkgs", "-vvv"],
|
||||
stdout=PIPE,
|
||||
check=False,
|
||||
text=True,
|
||||
),
|
||||
call(
|
||||
["git", "-C", nixpkgs_path, "rev-parse", "--short", "HEAD"],
|
||||
check=False,
|
||||
stdout=PIPE,
|
||||
text=True,
|
||||
),
|
||||
call(
|
||||
["git", "-C", nixpkgs_path, "diff", "--quiet"],
|
||||
check=False,
|
||||
),
|
||||
call(
|
||||
[
|
||||
"nix-build",
|
||||
@ -180,11 +203,15 @@ def test_execute_nix_switch_flake(mock_run: Any, tmp_path: Path) -> None:
|
||||
|
||||
|
||||
@patch(get_qualified_name(nr.nix.run, nr.nix), autospec=True)
|
||||
def test_execute_switch_rollback(mock_run: Any) -> None:
|
||||
def test_execute_switch_rollback(mock_run: Any, tmp_path: Path) -> None:
|
||||
nixpkgs_path = tmp_path / "nixpkgs"
|
||||
nixpkgs_path.touch()
|
||||
|
||||
nr.execute(["nixos-rebuild", "switch", "--rollback", "--install-bootloader"])
|
||||
|
||||
assert nr.VERBOSE is False
|
||||
assert mock_run.call_count == 2
|
||||
assert mock_run.call_count == 3
|
||||
# ignoring update_nixpkgs_rev calls
|
||||
mock_run.assert_has_calls(
|
||||
[
|
||||
call(
|
||||
|
@ -42,6 +42,61 @@ def test_edit(mock_run: Any, monkeypatch: Any, tmpdir: Any) -> None:
|
||||
mock_run.assert_called_with(["editor", default_nix], check=False)
|
||||
|
||||
|
||||
@patch(get_qualified_name(n.shutil.which), autospec=True, return_value="/bin/git")
|
||||
def test_get_nixpkgs_rev(mock_which: Any) -> None:
|
||||
assert n.get_nixpkgs_rev(None) is None
|
||||
|
||||
path = Path("/path/to/nix")
|
||||
|
||||
with patch(
|
||||
get_qualified_name(n.run, n),
|
||||
autospec=True,
|
||||
side_effect=[CompletedProcess([], 0, "")],
|
||||
) as mock_run:
|
||||
assert n.get_nixpkgs_rev(path) is None
|
||||
mock_run.assert_called_with(
|
||||
["git", "-C", path, "rev-parse", "--short", "HEAD"],
|
||||
check=False,
|
||||
stdout=PIPE,
|
||||
text=True,
|
||||
)
|
||||
|
||||
expected_calls = [
|
||||
call(
|
||||
["git", "-C", path, "rev-parse", "--short", "HEAD"],
|
||||
check=False,
|
||||
stdout=PIPE,
|
||||
text=True,
|
||||
),
|
||||
call(
|
||||
["git", "-C", path, "diff", "--quiet"],
|
||||
check=False,
|
||||
),
|
||||
]
|
||||
|
||||
with patch(
|
||||
get_qualified_name(n.run, n),
|
||||
autospec=True,
|
||||
side_effect=[
|
||||
CompletedProcess([], 0, "0f7c82403fd6"),
|
||||
CompletedProcess([], returncode=0),
|
||||
],
|
||||
) as mock_run:
|
||||
assert n.get_nixpkgs_rev(path) == ".git.0f7c82403fd6"
|
||||
mock_run.assert_has_calls(expected_calls)
|
||||
|
||||
with patch(
|
||||
get_qualified_name(n.run, n),
|
||||
autospec=True,
|
||||
side_effect=[
|
||||
CompletedProcess([], 0, "0f7c82403fd6"),
|
||||
CompletedProcess([], returncode=1),
|
||||
],
|
||||
) as mock_run:
|
||||
assert n.get_nixpkgs_rev(path) == ".git.0f7c82403fd6M"
|
||||
mock_run.assert_has_calls(expected_calls)
|
||||
|
||||
|
||||
def test_get_generations_from_nix_store(tmp_path: Path) -> None:
|
||||
nixos_path = tmp_path / "nixos-system"
|
||||
nixos_path.mkdir()
|
||||
|
Loading…
Reference in New Issue
Block a user