Refactor pkgmgr into modular installer pipeline with Nix flake support, PKGBUILD build workflow, local Nix cache, and full test suite restructuring.

See conversation: https://chatgpt.com/share/69332519-7ff4-800f-bc21-7fcd24a66c10
This commit is contained in:
Kevin Veen-Birkenbach
2025-12-05 19:32:42 +01:00
parent 341ec1179e
commit f5475d86e2
35 changed files with 1684 additions and 524 deletions

View File

View File

@@ -0,0 +1,71 @@
# tests/unit/pkgmgr/installers/test_ansible_requirements.py
import os
import unittest
from unittest.mock import patch, mock_open
from pkgmgr.context import RepoContext
from pkgmgr.installers.ansible_requirements import AnsibleRequirementsInstaller
class TestAnsibleRequirementsInstaller(unittest.TestCase):
def setUp(self):
self.repo = {"name": "test-repo"}
self.ctx = RepoContext(
repo=self.repo,
identifier="test-id",
repo_dir="/tmp/repo",
repositories_base_dir="/tmp",
bin_dir="/bin",
all_repos=[self.repo],
no_verification=False,
preview=False,
quiet=False,
clone_mode="ssh",
update_dependencies=False,
)
self.installer = AnsibleRequirementsInstaller()
@patch("os.path.exists", return_value=True)
def test_supports_true_when_requirements_exist(self, mock_exists):
self.assertTrue(self.installer.supports(self.ctx))
mock_exists.assert_called_with(os.path.join(self.ctx.repo_dir, "requirements.yml"))
@patch("os.path.exists", return_value=False)
def test_supports_false_when_requirements_missing(self, mock_exists):
self.assertFalse(self.installer.supports(self.ctx))
@patch("pkgmgr.installers.ansible_requirements.run_command")
@patch("tempfile.NamedTemporaryFile")
@patch(
"builtins.open",
new_callable=mock_open,
read_data="""
collections:
- name: community.docker
roles:
- src: geerlingguy.docker
""",
)
@patch("os.path.exists", return_value=True)
def test_run_installs_collections_and_roles(
self, mock_exists, mock_file, mock_tmp, mock_run_command
):
# Fake temp file name
mock_tmp().__enter__().name = "/tmp/req.yml"
self.installer.run(self.ctx)
cmds = [call[0][0] for call in mock_run_command.call_args_list]
self.assertIn(
"ansible-galaxy collection install -r /tmp/req.yml",
cmds,
)
self.assertIn(
"ansible-galaxy role install -r /tmp/req.yml",
cmds,
)
if __name__ == "__main__":
unittest.main()

View File

@@ -0,0 +1,97 @@
# tests/unit/pkgmgr/installers/test_aur.py
import os
import unittest
from unittest.mock import patch, mock_open
from pkgmgr.context import RepoContext
from pkgmgr.installers.aur import AurInstaller, AUR_CONFIG_FILENAME
class TestAurInstaller(unittest.TestCase):
def setUp(self):
self.repo = {"name": "test-repo"}
self.ctx = RepoContext(
repo=self.repo,
identifier="test-id",
repo_dir="/tmp/repo",
repositories_base_dir="/tmp",
bin_dir="/bin",
all_repos=[self.repo],
no_verification=False,
preview=False,
quiet=False,
clone_mode="ssh",
update_dependencies=False,
)
self.installer = AurInstaller()
@patch("shutil.which", return_value="/usr/bin/pacman")
@patch("os.path.exists", return_value=True)
@patch(
"builtins.open",
new_callable=mock_open,
read_data="""
helper: yay
packages:
- aurutils
- name: some-aur-only-tool
reason: "Test tool"
""",
)
def test_supports_true_when_arch_and_aur_config_present(
self, mock_file, mock_exists, mock_which
):
self.assertTrue(self.installer.supports(self.ctx))
mock_which.assert_called_with("pacman")
mock_exists.assert_called_with(os.path.join(self.ctx.repo_dir, AUR_CONFIG_FILENAME))
@patch("shutil.which", return_value=None)
def test_supports_false_when_not_arch(self, mock_which):
self.assertFalse(self.installer.supports(self.ctx))
@patch("shutil.which", return_value="/usr/bin/pacman")
@patch("os.path.exists", return_value=False)
def test_supports_false_when_no_config(self, mock_exists, mock_which):
self.assertFalse(self.installer.supports(self.ctx))
@patch("shutil.which", side_effect=lambda name: "/usr/bin/pacman" if name == "pacman" else "/usr/bin/yay")
@patch("pkgmgr.installers.aur.run_command")
@patch(
"builtins.open",
new_callable=mock_open,
read_data="""
helper: yay
packages:
- aurutils
- some-aur-only-tool
""",
)
@patch("os.path.exists", return_value=True)
def test_run_installs_packages_with_helper(
self, mock_exists, mock_file, mock_run_command, mock_which
):
self.installer.run(self.ctx)
cmd = mock_run_command.call_args[0][0]
self.assertTrue(cmd.startswith("yay -S --noconfirm "))
self.assertIn("aurutils", cmd)
self.assertIn("some-aur-only-tool", cmd)
@patch("shutil.which", return_value="/usr/bin/pacman")
@patch(
"builtins.open",
new_callable=mock_open,
read_data="packages: []",
)
@patch("os.path.exists", return_value=True)
def test_run_skips_when_no_packages(
self, mock_exists, mock_file, mock_which
):
with patch("pkgmgr.installers.aur.run_command") as mock_run_command:
self.installer.run(self.ctx)
mock_run_command.assert_not_called()
if __name__ == "__main__":
unittest.main()

