Commit 8e2bc8ee authored by Jörg Thalheim's avatar Jörg Thalheim
Browse files

nixos-rebuild-ng: fix repl behaving differently on git flakes than build commands

Since we use builtins.getFlake we have behavior differences between
normal nix build and the nix repl because builtins.getFlake won't pick
up local flakes as git+file but assumes path:// flakes instead.
This can have surprising effects such as beeing able to access untracked
files that would lead to build failures otherwise or copying large files
to the nix store.
parent d21198f2
Loading
Loading
Loading
Loading
+27 −4
Original line number Diff line number Diff line
@@ -61,6 +61,25 @@ class BuildAttr:
        return cls(Path(file or "default.nix"), attr)


def discover_git(location: Path) -> str | None:
    current = location.resolve()
    previous = None

    while current.is_dir() and current != previous:
        dotgit = current / ".git"
        if dotgit.is_dir():
            return str(current)
        elif dotgit.is_file():  # this is a worktree
            with dotgit.open() as f:
                dotgit_content = f.read().strip()
                if dotgit_content.startswith("gitdir: "):
                    return dotgit_content.split("gitdir: ")[1]
        previous = current
        current = current.parent

    return None


@dataclass(frozen=True)
class Flake:
    path: Path | str
@@ -84,11 +103,15 @@ class Flake:
        assert m is not None, f"got no matches for {flake_str}"
        attr = m.group("attr")
        nixos_attr = f"nixosConfigurations.{attr or hostname_fn() or 'default'}"
        path = m.group("path")
        if ":" in path:
            return cls(path, nixos_attr)
        path_str = m.group("path")
        if ":" in path_str:
            return cls(path_str, nixos_attr)
        else:
            return cls(Path(path), nixos_attr)
            path = Path(path_str)
            git_repo = discover_git(path)
            if git_repo is not None:
                return cls(f"git+file://{git_repo}", nixos_attr)
            return cls(path, nixos_attr)

    @classmethod
    def from_arg(cls, flake_arg: Any, target_host: Remote | None) -> Self | None:
+28 −8
Original line number Diff line number Diff line
@@ -4,6 +4,8 @@ from pathlib import Path
from typing import Any
from unittest.mock import patch

from pytest import MonkeyPatch

import nixos_rebuild.models as m

from .helpers import get_qualified_name
@@ -30,7 +32,7 @@ def test_build_attr_to_attr() -> None:
    )


def test_flake_parse() -> None:
def test_flake_parse(tmpdir: Path, monkeypatch: MonkeyPatch) -> None:
    assert m.Flake.parse("/path/to/flake#attr") == m.Flake(
        Path("/path/to/flake"), "nixosConfigurations.attr"
    )
@@ -40,6 +42,9 @@ def test_flake_parse() -> None:
    assert m.Flake.parse("/path/to/flake", lambda: "hostname") == m.Flake(
        Path("/path/to/flake"), "nixosConfigurations.hostname"
    )
    # change directory to tmpdir
    with monkeypatch.context() as patch_context:
        patch_context.chdir(tmpdir)
        assert m.Flake.parse(".#attr") == m.Flake(Path("."), "nixosConfigurations.attr")
        assert m.Flake.parse("#attr") == m.Flake(Path("."), "nixosConfigurations.attr")
        assert m.Flake.parse(".") == m.Flake(Path("."), "nixosConfigurations.default")
@@ -49,6 +54,19 @@ def test_flake_parse() -> None:
    assert m.Flake.parse("github:user/repo/branch") == m.Flake(
        "github:user/repo/branch", "nixosConfigurations.default"
    )
    git_root = tmpdir / "git_root"
    git_root.mkdir()
    (git_root / ".git").mkdir()
    assert m.Flake.parse(str(git_root)) == m.Flake(
        f"git+file://{git_root}", "nixosConfigurations.default"
    )

    work_tree = tmpdir / "work_tree"
    work_tree.mkdir()
    (work_tree / ".git").write_text("gitdir: /path/to/git", "utf-8")
    assert m.Flake.parse(str(work_tree)) == m.Flake(
        "git+file:///path/to/git", "nixosConfigurations.default"
    )


def test_flake_to_attr() -> None:
@@ -61,7 +79,7 @@ def test_flake_to_attr() -> None:


@patch(get_qualified_name(platform.node), autospec=True)
def test_flake_from_arg(mock_node: Any) -> None:
def test_flake_from_arg(mock_node: Any, monkeypatch: MonkeyPatch, tmpdir: Path) -> None:
    mock_node.return_value = "hostname"

    # Flake string
@@ -73,6 +91,8 @@ def test_flake_from_arg(mock_node: Any) -> None:
    assert m.Flake.from_arg(False, None) is None

    # True
    with monkeypatch.context() as patch_context:
        patch_context.chdir(tmpdir)
        assert m.Flake.from_arg(True, None) == m.Flake(
            Path("."), "nixosConfigurations.hostname"
        )
+9 −4
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@ from typing import Any
from unittest.mock import ANY, call, patch

import pytest
from pytest import MonkeyPatch

import nixos_rebuild.models as m
import nixos_rebuild.nix as n
@@ -51,7 +52,8 @@ def test_build(mock_run: Any, monkeypatch: Any) -> None:
    autospec=True,
    return_value=CompletedProcess([], 0, stdout=" \n/path/to/file\n "),
)
def test_build_flake(mock_run: Any) -> None:
def test_build_flake(mock_run: Any, monkeypatch: MonkeyPatch, tmpdir: Path) -> None:
    monkeypatch.chdir(tmpdir)
    flake = m.Flake.parse(".#hostname")

    assert n.build_flake(
@@ -165,7 +167,10 @@ def test_build_remote(mock_uuid4: Any, mock_run: Any, monkeypatch: Any) -> None:
    autospec=True,
    return_value=CompletedProcess([], 0, stdout=" \n/path/to/file\n "),
)
def test_build_remote_flake(mock_run: Any, monkeypatch: Any) -> None:
def test_build_remote_flake(
    mock_run: Any, monkeypatch: MonkeyPatch, tmpdir: Path
) -> None:
    monkeypatch.chdir(tmpdir)
    flake = m.Flake.parse(".#hostname")
    build_host = m.Remote("user@host", [], None)
    monkeypatch.setenv("NIX_SSHOPTS", "--ssh opts")
@@ -287,7 +292,7 @@ def test_copy_closure(monkeypatch: Any) -> None:
@patch(get_qualified_name(n.run_wrapper, n), autospec=True)
def test_edit(mock_run: Any, monkeypatch: Any, tmpdir: Any) -> None:
    # Flake
    flake = m.Flake.parse(".#attr")
    flake = m.Flake.parse(f"{tmpdir}#attr")
    n.edit(flake, {"commit_lock_file": True})
    mock_run.assert_called_with(
        [
@@ -297,7 +302,7 @@ def test_edit(mock_run: Any, monkeypatch: Any, tmpdir: Any) -> None:
            "edit",
            "--commit-lock-file",
            "--",
            ".#nixosConfigurations.attr",
            f"{tmpdir}#nixosConfigurations.attr",
        ],
        check=False,
    )