View File

@@ -0,0 +1,43 @@
# tests/unit/pkgmgr/installers/test_base.py
import unittest
from pkgmgr.installers.base import BaseInstaller
from pkgmgr.context import RepoContext
class DummyInstaller(BaseInstaller):
def __init__(self, supports_value: bool = True):
self._supports_value = supports_value
self.ran_with = None
def supports(self, ctx: RepoContext) -> bool:
return self._supports_value
def run(self, ctx: RepoContext) -> None:
self.ran_with = ctx
class TestBaseInstaller(unittest.TestCase):
def test_dummy_installer_supports_and_run(self):
ctx = RepoContext(
repo={},
identifier="id",
repo_dir="/tmp/repo",
repositories_base_dir="/tmp",
bin_dir="/bin",
all_repos=[],
no_verification=False,
preview=False,
quiet=False,
clone_mode="ssh",
update_dependencies=False,
)
inst = DummyInstaller(supports_value=True)
self.assertTrue(inst.supports(ctx))
self.assertIsNone(inst.ran_with)
inst.run(ctx)
self.assertIs(inst.ran_with, ctx)
if __name__ == "__main__":
unittest.main()

View File

@@ -0,0 +1,51 @@
# tests/unit/pkgmgr/installers/test_makefile_installer.py
import os
import unittest
from unittest.mock import patch
from pkgmgr.context import RepoContext
from pkgmgr.installers.makefile import MakefileInstaller
class TestMakefileInstaller(unittest.TestCase):
def setUp(self):
self.repo = {"name": "test-repo"}
self.ctx = RepoContext(
repo=self.repo,
identifier="test-id",
repo_dir="/tmp/repo",
repositories_base_dir="/tmp",
bin_dir="/bin",
all_repos=[self.repo],
no_verification=False,
preview=False,
quiet=False,
clone_mode="ssh",
update_dependencies=False,
)
self.installer = MakefileInstaller()
@patch("os.path.exists", return_value=True)
def test_supports_true_when_makefile_exists(self, mock_exists):
self.assertTrue(self.installer.supports(self.ctx))
mock_exists.assert_called_with(os.path.join(self.ctx.repo_dir, "Makefile"))
@patch("os.path.exists", return_value=False)
def test_supports_false_when_makefile_missing(self, mock_exists):
self.assertFalse(self.installer.supports(self.ctx))
@patch("pkgmgr.installers.makefile.run_command")
@patch("os.path.exists", return_value=True)
def test_run_executes_make_install(self, mock_exists, mock_run_command):
self.installer.run(self.ctx)
cmd = mock_run_command.call_args[0][0]
self.assertEqual(cmd, "make install")
self.assertEqual(
mock_run_command.call_args[1].get("cwd"),
self.ctx.repo_dir,
)
if __name__ == "__main__":
unittest.main()

View File

@@ -0,0 +1,64 @@
import os
import unittest
from unittest import mock
from unittest.mock import patch
from pkgmgr.context import RepoContext
from pkgmgr.installers.nix_flake import NixFlakeInstaller
class TestNixFlakeInstaller(unittest.TestCase):
def setUp(self):
self.repo = {"name": "test-repo"}
self.ctx = RepoContext(
repo=self.repo,
identifier="test-id",
repo_dir="/tmp/repo",
repositories_base_dir="/tmp",
bin_dir="/bin",
all_repos=[self.repo],
no_verification=False,
preview=False,
quiet=False,
clone_mode="ssh",
update_dependencies=False,
)
self.installer = NixFlakeInstaller()
@patch("shutil.which", return_value="/usr/bin/nix")
@patch("os.path.exists", return_value=True)
def test_supports_true_when_nix_and_flake_exist(self, mock_exists, mock_which):
self.assertTrue(self.installer.supports(self.ctx))
mock_which.assert_called_with("nix")
mock_exists.assert_called_with(os.path.join(self.ctx.repo_dir, "flake.nix"))
@patch("shutil.which", return_value=None)
@patch("os.path.exists", return_value=True)
def test_supports_false_when_nix_missing(self, mock_exists, mock_which):
self.assertFalse(self.installer.supports(self.ctx))
@patch("os.path.exists", return_value=True)
@patch("shutil.which", return_value="/usr/bin/nix")
@mock.patch("pkgmgr.installers.nix_flake.run_command")
def test_run_tries_pkgmgr_then_default(self, mock_run_command, mock_which, mock_exists):
cmds = []
def side_effect(cmd, cwd=None, preview=False, *args, **kwargs):
cmds.append(cmd)
return None
mock_run_command.side_effect = side_effect
self.installer.run(self.ctx)
self.assertIn(
f"nix profile install {self.ctx.repo_dir}#pkgmgr",
cmds,
)
self.assertIn(
f"nix profile install {self.ctx.repo_dir}#default",
cmds,
)
if __name__ == "__main__":
unittest.main()

View File

@@ -0,0 +1,65 @@
# tests/unit/pkgmgr/installers/test_pkgbuild.py
import os
import unittest
from unittest.mock import patch
from pkgmgr.context import RepoContext
from pkgmgr.installers.pkgbuild import PkgbuildInstaller
class TestPkgbuildInstaller(unittest.TestCase):
def setUp(self):
self.repo = {"name": "test-repo"}
self.ctx = RepoContext(
repo=self.repo,
identifier="test-id",
repo_dir="/tmp/repo",
repositories_base_dir="/tmp",
bin_dir="/bin",
all_repos=[self.repo],
no_verification=False,
preview=False,
quiet=False,
clone_mode="ssh",
update_dependencies=False,
)
self.installer = PkgbuildInstaller()
@patch("os.path.exists", return_value=True)
@patch("shutil.which", return_value="/usr/bin/pacman")
def test_supports_true_when_pacman_and_pkgbuild_exist(self, mock_which, mock_exists):
self.assertTrue(self.installer.supports(self.ctx))
mock_which.assert_called_with("pacman")
mock_exists.assert_called_with(os.path.join(self.ctx.repo_dir, "PKGBUILD"))
@patch("os.path.exists", return_value=False)
@patch("shutil.which", return_value="/usr/bin/pacman")
def test_supports_false_when_pkgbuild_missing(self, mock_which, mock_exists):
self.assertFalse(self.installer.supports(self.ctx))
@patch("pkgmgr.installers.pkgbuild.run_command")
@patch("subprocess.check_output", return_value="python\ngit\n")
@patch("os.path.exists", return_value=True)
@patch("shutil.which", return_value="/usr/bin/pacman")
def test_run_installs_all_packages_and_uses_clean_bash(
self, mock_which, mock_exists, mock_check_output, mock_run_command
):
self.installer.run(self.ctx)
# Check subprocess.check_output arguments (clean shell)
args, kwargs = mock_check_output.call_args
cmd_list = args[0]
self.assertEqual(cmd_list[0], "bash")
self.assertIn("--noprofile", cmd_list)
self.assertIn("--norc", cmd_list)
# Check that pacman is called with the extracted packages
cmd = mock_run_command.call_args[0][0]
self.assertTrue(cmd.startswith("sudo pacman -S --noconfirm "))
self.assertIn("python", cmd)
self.assertIn("git", cmd)
if __name__ == "__main__":
unittest.main()

View File

@@ -0,0 +1,87 @@
# tests/unit/pkgmgr/installers/test_pkgmgr_manifest.py
import os
import unittest
from unittest.mock import patch, mock_open
from pkgmgr.context import RepoContext
from pkgmgr.installers.pkgmgr_manifest import PkgmgrManifestInstaller
class TestPkgmgrManifestInstaller(unittest.TestCase):
def setUp(self):
self.repo = {"name": "test-repo"}
self.ctx = RepoContext(
repo=self.repo,
identifier="test-id",
repo_dir="/tmp/repo",
repositories_base_dir="/tmp",
bin_dir="/bin",
all_repos=[self.repo],
no_verification=False,
preview=False,
quiet=False,
clone_mode="ssh",
update_dependencies=True,
)
self.installer = PkgmgrManifestInstaller()
@patch("os.path.exists", return_value=True)
def test_supports_true_when_manifest_exists(self, mock_exists):
self.assertTrue(self.installer.supports(self.ctx))
manifest_path = os.path.join(self.ctx.repo_dir, "pkgmgr.yml")
mock_exists.assert_called_with(manifest_path)
@patch("os.path.exists", return_value=False)
def test_supports_false_when_manifest_missing(self, mock_exists):
self.assertFalse(self.installer.supports(self.ctx))
@patch("pkgmgr.installers.pkgmgr_manifest.run_command")
@patch("builtins.open", new_callable=mock_open, read_data="""
version: 1
author: "Kevin"
url: "https://example.com"
description: "Test repo"
dependencies:
- repository: github:user/repo1
version: main
reason: "Core dependency"
- repository: github:user/repo2
""")
@patch("os.path.exists", return_value=True)
def test_run_installs_dependencies_and_pulls_when_update_enabled(
self, mock_exists, mock_file, mock_run_command
):
self.installer.run(self.ctx)
# First call: pkgmgr pull github:user/repo1 github:user/repo2
# Then calls to pkgmgr install ...
cmds = [call_args[0][0] for call_args in mock_run_command.call_args_list]
self.assertIn(
"pkgmgr pull github:user/repo1 github:user/repo2",
cmds,
)
self.assertIn(
"pkgmgr install github:user/repo1 --version main --dependencies --clone-mode ssh",
cmds,
)
# For repo2: no version but dependencies + clone_mode
self.assertIn(
"pkgmgr install github:user/repo2 --dependencies --clone-mode ssh",
cmds,
)
@patch("pkgmgr.installers.pkgmgr_manifest.run_command")
@patch("builtins.open", new_callable=mock_open, read_data="{}")
@patch("os.path.exists", return_value=True)
def test_run_no_dependencies_no_command_called(
self, mock_exists, mock_file, mock_run_command
):
self.ctx.update_dependencies = True
self.installer.run(self.ctx)
mock_run_command.assert_not_called()
if __name__ == "__main__":
unittest.main()

View File

@@ -0,0 +1,71 @@
# tests/unit/pkgmgr/installers/test_python_installer.py
import os
import unittest
from unittest.mock import patch
from pkgmgr.context import RepoContext
from pkgmgr.installers.python import PythonInstaller
class TestPythonInstaller(unittest.TestCase):
def setUp(self):
self.repo = {"name": "test-repo"}
self.ctx = RepoContext(
repo=self.repo,
identifier="test-id",
repo_dir="/tmp/repo",
repositories_base_dir="/tmp",
bin_dir="/bin",
all_repos=[self.repo],
no_verification=False,
preview=False,
quiet=False,
clone_mode="ssh",
update_dependencies=False,
)
self.installer = PythonInstaller()
@patch("os.path.exists", side_effect=lambda path: path.endswith("pyproject.toml"))
def test_supports_true_when_pyproject_exists(self, mock_exists):
self.assertTrue(self.installer.supports(self.ctx))
@patch("os.path.exists", side_effect=lambda path: path.endswith("requirements.txt"))
def test_supports_true_when_requirements_exists(self, mock_exists):
self.assertTrue(self.installer.supports(self.ctx))
@patch("os.path.exists", return_value=False)
def test_supports_false_when_no_python_files(self, mock_exists):
self.assertFalse(self.installer.supports(self.ctx))
@patch("pkgmgr.installers.python.run_command")
@patch(
"os.path.exists",
side_effect=lambda path: path.endswith("pyproject.toml")
)
def test_run_installs_project_from_pyproject(self, mock_exists, mock_run_command):
self.installer.run(self.ctx)
cmd = mock_run_command.call_args[0][0]
self.assertIn("pip install .", cmd)
self.assertEqual(
mock_run_command.call_args[1].get("cwd"),
self.ctx.repo_dir,
)
@patch("pkgmgr.installers.python.run_command")
@patch(
"os.path.exists",
side_effect=lambda path: path.endswith("requirements.txt")
)
def test_run_installs_dependencies_from_requirements(self, mock_exists, mock_run_command):
self.installer.run(self.ctx)
cmd = mock_run_command.call_args[0][0]
self.assertIn("pip install -r requirements.txt", cmd)
self.assertEqual(
mock_run_command.call_args[1].get("cwd"),
self.ctx.repo_dir,
)
if __name__ == "__main__":
unittest.main()