From d50891dfe5b5cdd5a6c5400fecd7d5ea2ec61b2b Mon Sep 17 00:00:00 2001 From: Kevin Veen-Birkenbach Date: Tue, 9 Dec 2025 14:20:19 +0100 Subject: [PATCH] Refactor: Restructure pkgmgr into actions/, core/, and cli/ (full module breakup) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit introduces a large-scale structural refactor of the pkgmgr codebase. All functionality has been moved from the previous flat top-level layout into three clearly separated namespaces: • pkgmgr.actions – high-level operations invoked by the CLI • pkgmgr.core – pure logic, helpers, repository utilities, versioning, git helpers, config IO, and command resolution • pkgmgr.cli – parser, dispatch, context, and command handlers Key improvements: - Moved all “branch”, “release”, “changelog”, repo-management actions, installer pipelines, and proxy execution logic into pkgmgr.actions.. - Reworked installer structure under pkgmgr.actions.repository.install.installers including OS-package installers, Nix, Python, and Makefile. - Consolidated all low-level functionality under pkgmgr.core: • git helpers → core/git • config load/save → core/config • repository helpers → core/repository • versioning & semver → core/version • command helpers (alias, resolve, run, ink) → core/command - Replaced pkgmgr.cli_core with pkgmgr.cli and updated all imports. - Added minimal __init__.py files for clean package exposure. - Updated all E2E, integration, and unit tests with new module paths. - Fixed patch targets so mocks point to the new structure. - Ensured backward compatibility at the CLI boundary (pkgmgr entry point unchanged). This refactor produces a cleaner, layered architecture: - `core` = logic - `actions` = orchestrated behaviour - `cli` = user interface Reference: ChatGPT-assisted refactor discussion https://chatgpt.com/share/6938221c-e24c-800f-8317-7732cedf39b9 --- pkgmgr/actions/__init__.py | 0 .../branch/__init__.py} | 4 +- .../changelog/__init__.py} | 2 +- pkgmgr/actions/config/__init__.py | 0 .../config/add.py} | 0 .../config/init.py} | 4 +- .../config/show.py} | 2 +- .../proxy.py} | 6 +-- pkgmgr/{ => actions}/release.py | 6 +-- pkgmgr/actions/repository/__init__.py | 0 .../repository/clone.py} | 6 +-- .../repository/create.py} | 4 +- .../repository/deinstall.py} | 4 +- .../repository/delete.py} | 4 +- .../repository/install/__init__.py} | 22 ++++---- .../repository/install}/capabilities.py | 2 +- .../repository/install}/context.py | 0 .../repository/install/installers/__init__.py | 19 +++++++ .../repository/install}/installers/base.py | 4 +- .../install}/installers/makefile.py | 6 +-- .../install}/installers/nix_flake.py | 8 +-- .../installers/os_packages/__init__.py | 0 .../installers/os_packages/arch_pkgbuild.py | 6 +-- .../installers/os_packages/debian_control.py | 6 +-- .../installers/os_packages/rpm_spec.py | 6 +-- .../repository/install}/installers/python.py | 4 +- .../repository/list.py} | 0 .../repository/pull.py} | 6 +-- .../repository/status.py} | 6 +-- .../repository/update.py} | 6 +-- pkgmgr/{cli.py => cli/__init__.py} | 13 +++-- pkgmgr/{cli_core => cli}/commands/__init__.py | 0 pkgmgr/{cli_core => cli}/commands/branch.py | 4 +- .../{cli_core => cli}/commands/changelog.py | 12 ++--- pkgmgr/{cli_core => cli}/commands/config.py | 14 ++--- pkgmgr/{cli_core => cli}/commands/make.py | 4 +- pkgmgr/{cli_core => cli}/commands/release.py | 12 ++--- pkgmgr/{cli_core => cli}/commands/repos.py | 20 +++---- pkgmgr/{cli_core => cli}/commands/tools.py | 6 +-- pkgmgr/{cli_core => cli}/commands/version.py | 12 ++--- pkgmgr/{cli_core => cli}/context.py | 0 pkgmgr/{cli_core => cli}/dispatch.py | 10 ++-- pkgmgr/{cli_core => cli}/parser.py | 2 +- pkgmgr/{cli_core => cli}/proxy.py | 12 ++--- pkgmgr/cli_core/__init__.py | 5 -- pkgmgr/core/command/__init__.py | 0 .../command/alias.py} | 0 pkgmgr/{create_ink.py => core/command/ink.py} | 4 +- .../command/resolve.py} | 0 .../{run_command.py => core/command/run.py} | 0 pkgmgr/core/config/__init__.py | 0 .../{load_config.py => core/config/load.py} | 0 .../config/save.py} | 0 pkgmgr/{git_utils.py => core/git/__init__.py} | 0 pkgmgr/core/repository/__init__.py | 0 .../repository/dir.py} | 0 .../repository/identifier.py} | 0 .../repository/ignored.py} | 0 .../repository/resolve.py} | 0 .../repository/selected.py} | 2 +- pkgmgr/{ => core/repository}/verify.py | 0 pkgmgr/core/version/__init__.py | 0 .../{versioning.py => core/version/semver.py} | 0 .../version/source.py} | 0 pkgmgr/installers/__init__.py | 19 ------- tests/e2e/test_integration_branch_commands.py | 8 +-- .../e2e/test_integration_release_commands.py | 10 ++-- .../e2e/test_integration_version_commands.py | 2 +- .../test_install_repos_integration.py | 18 +++---- ...test_recursive_capabilities_integration.py | 12 ++--- .../pkgmgr/cli_core/commands/test_release.py | 44 +++++++-------- tests/unit/pkgmgr/cli_core/test_branch_cli.py | 10 ++-- .../os_packages/test_arch_pkgbuild.py | 14 ++--- .../os_packages/test_debian_control.py | 6 +-- .../installers/os_packages/test_rpm_spec.py | 6 +-- tests/unit/pkgmgr/installers/test_base.py | 4 +- .../installers/test_makefile_installer.py | 8 +-- .../unit/pkgmgr/installers/test_nix_flake.py | 8 +-- .../installers/test_python_installer.py | 6 +-- tests/unit/pkgmgr/test_branch_commands.py | 10 ++-- tests/unit/pkgmgr/test_capabilities.py | 26 ++++----- tests/unit/pkgmgr/test_changelog.py | 15 +++--- tests/unit/pkgmgr/test_cli.py | 8 +-- tests/unit/pkgmgr/test_cli_branch.py | 12 ++--- tests/unit/pkgmgr/test_clone_repos.py | 50 ++++++++--------- tests/unit/pkgmgr/test_context.py | 2 +- tests/unit/pkgmgr/test_create_ink.py | 52 +++++++++--------- tests/unit/pkgmgr/test_git_utils.py | 18 +++---- tests/unit/pkgmgr/test_install_repos.py | 34 ++++++------ tests/unit/pkgmgr/test_release.py | 54 +++++++++---------- tests/unit/pkgmgr/test_resolve_command.py | 36 ++++++------- tests/unit/pkgmgr/test_versioning.py | 2 +- 92 files changed, 381 insertions(+), 388 deletions(-) create mode 100644 pkgmgr/actions/__init__.py rename pkgmgr/{branch_commands.py => actions/branch/__init__.py} (97%) rename pkgmgr/{changelog.py => actions/changelog/__init__.py} (97%) create mode 100644 pkgmgr/actions/config/__init__.py rename pkgmgr/{interactive_add.py => actions/config/add.py} (100%) rename pkgmgr/{config_init.py => actions/config/init.py} (98%) rename pkgmgr/{show_config.py => actions/config/show.py} (92%) rename pkgmgr/{exec_proxy_command.py => actions/proxy.py} (88%) rename pkgmgr/{ => actions}/release.py (99%) create mode 100644 pkgmgr/actions/repository/__init__.py rename pkgmgr/{clone_repos.py => actions/repository/clone.py} (96%) rename pkgmgr/{create_repo.py => actions/repository/create.py} (98%) rename pkgmgr/{deinstall_repos.py => actions/repository/deinstall.py} (91%) rename pkgmgr/{delete_repos.py => actions/repository/delete.py} (90%) rename pkgmgr/{install_repos.py => actions/repository/install/__init__.py} (92%) rename pkgmgr/{ => actions/repository/install}/capabilities.py (99%) rename pkgmgr/{ => actions/repository/install}/context.py (100%) create mode 100644 pkgmgr/actions/repository/install/installers/__init__.py rename pkgmgr/{ => actions/repository/install}/installers/base.py (93%) rename pkgmgr/{ => actions/repository/install}/installers/makefile.py (94%) rename pkgmgr/{ => actions/repository/install}/installers/nix_flake.py (93%) rename pkgmgr/{ => actions/repository/install}/installers/os_packages/__init__.py (100%) rename pkgmgr/{ => actions/repository/install}/installers/os_packages/arch_pkgbuild.py (90%) rename pkgmgr/{ => actions/repository/install}/installers/os_packages/debian_control.py (95%) rename pkgmgr/{ => actions/repository/install}/installers/os_packages/rpm_spec.py (96%) rename pkgmgr/{ => actions/repository/install}/installers/python.py (93%) rename pkgmgr/{list_repositories.py => actions/repository/list.py} (100%) rename pkgmgr/{pull_with_verification.py => actions/repository/pull.py} (90%) rename pkgmgr/{status_repos.py => actions/repository/status.py} (86%) rename pkgmgr/{update_repos.py => actions/repository/update.py} (90%) rename pkgmgr/{cli.py => cli/__init__.py} (94%) mode change 100755 => 100644 rename pkgmgr/{cli_core => cli}/commands/__init__.py (100%) rename pkgmgr/{cli_core => cli}/commands/branch.py (88%) rename pkgmgr/{cli_core => cli}/commands/changelog.py (93%) rename pkgmgr/{cli_core => cli}/commands/config.py (95%) rename pkgmgr/{cli_core => cli}/commands/make.py (85%) rename pkgmgr/{cli_core => cli}/commands/release.py (88%) rename pkgmgr/{cli_core => cli}/commands/repos.py (89%) rename pkgmgr/{cli_core => cli}/commands/tools.py (93%) rename pkgmgr/{cli_core => cli}/commands/version.py (92%) rename pkgmgr/{cli_core => cli}/context.py (100%) rename pkgmgr/{cli_core => cli}/dispatch.py (95%) rename pkgmgr/{cli_core => cli}/parser.py (99%) rename pkgmgr/{cli_core => cli}/proxy.py (95%) delete mode 100644 pkgmgr/cli_core/__init__.py create mode 100644 pkgmgr/core/command/__init__.py rename pkgmgr/{generate_alias.py => core/command/alias.py} (100%) rename pkgmgr/{create_ink.py => core/command/ink.py} (95%) rename pkgmgr/{resolve_command.py => core/command/resolve.py} (100%) rename pkgmgr/{run_command.py => core/command/run.py} (100%) create mode 100644 pkgmgr/core/config/__init__.py rename pkgmgr/{load_config.py => core/config/load.py} (100%) rename pkgmgr/{save_user_config.py => core/config/save.py} (100%) rename pkgmgr/{git_utils.py => core/git/__init__.py} (100%) create mode 100644 pkgmgr/core/repository/__init__.py rename pkgmgr/{get_repo_dir.py => core/repository/dir.py} (100%) rename pkgmgr/{get_repo_identifier.py => core/repository/identifier.py} (100%) rename pkgmgr/{filter_ignored.py => core/repository/ignored.py} (100%) rename pkgmgr/{resolve_repos.py => core/repository/resolve.py} (100%) rename pkgmgr/{get_selected_repos.py => core/repository/selected.py} (98%) rename pkgmgr/{ => core/repository}/verify.py (100%) create mode 100644 pkgmgr/core/version/__init__.py rename pkgmgr/{versioning.py => core/version/semver.py} (100%) rename pkgmgr/{version_sources.py => core/version/source.py} (100%) delete mode 100644 pkgmgr/installers/__init__.py diff --git a/pkgmgr/actions/__init__.py b/pkgmgr/actions/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pkgmgr/branch_commands.py b/pkgmgr/actions/branch/__init__.py similarity index 97% rename from pkgmgr/branch_commands.py rename to pkgmgr/actions/branch/__init__.py index d0fe43c..fd80039 100644 --- a/pkgmgr/branch_commands.py +++ b/pkgmgr/actions/branch/__init__.py @@ -6,14 +6,14 @@ High-level helpers for branch-related operations. This module encapsulates the actual Git logic so the CLI layer -(pkgmgr.cli_core.commands.branch) stays thin and testable. +(pkgmgr.cli.commands.branch) stays thin and testable. """ from __future__ import annotations from typing import Optional -from pkgmgr.git_utils import run_git, GitError, get_current_branch +from pkgmgr.core.git import run_git, GitError, get_current_branch def open_branch( diff --git a/pkgmgr/changelog.py b/pkgmgr/actions/changelog/__init__.py similarity index 97% rename from pkgmgr/changelog.py rename to pkgmgr/actions/changelog/__init__.py index 1eaf3fa..18b2357 100644 --- a/pkgmgr/changelog.py +++ b/pkgmgr/actions/changelog/__init__.py @@ -13,7 +13,7 @@ from __future__ import annotations from typing import Optional -from pkgmgr.git_utils import run_git, GitError +from pkgmgr.core.git import run_git, GitError def generate_changelog( diff --git a/pkgmgr/actions/config/__init__.py b/pkgmgr/actions/config/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pkgmgr/interactive_add.py b/pkgmgr/actions/config/add.py similarity index 100% rename from pkgmgr/interactive_add.py rename to pkgmgr/actions/config/add.py diff --git a/pkgmgr/config_init.py b/pkgmgr/actions/config/init.py similarity index 98% rename from pkgmgr/config_init.py rename to pkgmgr/actions/config/init.py index a1b9e32..97a5b9a 100644 --- a/pkgmgr/config_init.py +++ b/pkgmgr/actions/config/init.py @@ -26,8 +26,8 @@ import os import subprocess from typing import Any, Dict -from pkgmgr.generate_alias import generate_alias -from pkgmgr.save_user_config import save_user_config +from pkgmgr.core.command.alias import generate_alias +from pkgmgr.core.config.save import save_user_config def config_init( diff --git a/pkgmgr/show_config.py b/pkgmgr/actions/config/show.py similarity index 92% rename from pkgmgr/show_config.py rename to pkgmgr/actions/config/show.py index 715acce..0f063f8 100644 --- a/pkgmgr/show_config.py +++ b/pkgmgr/actions/config/show.py @@ -1,5 +1,5 @@ import yaml -from .load_config import load_config +from pkgmgr.core.config.load import load_config def show_config(selected_repos, user_config_path, full_config=False): """Display configuration for one or more repositories, or the entire merged config.""" diff --git a/pkgmgr/exec_proxy_command.py b/pkgmgr/actions/proxy.py similarity index 88% rename from pkgmgr/exec_proxy_command.py rename to pkgmgr/actions/proxy.py index fca35e1..a1b4d4a 100644 --- a/pkgmgr/exec_proxy_command.py +++ b/pkgmgr/actions/proxy.py @@ -1,7 +1,7 @@ import os -from pkgmgr.get_repo_identifier import get_repo_identifier -from pkgmgr.get_repo_dir import get_repo_dir -from pkgmgr.run_command import run_command +from pkgmgr.core.repository.identifier import get_repo_identifier +from pkgmgr.core.repository.dir import get_repo_dir +from pkgmgr.core.command.run import run_command import sys def exec_proxy_command(proxy_prefix: str, selected_repos, repositories_base_dir, all_repos, proxy_command: str, extra_args, preview: bool): diff --git a/pkgmgr/release.py b/pkgmgr/actions/release.py similarity index 99% rename from pkgmgr/release.py rename to pkgmgr/actions/release.py index ede1eef..7b7ae91 100644 --- a/pkgmgr/release.py +++ b/pkgmgr/actions/release.py @@ -39,9 +39,9 @@ import tempfile from datetime import date, datetime from typing import Optional, Tuple -from pkgmgr.git_utils import get_tags, get_current_branch, GitError -from pkgmgr.branch_commands import close_branch -from pkgmgr.versioning import ( +from pkgmgr.core.git import get_tags, get_current_branch, GitError +from pkgmgr.actions.branch import close_branch +from pkgmgr.core.version.semver import ( SemVer, find_latest_version, bump_major, diff --git a/pkgmgr/actions/repository/__init__.py b/pkgmgr/actions/repository/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pkgmgr/clone_repos.py b/pkgmgr/actions/repository/clone.py similarity index 96% rename from pkgmgr/clone_repos.py rename to pkgmgr/actions/repository/clone.py index 6b096b7..40a1846 100644 --- a/pkgmgr/clone_repos.py +++ b/pkgmgr/actions/repository/clone.py @@ -1,8 +1,8 @@ import subprocess import os -from pkgmgr.get_repo_dir import get_repo_dir -from pkgmgr.get_repo_identifier import get_repo_identifier -from pkgmgr.verify import verify_repository +from pkgmgr.core.repository.dir import get_repo_dir +from pkgmgr.core.repository.identifier import get_repo_identifier +from pkgmgr.core.repository.verify import verify_repository def clone_repos( selected_repos, diff --git a/pkgmgr/create_repo.py b/pkgmgr/actions/repository/create.py similarity index 98% rename from pkgmgr/create_repo.py rename to pkgmgr/actions/repository/create.py index 62715fa..21f58bc 100644 --- a/pkgmgr/create_repo.py +++ b/pkgmgr/actions/repository/create.py @@ -2,8 +2,8 @@ import os import subprocess import sys import yaml -from pkgmgr.generate_alias import generate_alias -from pkgmgr.save_user_config import save_user_config +from pkgmgr.core.command.alias import generate_alias +from pkgmgr.core.config.save import save_user_config def create_repo(identifier, config_merged, user_config_path, bin_dir, remote=False, preview=False): """ diff --git a/pkgmgr/deinstall_repos.py b/pkgmgr/actions/repository/deinstall.py similarity index 91% rename from pkgmgr/deinstall_repos.py rename to pkgmgr/actions/repository/deinstall.py index 8635c63..cf3712a 100644 --- a/pkgmgr/deinstall_repos.py +++ b/pkgmgr/actions/repository/deinstall.py @@ -1,7 +1,7 @@ import os import sys -from pkgmgr.get_repo_identifier import get_repo_identifier -from pkgmgr.get_repo_dir import get_repo_dir +from pkgmgr.core.repository.identifier import get_repo_identifier +from pkgmgr.core.repository.dir import get_repo_dir def deinstall_repos(selected_repos, repositories_base_dir, bin_dir, all_repos, preview=False): for repo in selected_repos: diff --git a/pkgmgr/delete_repos.py b/pkgmgr/actions/repository/delete.py similarity index 90% rename from pkgmgr/delete_repos.py rename to pkgmgr/actions/repository/delete.py index 7c31fd9..9930f9b 100644 --- a/pkgmgr/delete_repos.py +++ b/pkgmgr/actions/repository/delete.py @@ -1,7 +1,7 @@ import shutil import os -from pkgmgr.get_repo_identifier import get_repo_identifier -from pkgmgr.get_repo_dir import get_repo_dir +from pkgmgr.core.repository.identifier import get_repo_identifier +from pkgmgr.core.repository.dir import get_repo_dir def delete_repos(selected_repos, repositories_base_dir, all_repos, preview=False): for repo in selected_repos: diff --git a/pkgmgr/install_repos.py b/pkgmgr/actions/repository/install/__init__.py similarity index 92% rename from pkgmgr/install_repos.py rename to pkgmgr/actions/repository/install/__init__.py index 2968971..a1f56b0 100644 --- a/pkgmgr/install_repos.py +++ b/pkgmgr/actions/repository/install/__init__.py @@ -21,23 +21,23 @@ focused installer classes. import os from typing import List, Dict, Any -from pkgmgr.get_repo_identifier import get_repo_identifier -from pkgmgr.get_repo_dir import get_repo_dir -from pkgmgr.create_ink import create_ink -from pkgmgr.verify import verify_repository -from pkgmgr.clone_repos import clone_repos -from pkgmgr.context import RepoContext -from pkgmgr.resolve_command import resolve_command_for_repo +from pkgmgr.core.repository.identifier import get_repo_identifier +from pkgmgr.core.repository.dir import get_repo_dir +from pkgmgr.core.command.ink import create_ink +from pkgmgr.core.repository.verify import verify_repository +from pkgmgr.actions.repository.clone import clone_repos +from pkgmgr.actions.repository.install.context import RepoContext +from pkgmgr.core.command.resolve import resolve_command_for_repo # Installer implementations -from pkgmgr.installers.os_packages import ( +from pkgmgr.actions.repository.install.installers.os_packages import ( ArchPkgbuildInstaller, DebianControlInstaller, RpmSpecInstaller, ) -from pkgmgr.installers.nix_flake import NixFlakeInstaller -from pkgmgr.installers.python import PythonInstaller -from pkgmgr.installers.makefile import MakefileInstaller +from pkgmgr.actions.repository.install.installers.nix_flake import NixFlakeInstaller +from pkgmgr.actions.repository.install.installers.python import PythonInstaller +from pkgmgr.actions.repository.install.installers.makefile import MakefileInstaller # Layering: diff --git a/pkgmgr/capabilities.py b/pkgmgr/actions/repository/install/capabilities.py similarity index 99% rename from pkgmgr/capabilities.py rename to pkgmgr/actions/repository/install/capabilities.py index c64d36d..25def88 100644 --- a/pkgmgr/capabilities.py +++ b/pkgmgr/actions/repository/install/capabilities.py @@ -38,7 +38,7 @@ from abc import ABC, abstractmethod from typing import Iterable, TYPE_CHECKING if TYPE_CHECKING: - from pkgmgr.context import RepoContext + from pkgmgr.actions.repository.install.context import RepoContext # --------------------------------------------------------------------------- diff --git a/pkgmgr/context.py b/pkgmgr/actions/repository/install/context.py similarity index 100% rename from pkgmgr/context.py rename to pkgmgr/actions/repository/install/context.py diff --git a/pkgmgr/actions/repository/install/installers/__init__.py b/pkgmgr/actions/repository/install/installers/__init__.py new file mode 100644 index 0000000..e546b8c --- /dev/null +++ b/pkgmgr/actions/repository/install/installers/__init__.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +Installer package for pkgmgr. + +This exposes all installer classes so users can import them directly from +pkgmgr.actions.repository.install.installers. +""" + +from pkgmgr.actions.repository.install.installers.base import BaseInstaller # noqa: F401 +from pkgmgr.actions.repository.install.installers.nix_flake import NixFlakeInstaller # noqa: F401 +from pkgmgr.actions.repository.install.installers.python import PythonInstaller # noqa: F401 +from pkgmgr.actions.repository.install.installers.makefile import MakefileInstaller # noqa: F401 + +# OS-specific installers +from pkgmgr.actions.repository.install.installers.os_packages.arch_pkgbuild import ArchPkgbuildInstaller # noqa: F401 +from pkgmgr.actions.repository.install.installers.os_packages.debian_control import DebianControlInstaller # noqa: F401 +from pkgmgr.actions.repository.install.installers.os_packages.rpm_spec import RpmSpecInstaller # noqa: F401 diff --git a/pkgmgr/installers/base.py b/pkgmgr/actions/repository/install/installers/base.py similarity index 93% rename from pkgmgr/installers/base.py rename to pkgmgr/actions/repository/install/installers/base.py index f50d53a..4ffe7cb 100644 --- a/pkgmgr/installers/base.py +++ b/pkgmgr/actions/repository/install/installers/base.py @@ -8,8 +8,8 @@ Base interface for all installer components in the pkgmgr installation pipeline. from abc import ABC, abstractmethod from typing import Set -from pkgmgr.context import RepoContext -from pkgmgr.capabilities import CAPABILITY_MATCHERS +from pkgmgr.actions.repository.install.context import RepoContext +from pkgmgr.actions.repository.install.capabilities import CAPABILITY_MATCHERS class BaseInstaller(ABC): diff --git a/pkgmgr/installers/makefile.py b/pkgmgr/actions/repository/install/installers/makefile.py similarity index 94% rename from pkgmgr/installers/makefile.py rename to pkgmgr/actions/repository/install/installers/makefile.py index 80b78ff..a584046 100644 --- a/pkgmgr/installers/makefile.py +++ b/pkgmgr/actions/repository/install/installers/makefile.py @@ -12,9 +12,9 @@ installation step. import os import re -from pkgmgr.context import RepoContext -from pkgmgr.installers.base import BaseInstaller -from pkgmgr.run_command import run_command +from pkgmgr.actions.repository.install.context import RepoContext +from pkgmgr.actions.repository.install.installers.base import BaseInstaller +from pkgmgr.core.command.run import run_command class MakefileInstaller(BaseInstaller): diff --git a/pkgmgr/installers/nix_flake.py b/pkgmgr/actions/repository/install/installers/nix_flake.py similarity index 93% rename from pkgmgr/installers/nix_flake.py rename to pkgmgr/actions/repository/install/installers/nix_flake.py index 646af28..63a9819 100644 --- a/pkgmgr/installers/nix_flake.py +++ b/pkgmgr/actions/repository/install/installers/nix_flake.py @@ -19,12 +19,12 @@ import os import shutil from typing import TYPE_CHECKING -from pkgmgr.installers.base import BaseInstaller -from pkgmgr.run_command import run_command +from pkgmgr.actions.repository.install.installers.base import BaseInstaller +from pkgmgr.core.command.run import run_command if TYPE_CHECKING: - from pkgmgr.context import RepoContext - from pkgmgr.install_repos import InstallContext + from pkgmgr.actions.repository.install.context import RepoContext + from pkgmgr.actions.repository.install import InstallContext class NixFlakeInstaller(BaseInstaller): diff --git a/pkgmgr/installers/os_packages/__init__.py b/pkgmgr/actions/repository/install/installers/os_packages/__init__.py similarity index 100% rename from pkgmgr/installers/os_packages/__init__.py rename to pkgmgr/actions/repository/install/installers/os_packages/__init__.py diff --git a/pkgmgr/installers/os_packages/arch_pkgbuild.py b/pkgmgr/actions/repository/install/installers/os_packages/arch_pkgbuild.py similarity index 90% rename from pkgmgr/installers/os_packages/arch_pkgbuild.py rename to pkgmgr/actions/repository/install/installers/os_packages/arch_pkgbuild.py index 058924d..ca299a7 100644 --- a/pkgmgr/installers/os_packages/arch_pkgbuild.py +++ b/pkgmgr/actions/repository/install/installers/os_packages/arch_pkgbuild.py @@ -3,9 +3,9 @@ import os import shutil -from pkgmgr.context import RepoContext -from pkgmgr.installers.base import BaseInstaller -from pkgmgr.run_command import run_command +from pkgmgr.actions.repository.install.context import RepoContext +from pkgmgr.actions.repository.install.installers.base import BaseInstaller +from pkgmgr.core.command.run import run_command class ArchPkgbuildInstaller(BaseInstaller): diff --git a/pkgmgr/installers/os_packages/debian_control.py b/pkgmgr/actions/repository/install/installers/os_packages/debian_control.py similarity index 95% rename from pkgmgr/installers/os_packages/debian_control.py rename to pkgmgr/actions/repository/install/installers/os_packages/debian_control.py index 6e48165..49d4e03 100644 --- a/pkgmgr/installers/os_packages/debian_control.py +++ b/pkgmgr/actions/repository/install/installers/os_packages/debian_control.py @@ -20,9 +20,9 @@ import shutil from typing import List -from pkgmgr.context import RepoContext -from pkgmgr.installers.base import BaseInstaller -from pkgmgr.run_command import run_command +from pkgmgr.actions.repository.install.context import RepoContext +from pkgmgr.actions.repository.install.installers.base import BaseInstaller +from pkgmgr.core.command.run import run_command class DebianControlInstaller(BaseInstaller): diff --git a/pkgmgr/installers/os_packages/rpm_spec.py b/pkgmgr/actions/repository/install/installers/os_packages/rpm_spec.py similarity index 96% rename from pkgmgr/installers/os_packages/rpm_spec.py rename to pkgmgr/actions/repository/install/installers/os_packages/rpm_spec.py index ebd3707..b612476 100644 --- a/pkgmgr/installers/os_packages/rpm_spec.py +++ b/pkgmgr/actions/repository/install/installers/os_packages/rpm_spec.py @@ -19,9 +19,9 @@ import shutil from typing import List, Optional -from pkgmgr.context import RepoContext -from pkgmgr.installers.base import BaseInstaller -from pkgmgr.run_command import run_command +from pkgmgr.actions.repository.install.context import RepoContext +from pkgmgr.actions.repository.install.installers.base import BaseInstaller +from pkgmgr.core.command.run import run_command class RpmSpecInstaller(BaseInstaller): diff --git a/pkgmgr/installers/python.py b/pkgmgr/actions/repository/install/installers/python.py similarity index 93% rename from pkgmgr/installers/python.py rename to pkgmgr/actions/repository/install/installers/python.py index cfe58a8..acb47fd 100644 --- a/pkgmgr/installers/python.py +++ b/pkgmgr/actions/repository/install/installers/python.py @@ -17,8 +17,8 @@ All installation failures are treated as fatal errors (SystemExit). import os import sys -from pkgmgr.installers.base import BaseInstaller -from pkgmgr.run_command import run_command +from pkgmgr.actions.repository.install.installers.base import BaseInstaller +from pkgmgr.core.command.run import run_command class PythonInstaller(BaseInstaller): diff --git a/pkgmgr/list_repositories.py b/pkgmgr/actions/repository/list.py similarity index 100% rename from pkgmgr/list_repositories.py rename to pkgmgr/actions/repository/list.py diff --git a/pkgmgr/pull_with_verification.py b/pkgmgr/actions/repository/pull.py similarity index 90% rename from pkgmgr/pull_with_verification.py rename to pkgmgr/actions/repository/pull.py index 1dca281..c0cdc87 100644 --- a/pkgmgr/pull_with_verification.py +++ b/pkgmgr/actions/repository/pull.py @@ -1,9 +1,9 @@ import os import subprocess import sys -from pkgmgr.get_repo_identifier import get_repo_identifier -from pkgmgr.get_repo_dir import get_repo_dir -from pkgmgr.verify import verify_repository +from pkgmgr.core.repository.identifier import get_repo_identifier +from pkgmgr.core.repository.dir import get_repo_dir +from pkgmgr.core.repository.verify import verify_repository def pull_with_verification( selected_repos, diff --git a/pkgmgr/status_repos.py b/pkgmgr/actions/repository/status.py similarity index 86% rename from pkgmgr/status_repos.py rename to pkgmgr/actions/repository/status.py index 9831e15..a3c884d 100644 --- a/pkgmgr/status_repos.py +++ b/pkgmgr/actions/repository/status.py @@ -1,9 +1,9 @@ import sys import shutil -from .exec_proxy_command import exec_proxy_command -from .run_command import run_command -from .get_repo_identifier import get_repo_identifier +from pkgmgr.actions.proxy import exec_proxy_command +from pkgmgr.core.command.run import run_command +from pkgmgr.core.repository.identifier import get_repo_identifier def status_repos( diff --git a/pkgmgr/update_repos.py b/pkgmgr/actions/repository/update.py similarity index 90% rename from pkgmgr/update_repos.py rename to pkgmgr/actions/repository/update.py index badd56b..df6416f 100644 --- a/pkgmgr/update_repos.py +++ b/pkgmgr/actions/repository/update.py @@ -1,8 +1,8 @@ import sys import shutil -from pkgmgr.pull_with_verification import pull_with_verification -from pkgmgr.install_repos import install_repos +from pkgmgr.actions.repository.pull import pull_with_verification +from pkgmgr.actions.repository.install import install_repos def update_repos( @@ -54,7 +54,7 @@ def update_repos( ) if system_update: - from pkgmgr.run_command import run_command + from pkgmgr.core.command.run import run_command # Nix: upgrade all profile entries (if Nix is available) if shutil.which("nix") is not None: diff --git a/pkgmgr/cli.py b/pkgmgr/cli/__init__.py old mode 100755 new mode 100644 similarity index 94% rename from pkgmgr/cli.py rename to pkgmgr/cli/__init__.py index 278728c..1bda9da --- a/pkgmgr/cli.py +++ b/pkgmgr/cli/__init__.py @@ -1,13 +1,16 @@ -#!/usr/bin/env python3 # -*- coding: utf-8 -*- - from __future__ import annotations import os -import sys -from pkgmgr.load_config import load_config -from pkgmgr.cli_core import CLIContext, create_parser, dispatch_command +from pkgmgr.core.config.load import load_config + +from .context import CLIContext +from .parser import create_parser +from .dispatch import dispatch_command + +__all__ = ["CLIContext", "create_parser", "dispatch_command", "main"] + # User config lives in the home directory: # ~/.config/pkgmgr/config.yaml diff --git a/pkgmgr/cli_core/commands/__init__.py b/pkgmgr/cli/commands/__init__.py similarity index 100% rename from pkgmgr/cli_core/commands/__init__.py rename to pkgmgr/cli/commands/__init__.py diff --git a/pkgmgr/cli_core/commands/branch.py b/pkgmgr/cli/commands/branch.py similarity index 88% rename from pkgmgr/cli_core/commands/branch.py rename to pkgmgr/cli/commands/branch.py index ed0a1d3..746b663 100644 --- a/pkgmgr/cli_core/commands/branch.py +++ b/pkgmgr/cli/commands/branch.py @@ -3,8 +3,8 @@ from __future__ import annotations import sys -from pkgmgr.cli_core.context import CLIContext -from pkgmgr.branch_commands import open_branch, close_branch +from pkgmgr.cli.context import CLIContext +from pkgmgr.actions.branch import open_branch, close_branch def handle_branch(args, ctx: CLIContext) -> None: diff --git a/pkgmgr/cli_core/commands/changelog.py b/pkgmgr/cli/commands/changelog.py similarity index 93% rename from pkgmgr/cli_core/commands/changelog.py rename to pkgmgr/cli/commands/changelog.py index 2a4e38c..2c16222 100644 --- a/pkgmgr/cli_core/commands/changelog.py +++ b/pkgmgr/cli/commands/changelog.py @@ -4,12 +4,12 @@ import os import sys from typing import Any, Dict, List, Optional, Tuple -from pkgmgr.cli_core.context import CLIContext -from pkgmgr.get_repo_dir import get_repo_dir -from pkgmgr.get_repo_identifier import get_repo_identifier -from pkgmgr.git_utils import get_tags -from pkgmgr.versioning import SemVer, extract_semver_from_tags -from pkgmgr.changelog import generate_changelog +from pkgmgr.cli.context import CLIContext +from pkgmgr.core.repository.dir import get_repo_dir +from pkgmgr.core.repository.identifier import get_repo_identifier +from pkgmgr.core.git import get_tags +from pkgmgr.core.version.semver import SemVer, extract_semver_from_tags +from pkgmgr.actions.changelog import generate_changelog Repository = Dict[str, Any] diff --git a/pkgmgr/cli_core/commands/config.py b/pkgmgr/cli/commands/config.py similarity index 95% rename from pkgmgr/cli_core/commands/config.py rename to pkgmgr/cli/commands/config.py index 478ee03..a22830b 100644 --- a/pkgmgr/cli_core/commands/config.py +++ b/pkgmgr/cli/commands/config.py @@ -11,13 +11,13 @@ from typing import Any, Dict import yaml -from pkgmgr.cli_core.context import CLIContext -from pkgmgr.config_init import config_init -from pkgmgr.interactive_add import interactive_add -from pkgmgr.resolve_repos import resolve_repos -from pkgmgr.save_user_config import save_user_config -from pkgmgr.show_config import show_config -from pkgmgr.run_command import run_command +from pkgmgr.cli.context import CLIContext +from pkgmgr.actions.config.init import config_init +from pkgmgr.actions.config.add import interactive_add +from pkgmgr.core.repository.resolve import resolve_repos +from pkgmgr.core.config.save import save_user_config +from pkgmgr.actions.config.show import show_config +from pkgmgr.core.command.run import run_command def _load_user_config(user_config_path: str) -> Dict[str, Any]: diff --git a/pkgmgr/cli_core/commands/make.py b/pkgmgr/cli/commands/make.py similarity index 85% rename from pkgmgr/cli_core/commands/make.py rename to pkgmgr/cli/commands/make.py index 50f5813..b4f61e2 100644 --- a/pkgmgr/cli_core/commands/make.py +++ b/pkgmgr/cli/commands/make.py @@ -3,8 +3,8 @@ from __future__ import annotations import sys from typing import Any, Dict, List -from pkgmgr.cli_core.context import CLIContext -from pkgmgr.exec_proxy_command import exec_proxy_command +from pkgmgr.cli.context import CLIContext +from pkgmgr.actions.proxy import exec_proxy_command Repository = Dict[str, Any] diff --git a/pkgmgr/cli_core/commands/release.py b/pkgmgr/cli/commands/release.py similarity index 88% rename from pkgmgr/cli_core/commands/release.py rename to pkgmgr/cli/commands/release.py index 92828f6..95b42fe 100644 --- a/pkgmgr/cli_core/commands/release.py +++ b/pkgmgr/cli/commands/release.py @@ -13,7 +13,7 @@ Responsibilities: - Take the parsed argparse.Namespace for the `release` command. - Use the list of selected repositories provided by dispatch_command(). - Optionally list affected repositories when --list is set. - - For each selected repository, run pkgmgr.release.release(...) in + - For each selected repository, run pkgmgr.actions.release.release(...) in the context of that repository directory. """ @@ -22,10 +22,10 @@ from __future__ import annotations import os from typing import Any, Dict, List -from pkgmgr.cli_core.context import CLIContext -from pkgmgr.get_repo_dir import get_repo_dir -from pkgmgr.get_repo_identifier import get_repo_identifier -from pkgmgr.release import release as run_release +from pkgmgr.cli.context import CLIContext +from pkgmgr.core.repository.dir import get_repo_dir +from pkgmgr.core.repository.identifier import get_repo_identifier +from pkgmgr.actions.release import release as run_release Repository = Dict[str, Any] @@ -46,7 +46,7 @@ def handle_release( 3) For each selected repository: - Resolve its identifier and local directory. - Change into that directory. - - Call pkgmgr.release.release(...) with the parsed options. + - Call pkgmgr.actions.release.release(...) with the parsed options. """ if not selected: print("[pkgmgr] No repositories selected for release.") diff --git a/pkgmgr/cli_core/commands/repos.py b/pkgmgr/cli/commands/repos.py similarity index 89% rename from pkgmgr/cli_core/commands/repos.py rename to pkgmgr/cli/commands/repos.py index 3944293..7e8c2f4 100644 --- a/pkgmgr/cli_core/commands/repos.py +++ b/pkgmgr/cli/commands/repos.py @@ -6,16 +6,16 @@ from __future__ import annotations import sys from typing import Any, Dict, List -from pkgmgr.cli_core.context import CLIContext -from pkgmgr.install_repos import install_repos -from pkgmgr.deinstall_repos import deinstall_repos -from pkgmgr.delete_repos import delete_repos -from pkgmgr.update_repos import update_repos -from pkgmgr.status_repos import status_repos -from pkgmgr.list_repositories import list_repositories -from pkgmgr.run_command import run_command -from pkgmgr.create_repo import create_repo -from pkgmgr.get_selected_repos import get_selected_repos +from pkgmgr.cli.context import CLIContext +from pkgmgr.actions.repository.install import install_repos +from pkgmgr.actions.repository.deinstall import deinstall_repos +from pkgmgr.actions.repository.delete import delete_repos +from pkgmgr.actions.repository.update import update_repos +from pkgmgr.actions.repository.status import status_repos +from pkgmgr.actions.repository.list import list_repositories +from pkgmgr.core.command.run import run_command +from pkgmgr.actions.repository.create import create_repo +from pkgmgr.core.repository.selected import get_selected_repos Repository = Dict[str, Any] diff --git a/pkgmgr/cli_core/commands/tools.py b/pkgmgr/cli/commands/tools.py similarity index 93% rename from pkgmgr/cli_core/commands/tools.py rename to pkgmgr/cli/commands/tools.py index 682f02e..6fe4198 100644 --- a/pkgmgr/cli_core/commands/tools.py +++ b/pkgmgr/cli/commands/tools.py @@ -5,9 +5,9 @@ import os from typing import Any, Dict, List -from pkgmgr.cli_core.context import CLIContext -from pkgmgr.run_command import run_command -from pkgmgr.get_repo_identifier import get_repo_identifier +from pkgmgr.cli.context import CLIContext +from pkgmgr.core.command.run import run_command +from pkgmgr.core.repository.identifier import get_repo_identifier Repository = Dict[str, Any] diff --git a/pkgmgr/cli_core/commands/version.py b/pkgmgr/cli/commands/version.py similarity index 92% rename from pkgmgr/cli_core/commands/version.py rename to pkgmgr/cli/commands/version.py index 2b2639b..cdf05c0 100644 --- a/pkgmgr/cli_core/commands/version.py +++ b/pkgmgr/cli/commands/version.py @@ -4,12 +4,12 @@ import os import sys from typing import Any, Dict, List, Optional, Tuple -from pkgmgr.cli_core.context import CLIContext -from pkgmgr.get_repo_dir import get_repo_dir -from pkgmgr.get_repo_identifier import get_repo_identifier -from pkgmgr.git_utils import get_tags -from pkgmgr.versioning import SemVer, find_latest_version -from pkgmgr.version_sources import ( +from pkgmgr.cli.context import CLIContext +from pkgmgr.core.repository.dir import get_repo_dir +from pkgmgr.core.repository.identifier import get_repo_identifier +from pkgmgr.core.git import get_tags +from pkgmgr.core.version.semver import SemVer, find_latest_version +from pkgmgr.core.version.source import ( read_pyproject_version, read_flake_version, read_pkgbuild_version, diff --git a/pkgmgr/cli_core/context.py b/pkgmgr/cli/context.py similarity index 100% rename from pkgmgr/cli_core/context.py rename to pkgmgr/cli/context.py diff --git a/pkgmgr/cli_core/dispatch.py b/pkgmgr/cli/dispatch.py similarity index 95% rename from pkgmgr/cli_core/dispatch.py rename to pkgmgr/cli/dispatch.py index dd4f14a..08b8f5c 100644 --- a/pkgmgr/cli_core/dispatch.py +++ b/pkgmgr/cli/dispatch.py @@ -7,12 +7,12 @@ import os import sys from typing import List, Dict, Any -from pkgmgr.cli_core.context import CLIContext -from pkgmgr.cli_core.proxy import maybe_handle_proxy -from pkgmgr.get_selected_repos import get_selected_repos -from pkgmgr.get_repo_dir import get_repo_dir +from pkgmgr.cli.context import CLIContext +from pkgmgr.cli.proxy import maybe_handle_proxy +from pkgmgr.core.repository.selected import get_selected_repos +from pkgmgr.core.repository.dir import get_repo_dir -from pkgmgr.cli_core.commands import ( +from pkgmgr.cli.commands import ( handle_repos_command, handle_tools_command, handle_release, diff --git a/pkgmgr/cli_core/parser.py b/pkgmgr/cli/parser.py similarity index 99% rename from pkgmgr/cli_core/parser.py rename to pkgmgr/cli/parser.py index 51671ae..359dab1 100644 --- a/pkgmgr/cli_core/parser.py +++ b/pkgmgr/cli/parser.py @@ -5,7 +5,7 @@ from __future__ import annotations import argparse -from pkgmgr.cli_core.proxy import register_proxy_commands +from pkgmgr.cli.proxy import register_proxy_commands class SortedSubParsersAction(argparse._SubParsersAction): diff --git a/pkgmgr/cli_core/proxy.py b/pkgmgr/cli/proxy.py similarity index 95% rename from pkgmgr/cli_core/proxy.py rename to pkgmgr/cli/proxy.py index 1b7f709..460f5c6 100644 --- a/pkgmgr/cli_core/proxy.py +++ b/pkgmgr/cli/proxy.py @@ -8,12 +8,12 @@ import os import sys from typing import Dict, List, Any -from pkgmgr.cli_core.context import CLIContext -from pkgmgr.clone_repos import clone_repos -from pkgmgr.exec_proxy_command import exec_proxy_command -from pkgmgr.pull_with_verification import pull_with_verification -from pkgmgr.get_selected_repos import get_selected_repos -from pkgmgr.get_repo_dir import get_repo_dir +from pkgmgr.cli.context import CLIContext +from pkgmgr.actions.repository.clone import clone_repos +from pkgmgr.actions.proxy import exec_proxy_command +from pkgmgr.actions.repository.pull import pull_with_verification +from pkgmgr.core.repository.selected import get_selected_repos +from pkgmgr.core.repository.dir import get_repo_dir PROXY_COMMANDS: Dict[str, List[str]] = { diff --git a/pkgmgr/cli_core/__init__.py b/pkgmgr/cli_core/__init__.py deleted file mode 100644 index aceb5ba..0000000 --- a/pkgmgr/cli_core/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from .context import CLIContext -from .parser import create_parser -from .dispatch import dispatch_command - -__all__ = ["CLIContext", "create_parser", "dispatch_command"] diff --git a/pkgmgr/core/command/__init__.py b/pkgmgr/core/command/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pkgmgr/generate_alias.py b/pkgmgr/core/command/alias.py similarity index 100% rename from pkgmgr/generate_alias.py rename to pkgmgr/core/command/alias.py diff --git a/pkgmgr/create_ink.py b/pkgmgr/core/command/ink.py similarity index 95% rename from pkgmgr/create_ink.py rename to pkgmgr/core/command/ink.py index 9957f92..a72d5d7 100644 --- a/pkgmgr/create_ink.py +++ b/pkgmgr/core/command/ink.py @@ -2,8 +2,8 @@ # -*- coding: utf-8 -*- import os -from pkgmgr.get_repo_identifier import get_repo_identifier -from pkgmgr.get_repo_dir import get_repo_dir +from pkgmgr.core.repository.identifier import get_repo_identifier +from pkgmgr.core.repository.dir import get_repo_dir def create_ink(repo, repositories_base_dir, bin_dir, all_repos, diff --git a/pkgmgr/resolve_command.py b/pkgmgr/core/command/resolve.py similarity index 100% rename from pkgmgr/resolve_command.py rename to pkgmgr/core/command/resolve.py diff --git a/pkgmgr/run_command.py b/pkgmgr/core/command/run.py similarity index 100% rename from pkgmgr/run_command.py rename to pkgmgr/core/command/run.py diff --git a/pkgmgr/core/config/__init__.py b/pkgmgr/core/config/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pkgmgr/load_config.py b/pkgmgr/core/config/load.py similarity index 100% rename from pkgmgr/load_config.py rename to pkgmgr/core/config/load.py diff --git a/pkgmgr/save_user_config.py b/pkgmgr/core/config/save.py similarity index 100% rename from pkgmgr/save_user_config.py rename to pkgmgr/core/config/save.py diff --git a/pkgmgr/git_utils.py b/pkgmgr/core/git/__init__.py similarity index 100% rename from pkgmgr/git_utils.py rename to pkgmgr/core/git/__init__.py diff --git a/pkgmgr/core/repository/__init__.py b/pkgmgr/core/repository/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pkgmgr/get_repo_dir.py b/pkgmgr/core/repository/dir.py similarity index 100% rename from pkgmgr/get_repo_dir.py rename to pkgmgr/core/repository/dir.py diff --git a/pkgmgr/get_repo_identifier.py b/pkgmgr/core/repository/identifier.py similarity index 100% rename from pkgmgr/get_repo_identifier.py rename to pkgmgr/core/repository/identifier.py diff --git a/pkgmgr/filter_ignored.py b/pkgmgr/core/repository/ignored.py similarity index 100% rename from pkgmgr/filter_ignored.py rename to pkgmgr/core/repository/ignored.py diff --git a/pkgmgr/resolve_repos.py b/pkgmgr/core/repository/resolve.py similarity index 100% rename from pkgmgr/resolve_repos.py rename to pkgmgr/core/repository/resolve.py diff --git a/pkgmgr/get_selected_repos.py b/pkgmgr/core/repository/selected.py similarity index 98% rename from pkgmgr/get_selected_repos.py rename to pkgmgr/core/repository/selected.py index 1b94e1c..4a8ad20 100644 --- a/pkgmgr/get_selected_repos.py +++ b/pkgmgr/core/repository/selected.py @@ -7,7 +7,7 @@ import os import re from typing import Any, Dict, List, Sequence -from pkgmgr.resolve_repos import resolve_repos +from pkgmgr.core.repository.resolve import resolve_repos Repository = Dict[str, Any] diff --git a/pkgmgr/verify.py b/pkgmgr/core/repository/verify.py similarity index 100% rename from pkgmgr/verify.py rename to pkgmgr/core/repository/verify.py diff --git a/pkgmgr/core/version/__init__.py b/pkgmgr/core/version/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pkgmgr/versioning.py b/pkgmgr/core/version/semver.py similarity index 100% rename from pkgmgr/versioning.py rename to pkgmgr/core/version/semver.py diff --git a/pkgmgr/version_sources.py b/pkgmgr/core/version/source.py similarity index 100% rename from pkgmgr/version_sources.py rename to pkgmgr/core/version/source.py diff --git a/pkgmgr/installers/__init__.py b/pkgmgr/installers/__init__.py deleted file mode 100644 index e9a6fd3..0000000 --- a/pkgmgr/installers/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -""" -Installer package for pkgmgr. - -This exposes all installer classes so users can import them directly from -pkgmgr.installers. -""" - -from pkgmgr.installers.base import BaseInstaller # noqa: F401 -from pkgmgr.installers.nix_flake import NixFlakeInstaller # noqa: F401 -from pkgmgr.installers.python import PythonInstaller # noqa: F401 -from pkgmgr.installers.makefile import MakefileInstaller # noqa: F401 - -# OS-specific installers -from pkgmgr.installers.os_packages.arch_pkgbuild import ArchPkgbuildInstaller # noqa: F401 -from pkgmgr.installers.os_packages.debian_control import DebianControlInstaller # noqa: F401 -from pkgmgr.installers.os_packages.rpm_spec import RpmSpecInstaller # noqa: F401 diff --git a/tests/e2e/test_integration_branch_commands.py b/tests/e2e/test_integration_branch_commands.py index e2d8e5e..4d3bcc2 100644 --- a/tests/e2e/test_integration_branch_commands.py +++ b/tests/e2e/test_integration_branch_commands.py @@ -31,7 +31,7 @@ class TestIntegrationBranchCommands(unittest.TestCase): finally: sys.argv = original_argv - @patch("pkgmgr.cli_core.commands.branch.open_branch") + @patch("pkgmgr.cli.commands.branch.open_branch") def test_branch_open_with_name_and_base(self, mock_open_branch) -> None: """ `pkgmgr branch open feature/test --base develop` must forward @@ -47,7 +47,7 @@ class TestIntegrationBranchCommands(unittest.TestCase): self.assertEqual(kwargs.get("base_branch"), "develop") self.assertEqual(kwargs.get("cwd"), ".") - @patch("pkgmgr.cli_core.commands.branch.open_branch") + @patch("pkgmgr.cli.commands.branch.open_branch") def test_branch_open_without_name_uses_default_base( self, mock_open_branch, @@ -68,7 +68,7 @@ class TestIntegrationBranchCommands(unittest.TestCase): # close subcommand # ------------------------------------------------------------------ - @patch("pkgmgr.cli_core.commands.branch.close_branch") + @patch("pkgmgr.cli.commands.branch.close_branch") def test_branch_close_with_name_and_base(self, mock_close_branch) -> None: """ `pkgmgr branch close feature/test --base develop` must forward @@ -84,7 +84,7 @@ class TestIntegrationBranchCommands(unittest.TestCase): self.assertEqual(kwargs.get("base_branch"), "develop") self.assertEqual(kwargs.get("cwd"), ".") - @patch("pkgmgr.cli_core.commands.branch.close_branch") + @patch("pkgmgr.cli.commands.branch.close_branch") def test_branch_close_without_name_uses_default_base( self, mock_close_branch, diff --git a/tests/e2e/test_integration_release_commands.py b/tests/e2e/test_integration_release_commands.py index 9e91fbd..ec045cd 100644 --- a/tests/e2e/test_integration_release_commands.py +++ b/tests/e2e/test_integration_release_commands.py @@ -6,7 +6,7 @@ End-to-end style integration tests for the `pkgmgr release` CLI command. These tests exercise the real top-level entry point (main.py) and mock the high-level helper used by the CLI wiring -(pkgmgr.cli_core.commands.release.run_release) to ensure that argument +(pkgmgr.cli.commands.release.run_release) to ensure that argument parsing and dispatch behave as expected, in particular the new `close` flag. @@ -52,8 +52,8 @@ class TestIntegrationReleaseCommand(unittest.TestCase): # Behaviour without --close # ------------------------------------------------------------------ - @patch("pkgmgr.cli_core.commands.release.run_release") - @patch("pkgmgr.cli_core.dispatch._select_repo_for_current_directory") + @patch("pkgmgr.cli.commands.release.run_release") + @patch("pkgmgr.cli.dispatch._select_repo_for_current_directory") def test_release_without_close_flag( self, mock_select_repo, @@ -95,8 +95,8 @@ class TestIntegrationReleaseCommand(unittest.TestCase): # Behaviour with --close # ------------------------------------------------------------------ - @patch("pkgmgr.cli_core.commands.release.run_release") - @patch("pkgmgr.cli_core.dispatch._select_repo_for_current_directory") + @patch("pkgmgr.cli.commands.release.run_release") + @patch("pkgmgr.cli.dispatch._select_repo_for_current_directory") def test_release_with_close_flag( self, mock_select_repo, diff --git a/tests/e2e/test_integration_version_commands.py b/tests/e2e/test_integration_version_commands.py index d07f22e..171ca3d 100644 --- a/tests/e2e/test_integration_version_commands.py +++ b/tests/e2e/test_integration_version_commands.py @@ -28,7 +28,7 @@ import sys import unittest from typing import List -from pkgmgr.load_config import load_config +from pkgmgr.core.config.load import load_config # Resolve project root (the repo where main.py lives, e.g. /src) PROJECT_ROOT = os.path.abspath( diff --git a/tests/integration/test_install_repos_integration.py b/tests/integration/test_install_repos_integration.py index e3a520e..75ec6ae 100644 --- a/tests/integration/test_install_repos_integration.py +++ b/tests/integration/test_install_repos_integration.py @@ -5,9 +5,9 @@ import tempfile import unittest from unittest.mock import patch -import pkgmgr.install_repos as install_module -from pkgmgr.install_repos import install_repos -from pkgmgr.installers.base import BaseInstaller +import pkgmgr.actions.repository.install as install_module +from pkgmgr.actions.repository.install import install_repos +from pkgmgr.actions.repository.install.installers.base import BaseInstaller class DummyInstaller(BaseInstaller): @@ -26,10 +26,10 @@ class DummyInstaller(BaseInstaller): class TestInstallReposIntegration(unittest.TestCase): - @patch("pkgmgr.install_repos.verify_repository") - @patch("pkgmgr.install_repos.clone_repos") - @patch("pkgmgr.install_repos.get_repo_dir") - @patch("pkgmgr.install_repos.get_repo_identifier") + @patch("pkgmgr.actions.repository.install.verify_repository") + @patch("pkgmgr.actions.repository.install.clone_repos") + @patch("pkgmgr.actions.repository.install.get_repo_dir") + @patch("pkgmgr.actions.repository.install.get_repo_identifier") def test_system_binary_vs_nix_binary( self, mock_get_repo_identifier, @@ -100,8 +100,8 @@ class TestInstallReposIntegration(unittest.TestCase): nix_tool_path = "/nix/profile/bin/repo-nix" # Patch resolve_command_for_repo at the install_repos module level - with patch("pkgmgr.install_repos.resolve_command_for_repo") as mock_resolve, \ - patch("pkgmgr.install_repos.os.path.exists") as mock_exists_install: + with patch("pkgmgr.actions.repository.install.resolve_command_for_repo") as mock_resolve, \ + patch("pkgmgr.actions.repository.install.os.path.exists") as mock_exists_install: def fake_resolve_command(repo, repo_identifier: str, repo_dir: str): """ diff --git a/tests/integration/test_recursive_capabilities_integration.py b/tests/integration/test_recursive_capabilities_integration.py index 1bc68d9..63dfbbc 100644 --- a/tests/integration/test_recursive_capabilities_integration.py +++ b/tests/integration/test_recursive_capabilities_integration.py @@ -61,12 +61,12 @@ import tempfile import unittest from unittest.mock import patch -import pkgmgr.install_repos as install_mod -from pkgmgr.install_repos import install_repos -from pkgmgr.installers.nix_flake import NixFlakeInstaller -from pkgmgr.installers.python import PythonInstaller -from pkgmgr.installers.makefile import MakefileInstaller -from pkgmgr.installers.os_packages.arch_pkgbuild import ArchPkgbuildInstaller +import pkgmgr.actions.repository.install as install_mod +from pkgmgr.actions.repository.install import install_repos +from pkgmgr.actions.repository.install.installers.nix_flake import NixFlakeInstaller +from pkgmgr.actions.repository.install.installers.python import PythonInstaller +from pkgmgr.actions.repository.install.installers.makefile import MakefileInstaller +from pkgmgr.actions.repository.install.installers.os_packages.arch_pkgbuild import ArchPkgbuildInstaller class TestRecursiveCapabilitiesIntegration(unittest.TestCase): diff --git a/tests/unit/pkgmgr/cli_core/commands/test_release.py b/tests/unit/pkgmgr/cli_core/commands/test_release.py index 0c2e983..40ce111 100644 --- a/tests/unit/pkgmgr/cli_core/commands/test_release.py +++ b/tests/unit/pkgmgr/cli_core/commands/test_release.py @@ -2,12 +2,12 @@ # -*- coding: utf-8 -*- """ -Unit tests for pkgmgr.cli_core.commands.release. +Unit tests for pkgmgr.cli.commands.release. These tests focus on the wiring layer: - Argument handling for the release command as defined by the top-level parser (cli_core.parser.create_parser). - - Correct invocation of pkgmgr.release.release(...) for the + - Correct invocation of pkgmgr.actions.release.release(...) for the selected repositories. - Behaviour of --preview, --list, --close, and -f/--force. """ @@ -46,19 +46,19 @@ class TestReleaseCommand(unittest.TestCase): Build a real top-level parser and parse the given argv list to obtain the Namespace for the `release` command. """ - from pkgmgr.cli_core.parser import create_parser + from pkgmgr.cli.parser import create_parser parser = create_parser("test parser") args = parser.parse_args(argv) self.assertEqual(args.command, "release") return args - @patch("pkgmgr.cli_core.commands.release.os.path.isdir", return_value=True) - @patch("pkgmgr.cli_core.commands.release.run_release") - @patch("pkgmgr.cli_core.commands.release.get_repo_dir") - @patch("pkgmgr.cli_core.commands.release.get_repo_identifier") - @patch("pkgmgr.cli_core.commands.release.os.chdir") - @patch("pkgmgr.cli_core.commands.release.os.getcwd", return_value="/cwd") + @patch("pkgmgr.cli.commands.release.os.path.isdir", return_value=True) + @patch("pkgmgr.cli.commands.release.run_release") + @patch("pkgmgr.cli.commands.release.get_repo_dir") + @patch("pkgmgr.cli.commands.release.get_repo_identifier") + @patch("pkgmgr.cli.commands.release.os.chdir") + @patch("pkgmgr.cli.commands.release.os.getcwd", return_value="/cwd") def test_release_with_close_and_message( self, mock_getcwd, @@ -69,7 +69,7 @@ class TestReleaseCommand(unittest.TestCase): mock_isdir, ) -> None: """ - The release handler should call pkgmgr.release.release() with: + The release handler should call pkgmgr.actions.release.release() with: - release_type (e.g. minor) - provided message - preview flag @@ -78,7 +78,7 @@ class TestReleaseCommand(unittest.TestCase): It must change into the repository directory and then back. """ - from pkgmgr.cli_core.commands.release import handle_release + from pkgmgr.cli.commands.release import handle_release repo = {"name": "dummy-repo"} selected = [repo] @@ -116,12 +116,12 @@ class TestReleaseCommand(unittest.TestCase): close=True, ) - @patch("pkgmgr.cli_core.commands.release.os.path.isdir", return_value=True) - @patch("pkgmgr.cli_core.commands.release.run_release") - @patch("pkgmgr.cli_core.commands.release.get_repo_dir") - @patch("pkgmgr.cli_core.commands.release.get_repo_identifier") - @patch("pkgmgr.cli_core.commands.release.os.chdir") - @patch("pkgmgr.cli_core.commands.release.os.getcwd", return_value="/cwd") + @patch("pkgmgr.cli.commands.release.os.path.isdir", return_value=True) + @patch("pkgmgr.cli.commands.release.run_release") + @patch("pkgmgr.cli.commands.release.get_repo_dir") + @patch("pkgmgr.cli.commands.release.get_repo_identifier") + @patch("pkgmgr.cli.commands.release.os.chdir") + @patch("pkgmgr.cli.commands.release.os.getcwd", return_value="/cwd") def test_release_preview_mode( self, mock_getcwd, @@ -135,7 +135,7 @@ class TestReleaseCommand(unittest.TestCase): In preview mode, the handler should pass preview=True to the release helper and force=False by default. """ - from pkgmgr.cli_core.commands.release import handle_release + from pkgmgr.cli.commands.release import handle_release repo = {"name": "dummy-repo"} selected = [repo] @@ -164,9 +164,9 @@ class TestReleaseCommand(unittest.TestCase): close=False, ) - @patch("pkgmgr.cli_core.commands.release.run_release") - @patch("pkgmgr.cli_core.commands.release.get_repo_dir") - @patch("pkgmgr.cli_core.commands.release.get_repo_identifier") + @patch("pkgmgr.cli.commands.release.run_release") + @patch("pkgmgr.cli.commands.release.get_repo_dir") + @patch("pkgmgr.cli.commands.release.get_repo_identifier") def test_release_list_mode_does_not_invoke_helper( self, mock_get_repo_identifier, @@ -177,7 +177,7 @@ class TestReleaseCommand(unittest.TestCase): When --list is provided, the handler should print the list of affected repositories and must NOT invoke run_release(). """ - from pkgmgr.cli_core.commands.release import handle_release + from pkgmgr.cli.commands.release import handle_release repo1 = {"name": "repo-1"} repo2 = {"name": "repo-2"} diff --git a/tests/unit/pkgmgr/cli_core/test_branch_cli.py b/tests/unit/pkgmgr/cli_core/test_branch_cli.py index 63f6f47..58dce0c 100644 --- a/tests/unit/pkgmgr/cli_core/test_branch_cli.py +++ b/tests/unit/pkgmgr/cli_core/test_branch_cli.py @@ -16,8 +16,8 @@ from __future__ import annotations import unittest from unittest.mock import patch -from pkgmgr.cli_core.parser import create_parser -from pkgmgr.cli_core.commands.branch import handle_branch +from pkgmgr.cli.parser import create_parser +from pkgmgr.cli.commands.branch import handle_branch class TestBranchCLI(unittest.TestCase): @@ -31,7 +31,7 @@ class TestBranchCLI(unittest.TestCase): """ return create_parser("pkgmgr test parser") - @patch("pkgmgr.cli_core.commands.branch.open_branch") + @patch("pkgmgr.cli.commands.branch.open_branch") def test_branch_open_with_name_and_base(self, mock_open_branch): """ Ensure that `pkgmgr branch open --base ` calls @@ -58,7 +58,7 @@ class TestBranchCLI(unittest.TestCase): self.assertEqual(kwargs.get("base_branch"), "develop") self.assertEqual(kwargs.get("cwd"), ".") - @patch("pkgmgr.cli_core.commands.branch.close_branch") + @patch("pkgmgr.cli.commands.branch.close_branch") def test_branch_close_with_name_and_base(self, mock_close_branch): """ Ensure that `pkgmgr branch close --base ` calls @@ -84,7 +84,7 @@ class TestBranchCLI(unittest.TestCase): self.assertEqual(kwargs.get("base_branch"), "main") self.assertEqual(kwargs.get("cwd"), ".") - @patch("pkgmgr.cli_core.commands.branch.close_branch") + @patch("pkgmgr.cli.commands.branch.close_branch") def test_branch_close_without_name_uses_none(self, mock_close_branch): """ Ensure that `pkgmgr branch close` without a name passes name=None diff --git a/tests/unit/pkgmgr/installers/os_packages/test_arch_pkgbuild.py b/tests/unit/pkgmgr/installers/os_packages/test_arch_pkgbuild.py index 5359f86..1fe2db8 100644 --- a/tests/unit/pkgmgr/installers/os_packages/test_arch_pkgbuild.py +++ b/tests/unit/pkgmgr/installers/os_packages/test_arch_pkgbuild.py @@ -4,8 +4,8 @@ import os import unittest from unittest.mock import patch -from pkgmgr.context import RepoContext -from pkgmgr.installers.os_packages.arch_pkgbuild import ArchPkgbuildInstaller +from pkgmgr.actions.repository.install.context import RepoContext +from pkgmgr.actions.repository.install.installers.os_packages.arch_pkgbuild import ArchPkgbuildInstaller class TestArchPkgbuildInstaller(unittest.TestCase): @@ -26,7 +26,7 @@ class TestArchPkgbuildInstaller(unittest.TestCase): ) self.installer = ArchPkgbuildInstaller() - @patch("pkgmgr.installers.os_packages.arch_pkgbuild.os.geteuid", return_value=1000) + @patch("pkgmgr.actions.repository.install.installers.os_packages.arch_pkgbuild.os.geteuid", return_value=1000) @patch("os.path.exists", return_value=True) @patch("shutil.which") def test_supports_true_when_tools_and_pkgbuild_exist( @@ -46,7 +46,7 @@ class TestArchPkgbuildInstaller(unittest.TestCase): self.assertIn("makepkg", calls) mock_exists.assert_called_with(os.path.join(self.ctx.repo_dir, "PKGBUILD")) - @patch("pkgmgr.installers.os_packages.arch_pkgbuild.os.geteuid", return_value=0) + @patch("pkgmgr.actions.repository.install.installers.os_packages.arch_pkgbuild.os.geteuid", return_value=0) @patch("os.path.exists", return_value=True) @patch("shutil.which") def test_supports_false_when_running_as_root( @@ -55,7 +55,7 @@ class TestArchPkgbuildInstaller(unittest.TestCase): mock_which.return_value = "/usr/bin/pacman" self.assertFalse(self.installer.supports(self.ctx)) - @patch("pkgmgr.installers.os_packages.arch_pkgbuild.os.geteuid", return_value=1000) + @patch("pkgmgr.actions.repository.install.installers.os_packages.arch_pkgbuild.os.geteuid", return_value=1000) @patch("os.path.exists", return_value=False) @patch("shutil.which") def test_supports_false_when_pkgbuild_missing( @@ -64,8 +64,8 @@ class TestArchPkgbuildInstaller(unittest.TestCase): mock_which.return_value = "/usr/bin/pacman" self.assertFalse(self.installer.supports(self.ctx)) - @patch("pkgmgr.installers.os_packages.arch_pkgbuild.run_command") - @patch("pkgmgr.installers.os_packages.arch_pkgbuild.os.geteuid", return_value=1000) + @patch("pkgmgr.actions.repository.install.installers.os_packages.arch_pkgbuild.run_command") + @patch("pkgmgr.actions.repository.install.installers.os_packages.arch_pkgbuild.os.geteuid", return_value=1000) @patch("os.path.exists", return_value=True) @patch("shutil.which") def test_run_builds_and_installs_with_makepkg( diff --git a/tests/unit/pkgmgr/installers/os_packages/test_debian_control.py b/tests/unit/pkgmgr/installers/os_packages/test_debian_control.py index f2cea5b..9efc8d4 100644 --- a/tests/unit/pkgmgr/installers/os_packages/test_debian_control.py +++ b/tests/unit/pkgmgr/installers/os_packages/test_debian_control.py @@ -4,8 +4,8 @@ import os import unittest from unittest.mock import patch -from pkgmgr.context import RepoContext -from pkgmgr.installers.os_packages.debian_control import DebianControlInstaller +from pkgmgr.actions.repository.install.context import RepoContext +from pkgmgr.actions.repository.install.installers.os_packages.debian_control import DebianControlInstaller class TestDebianControlInstaller(unittest.TestCase): @@ -36,7 +36,7 @@ class TestDebianControlInstaller(unittest.TestCase): def test_supports_false_without_dpkg_buildpackage(self, mock_which, mock_exists): self.assertFalse(self.installer.supports(self.ctx)) - @patch("pkgmgr.installers.os_packages.debian_control.run_command") + @patch("pkgmgr.actions.repository.install.installers.os_packages.debian_control.run_command") @patch("glob.glob", return_value=["/tmp/package-manager_0.1.1_all.deb"]) @patch("os.path.exists", return_value=True) @patch("shutil.which") diff --git a/tests/unit/pkgmgr/installers/os_packages/test_rpm_spec.py b/tests/unit/pkgmgr/installers/os_packages/test_rpm_spec.py index 09f5dda..76acb22 100644 --- a/tests/unit/pkgmgr/installers/os_packages/test_rpm_spec.py +++ b/tests/unit/pkgmgr/installers/os_packages/test_rpm_spec.py @@ -3,8 +3,8 @@ import unittest from unittest.mock import patch -from pkgmgr.context import RepoContext -from pkgmgr.installers.os_packages.rpm_spec import RpmSpecInstaller +from pkgmgr.actions.repository.install.context import RepoContext +from pkgmgr.actions.repository.install.installers.os_packages.rpm_spec import RpmSpecInstaller class TestRpmSpecInstaller(unittest.TestCase): @@ -45,7 +45,7 @@ class TestRpmSpecInstaller(unittest.TestCase): mock_which.return_value = "/usr/bin/rpmbuild" self.assertFalse(self.installer.supports(self.ctx)) - @patch("pkgmgr.installers.os_packages.rpm_spec.run_command") + @patch("pkgmgr.actions.repository.install.installers.os_packages.rpm_spec.run_command") @patch("glob.glob") @patch("shutil.which") def test_run_builds_and_installs_rpms( diff --git a/tests/unit/pkgmgr/installers/test_base.py b/tests/unit/pkgmgr/installers/test_base.py index 99e0083..c386bf1 100644 --- a/tests/unit/pkgmgr/installers/test_base.py +++ b/tests/unit/pkgmgr/installers/test_base.py @@ -1,8 +1,8 @@ # tests/unit/pkgmgr/installers/test_base.py import unittest -from pkgmgr.installers.base import BaseInstaller -from pkgmgr.context import RepoContext +from pkgmgr.actions.repository.install.installers.base import BaseInstaller +from pkgmgr.actions.repository.install.context import RepoContext class DummyInstaller(BaseInstaller): diff --git a/tests/unit/pkgmgr/installers/test_makefile_installer.py b/tests/unit/pkgmgr/installers/test_makefile_installer.py index b30df1d..0fca4f1 100644 --- a/tests/unit/pkgmgr/installers/test_makefile_installer.py +++ b/tests/unit/pkgmgr/installers/test_makefile_installer.py @@ -4,8 +4,8 @@ import os import unittest from unittest.mock import patch, mock_open -from pkgmgr.context import RepoContext -from pkgmgr.installers.makefile import MakefileInstaller +from pkgmgr.actions.repository.install.context import RepoContext +from pkgmgr.actions.repository.install.installers.makefile import MakefileInstaller class TestMakefileInstaller(unittest.TestCase): @@ -35,7 +35,7 @@ class TestMakefileInstaller(unittest.TestCase): def test_supports_false_when_makefile_missing(self, mock_exists): self.assertFalse(self.installer.supports(self.ctx)) - @patch("pkgmgr.installers.makefile.run_command") + @patch("pkgmgr.actions.repository.install.installers.makefile.run_command") @patch( "builtins.open", new_callable=mock_open, @@ -62,7 +62,7 @@ class TestMakefileInstaller(unittest.TestCase): self.ctx.repo_dir, ) - @patch("pkgmgr.installers.makefile.run_command") + @patch("pkgmgr.actions.repository.install.installers.makefile.run_command") @patch( "builtins.open", new_callable=mock_open, diff --git a/tests/unit/pkgmgr/installers/test_nix_flake.py b/tests/unit/pkgmgr/installers/test_nix_flake.py index 035c1f3..62d3b0f 100644 --- a/tests/unit/pkgmgr/installers/test_nix_flake.py +++ b/tests/unit/pkgmgr/installers/test_nix_flake.py @@ -3,8 +3,8 @@ import unittest from unittest import mock from unittest.mock import patch -from pkgmgr.context import RepoContext -from pkgmgr.installers.nix_flake import NixFlakeInstaller +from pkgmgr.actions.repository.install.context import RepoContext +from pkgmgr.actions.repository.install.installers.nix_flake import NixFlakeInstaller class TestNixFlakeInstaller(unittest.TestCase): @@ -39,7 +39,7 @@ class TestNixFlakeInstaller(unittest.TestCase): @patch("os.path.exists", return_value=True) @patch("shutil.which", return_value="/usr/bin/nix") - @mock.patch("pkgmgr.installers.nix_flake.run_command") + @mock.patch("pkgmgr.actions.repository.install.installers.nix_flake.run_command") def test_run_removes_old_profile_and_installs_outputs( self, mock_run_command, @@ -74,7 +74,7 @@ class TestNixFlakeInstaller(unittest.TestCase): self.assertEqual(cmds[0], remove_cmd) @patch("shutil.which", return_value="/usr/bin/nix") - @mock.patch("pkgmgr.installers.nix_flake.run_command") + @mock.patch("pkgmgr.actions.repository.install.installers.nix_flake.run_command") def test_ensure_old_profile_removed_ignores_systemexit( self, mock_run_command, diff --git a/tests/unit/pkgmgr/installers/test_python_installer.py b/tests/unit/pkgmgr/installers/test_python_installer.py index 7c6186c..713230b 100644 --- a/tests/unit/pkgmgr/installers/test_python_installer.py +++ b/tests/unit/pkgmgr/installers/test_python_installer.py @@ -4,8 +4,8 @@ import os import unittest from unittest.mock import patch -from pkgmgr.context import RepoContext -from pkgmgr.installers.python import PythonInstaller +from pkgmgr.actions.repository.install.context import RepoContext +from pkgmgr.actions.repository.install.installers.python import PythonInstaller class TestPythonInstaller(unittest.TestCase): @@ -34,7 +34,7 @@ class TestPythonInstaller(unittest.TestCase): def test_supports_false_when_no_pyproject(self, mock_exists): self.assertFalse(self.installer.supports(self.ctx)) - @patch("pkgmgr.installers.python.run_command") + @patch("pkgmgr.actions.repository.install.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) diff --git a/tests/unit/pkgmgr/test_branch_commands.py b/tests/unit/pkgmgr/test_branch_commands.py index 68df955..8f092b8 100644 --- a/tests/unit/pkgmgr/test_branch_commands.py +++ b/tests/unit/pkgmgr/test_branch_commands.py @@ -4,12 +4,12 @@ import unittest from types import SimpleNamespace from unittest.mock import patch -from pkgmgr.branch_commands import open_branch -from pkgmgr.git_utils import GitError +from pkgmgr.actions.branch import open_branch +from pkgmgr.core.git import GitError class TestOpenBranch(unittest.TestCase): - @patch("pkgmgr.branch_commands.run_git") + @patch("pkgmgr.actions.branch.run_git") def test_open_branch_with_explicit_name_and_default_base(self, mock_run_git) -> None: """ open_branch(name, base='main') should: @@ -42,7 +42,7 @@ class TestOpenBranch(unittest.TestCase): self.assertEqual(kwargs.get("cwd"), cwd_expected) @patch("builtins.input", return_value="feature/interactive") - @patch("pkgmgr.branch_commands.run_git") + @patch("pkgmgr.actions.branch.run_git") def test_open_branch_prompts_for_name_if_missing( self, mock_run_git, @@ -75,7 +75,7 @@ class TestOpenBranch(unittest.TestCase): self.assertEqual(args[0], args_expected) self.assertEqual(kwargs.get("cwd"), cwd_expected) - @patch("pkgmgr.branch_commands.run_git") + @patch("pkgmgr.actions.branch.run_git") def test_open_branch_raises_runtimeerror_on_git_failure(self, mock_run_git) -> None: """ If a GitError occurs (e.g. fetch fails), open_branch should diff --git a/tests/unit/pkgmgr/test_capabilities.py b/tests/unit/pkgmgr/test_capabilities.py index 7be7294..7f6bc74 100644 --- a/tests/unit/pkgmgr/test_capabilities.py +++ b/tests/unit/pkgmgr/test_capabilities.py @@ -4,7 +4,7 @@ import os import unittest from unittest.mock import patch, mock_open -from pkgmgr.capabilities import ( +from pkgmgr.actions.repository.install.capabilities import ( PythonRuntimeCapability, MakeInstallCapability, NixFlakeCapability, @@ -31,7 +31,7 @@ class TestCapabilitiesDetectors(unittest.TestCase): def setUp(self): self.ctx = DummyCtx("/tmp/repo") - @patch("pkgmgr.capabilities.os.path.exists") + @patch("pkgmgr.actions.repository.install.capabilities.os.path.exists") def test_python_runtime_python_layer_pyproject(self, mock_exists): """PythonRuntimeCapability: python layer is provided if pyproject.toml exists.""" cap = PythonRuntimeCapability() @@ -47,8 +47,8 @@ class TestCapabilitiesDetectors(unittest.TestCase): self.assertFalse(cap.is_provided(self.ctx, "nix")) self.assertFalse(cap.is_provided(self.ctx, "os-packages")) - @patch("pkgmgr.capabilities._read_text_if_exists") - @patch("pkgmgr.capabilities.os.path.exists") + @patch("pkgmgr.actions.repository.install.capabilities._read_text_if_exists") + @patch("pkgmgr.actions.repository.install.capabilities.os.path.exists") def test_python_runtime_nix_layer_flake(self, mock_exists, mock_read): """ PythonRuntimeCapability: nix layer is provided if flake.nix contains @@ -65,7 +65,7 @@ class TestCapabilitiesDetectors(unittest.TestCase): self.assertTrue(cap.applies_to_layer("nix")) self.assertTrue(cap.is_provided(self.ctx, "nix")) - @patch("pkgmgr.capabilities.os.path.exists", return_value=True) + @patch("pkgmgr.actions.repository.install.capabilities.os.path.exists", return_value=True) @patch( "builtins.open", new_callable=mock_open, @@ -78,7 +78,7 @@ class TestCapabilitiesDetectors(unittest.TestCase): self.assertTrue(cap.applies_to_layer("makefile")) self.assertTrue(cap.is_provided(self.ctx, "makefile")) - @patch("pkgmgr.capabilities.os.path.exists") + @patch("pkgmgr.actions.repository.install.capabilities.os.path.exists") def test_nix_flake_capability_on_nix_layer(self, mock_exists): """NixFlakeCapability: nix layer is provided if flake.nix exists.""" cap = NixFlakeCapability() @@ -153,7 +153,7 @@ class TestDetectCapabilities(unittest.TestCase): }, ) - with patch("pkgmgr.capabilities.CAPABILITY_MATCHERS", [dummy1, dummy2]): + with patch("pkgmgr.actions.repository.install.capabilities.CAPABILITY_MATCHERS", [dummy1, dummy2]): caps = detect_capabilities(self.ctx, layers) self.assertEqual( @@ -221,7 +221,7 @@ class TestResolveEffectiveCapabilities(unittest.TestCase): ) with patch( - "pkgmgr.capabilities.CAPABILITY_MATCHERS", + "pkgmgr.actions.repository.install.capabilities.CAPABILITY_MATCHERS", [cap_make_install, cap_python_runtime, cap_nix_flake], ): effective = resolve_effective_capabilities(self.ctx, layers) @@ -258,7 +258,7 @@ class TestResolveEffectiveCapabilities(unittest.TestCase): ) with patch( - "pkgmgr.capabilities.CAPABILITY_MATCHERS", + "pkgmgr.actions.repository.install.capabilities.CAPABILITY_MATCHERS", [cap_python_runtime], ): effective = resolve_effective_capabilities(self.ctx, layers) @@ -283,7 +283,7 @@ class TestResolveEffectiveCapabilities(unittest.TestCase): }, ) - with patch("pkgmgr.capabilities.CAPABILITY_MATCHERS", [cap_only_make]): + with patch("pkgmgr.actions.repository.install.capabilities.CAPABILITY_MATCHERS", [cap_only_make]): effective = resolve_effective_capabilities(self.ctx, layers) self.assertEqual(effective["makefile"], {"make-install"}) @@ -306,7 +306,7 @@ class TestResolveEffectiveCapabilities(unittest.TestCase): }, ) - with patch("pkgmgr.capabilities.CAPABILITY_MATCHERS", [cap_only_nix]): + with patch("pkgmgr.actions.repository.install.capabilities.CAPABILITY_MATCHERS", [cap_only_nix]): effective = resolve_effective_capabilities(self.ctx, layers) self.assertEqual(effective["makefile"], set()) @@ -337,7 +337,7 @@ class TestResolveEffectiveCapabilities(unittest.TestCase): ) with patch( - "pkgmgr.capabilities.CAPABILITY_MATCHERS", + "pkgmgr.actions.repository.install.capabilities.CAPABILITY_MATCHERS", [cap_python_runtime], ): effective = resolve_effective_capabilities(self.ctx, layers) @@ -359,7 +359,7 @@ class TestResolveEffectiveCapabilities(unittest.TestCase): ) with patch( - "pkgmgr.capabilities.CAPABILITY_MATCHERS", + "pkgmgr.actions.repository.install.capabilities.CAPABILITY_MATCHERS", [cap_dummy], ): effective = resolve_effective_capabilities(self.ctx) diff --git a/tests/unit/pkgmgr/test_changelog.py b/tests/unit/pkgmgr/test_changelog.py index 79aa50b..2d985c2 100644 --- a/tests/unit/pkgmgr/test_changelog.py +++ b/tests/unit/pkgmgr/test_changelog.py @@ -3,13 +3,12 @@ from __future__ import annotations import unittest from unittest.mock import patch -from pkgmgr.changelog import generate_changelog -from pkgmgr.git_utils import GitError -from pkgmgr.cli_core.commands.changelog import _find_previous_and_current_tag - +from pkgmgr.actions.changelog import generate_changelog +from pkgmgr.core.git import GitError +from pkgmgr.cli.commands.changelog import _find_previous_and_current_tag class TestGenerateChangelog(unittest.TestCase): - @patch("pkgmgr.changelog.run_git") + @patch("pkgmgr.actions.changelog.run_git") def test_generate_changelog_default_range_no_merges(self, mock_run_git) -> None: """ Default behaviour: @@ -35,7 +34,7 @@ class TestGenerateChangelog(unittest.TestCase): self.assertIn("HEAD", args[0]) self.assertEqual(kwargs.get("cwd"), "/repo") - @patch("pkgmgr.changelog.run_git") + @patch("pkgmgr.actions.changelog.run_git") def test_generate_changelog_with_range_and_merges(self, mock_run_git) -> None: """ Explicit range and include_merges=True: @@ -64,7 +63,7 @@ class TestGenerateChangelog(unittest.TestCase): self.assertIn("v1.0.0..v1.1.0", cmd) self.assertEqual(kwargs.get("cwd"), "/repo") - @patch("pkgmgr.changelog.run_git") + @patch("pkgmgr.actions.changelog.run_git") def test_generate_changelog_giterror_returns_error_message(self, mock_run_git) -> None: """ If Git fails, we do NOT raise; instead we return a human readable error string. @@ -77,7 +76,7 @@ class TestGenerateChangelog(unittest.TestCase): self.assertIn("simulated git failure", result) self.assertIn("v0.1.0..v0.2.0", result) - @patch("pkgmgr.changelog.run_git") + @patch("pkgmgr.actions.changelog.run_git") def test_generate_changelog_empty_output_returns_info(self, mock_run_git) -> None: """ Empty git log output -> informational message instead of empty string. diff --git a/tests/unit/pkgmgr/test_cli.py b/tests/unit/pkgmgr/test_cli.py index 837f586..ad5d28f 100644 --- a/tests/unit/pkgmgr/test_cli.py +++ b/tests/unit/pkgmgr/test_cli.py @@ -83,7 +83,7 @@ class TestCliVersion(unittest.TestCase): # This matches the new behaviour: without explicit identifiers, # version uses _select_repo_for_current_directory(ctx). self._patch_select_repo_for_current_directory = mock.patch( - "pkgmgr.cli_core.dispatch._select_repo_for_current_directory", + "pkgmgr.cli.dispatch._select_repo_for_current_directory", return_value=[self._fake_repo], ) self.mock_select_repo_for_current_directory = ( @@ -166,7 +166,7 @@ class TestCliVersion(unittest.TestCase): # Arrange: mock git tags used by handle_version with mock.patch( - "pkgmgr.cli_core.commands.version.get_tags", + "pkgmgr.cli.commands.version.get_tags", return_value=["v1.2.0", "v1.2.3", "v1.0.0"], ): # Act @@ -198,7 +198,7 @@ class TestCliVersion(unittest.TestCase): # Arrange: mock git tags (latest is 1.2.3) with mock.patch( - "pkgmgr.cli_core.commands.version.get_tags", + "pkgmgr.cli.commands.version.get_tags", return_value=["v1.2.3"], ): stdout = self._run_cli_version_and_capture() @@ -226,7 +226,7 @@ class TestCliVersion(unittest.TestCase): # Arrange: no tags returned with mock.patch( - "pkgmgr.cli_core.commands.version.get_tags", + "pkgmgr.cli.commands.version.get_tags", return_value=[], ): stdout = self._run_cli_version_and_capture() diff --git a/tests/unit/pkgmgr/test_cli_branch.py b/tests/unit/pkgmgr/test_cli_branch.py index 22e1561..23a2d84 100644 --- a/tests/unit/pkgmgr/test_cli_branch.py +++ b/tests/unit/pkgmgr/test_cli_branch.py @@ -4,8 +4,8 @@ import unittest from types import SimpleNamespace from unittest.mock import patch -from pkgmgr.cli_core.commands.branch import handle_branch -from pkgmgr.cli_core.context import CLIContext +from pkgmgr.cli.commands.branch import handle_branch +from pkgmgr.cli.context import CLIContext class TestCliBranch(unittest.TestCase): @@ -22,7 +22,7 @@ class TestCliBranch(unittest.TestCase): user_config_path="/tmp/config.yaml", ) - @patch("pkgmgr.cli_core.commands.branch.open_branch") + @patch("pkgmgr.cli.commands.branch.open_branch") def test_handle_branch_open_forwards_args_to_open_branch(self, mock_open_branch) -> None: """ handle_branch('open') should call open_branch with name, base and cwd='.'. @@ -44,7 +44,7 @@ class TestCliBranch(unittest.TestCase): self.assertEqual(call_kwargs.get("base_branch"), "develop") self.assertEqual(call_kwargs.get("cwd"), ".") - @patch("pkgmgr.cli_core.commands.branch.open_branch") + @patch("pkgmgr.cli.commands.branch.open_branch") def test_handle_branch_open_uses_default_base_when_not_set(self, mock_open_branch) -> None: """ If --base is not passed, argparse gives base='main' (default), @@ -70,7 +70,7 @@ class TestCliBranch(unittest.TestCase): # close subcommand # ------------------------------------------------------------------ - @patch("pkgmgr.cli_core.commands.branch.close_branch") + @patch("pkgmgr.cli.commands.branch.close_branch") def test_handle_branch_close_forwards_args_to_close_branch(self, mock_close_branch) -> None: """ handle_branch('close') should call close_branch with name, base and cwd='.'. @@ -92,7 +92,7 @@ class TestCliBranch(unittest.TestCase): self.assertEqual(call_kwargs.get("base_branch"), "develop") self.assertEqual(call_kwargs.get("cwd"), ".") - @patch("pkgmgr.cli_core.commands.branch.close_branch") + @patch("pkgmgr.cli.commands.branch.close_branch") def test_handle_branch_close_uses_default_base_when_not_set(self, mock_close_branch) -> None: """ If --base is not passed for 'close', argparse gives base='main' diff --git a/tests/unit/pkgmgr/test_clone_repos.py b/tests/unit/pkgmgr/test_clone_repos.py index da2200b..46b5d67 100644 --- a/tests/unit/pkgmgr/test_clone_repos.py +++ b/tests/unit/pkgmgr/test_clone_repos.py @@ -2,7 +2,7 @@ import unittest from unittest.mock import patch, MagicMock -from pkgmgr.clone_repos import clone_repos +from pkgmgr.actions.repository.clone import clone_repos class TestCloneRepos(unittest.TestCase): @@ -16,12 +16,12 @@ class TestCloneRepos(unittest.TestCase): self.base_dir = "/tmp/repos" self.all_repos = self.selected - @patch("pkgmgr.clone_repos.verify_repository") - @patch("pkgmgr.clone_repos.subprocess.run") - @patch("pkgmgr.clone_repos.os.makedirs") - @patch("pkgmgr.clone_repos.os.path.exists") - @patch("pkgmgr.clone_repos.get_repo_dir") - @patch("pkgmgr.clone_repos.get_repo_identifier") + @patch("pkgmgr.actions.repository.clone.verify_repository") + @patch("pkgmgr.actions.repository.clone.subprocess.run") + @patch("pkgmgr.actions.repository.clone.os.makedirs") + @patch("pkgmgr.actions.repository.clone.os.path.exists") + @patch("pkgmgr.actions.repository.clone.get_repo_dir") + @patch("pkgmgr.actions.repository.clone.get_repo_identifier") def test_clone_ssh_mode_uses_ssh_url( self, mock_get_repo_identifier, @@ -55,12 +55,12 @@ class TestCloneRepos(unittest.TestCase): self.assertIn("git@github.com:user/repo.git", cmd) self.assertEqual(cwd, "/tmp/repos/user") - @patch("pkgmgr.clone_repos.verify_repository") - @patch("pkgmgr.clone_repos.subprocess.run") - @patch("pkgmgr.clone_repos.os.makedirs") - @patch("pkgmgr.clone_repos.os.path.exists") - @patch("pkgmgr.clone_repos.get_repo_dir") - @patch("pkgmgr.clone_repos.get_repo_identifier") + @patch("pkgmgr.actions.repository.clone.verify_repository") + @patch("pkgmgr.actions.repository.clone.subprocess.run") + @patch("pkgmgr.actions.repository.clone.os.makedirs") + @patch("pkgmgr.actions.repository.clone.os.path.exists") + @patch("pkgmgr.actions.repository.clone.get_repo_dir") + @patch("pkgmgr.actions.repository.clone.get_repo_identifier") def test_clone_https_mode_uses_https_url( self, mock_get_repo_identifier, @@ -93,12 +93,12 @@ class TestCloneRepos(unittest.TestCase): self.assertIn("https://github.com/user/repo.git", cmd) self.assertEqual(cwd, "/tmp/repos/user") - @patch("pkgmgr.clone_repos.verify_repository") - @patch("pkgmgr.clone_repos.subprocess.run") - @patch("pkgmgr.clone_repos.os.makedirs") - @patch("pkgmgr.clone_repos.os.path.exists") - @patch("pkgmgr.clone_repos.get_repo_dir") - @patch("pkgmgr.clone_repos.get_repo_identifier") + @patch("pkgmgr.actions.repository.clone.verify_repository") + @patch("pkgmgr.actions.repository.clone.subprocess.run") + @patch("pkgmgr.actions.repository.clone.os.makedirs") + @patch("pkgmgr.actions.repository.clone.os.path.exists") + @patch("pkgmgr.actions.repository.clone.get_repo_dir") + @patch("pkgmgr.actions.repository.clone.get_repo_identifier") def test_clone_shallow_mode_uses_https_with_depth( self, mock_get_repo_identifier, @@ -131,12 +131,12 @@ class TestCloneRepos(unittest.TestCase): self.assertIn("https://github.com/user/repo.git", cmd) self.assertEqual(cwd, "/tmp/repos/user") - @patch("pkgmgr.clone_repos.verify_repository") - @patch("pkgmgr.clone_repos.subprocess.run") - @patch("pkgmgr.clone_repos.os.makedirs") - @patch("pkgmgr.clone_repos.os.path.exists") - @patch("pkgmgr.clone_repos.get_repo_dir") - @patch("pkgmgr.clone_repos.get_repo_identifier") + @patch("pkgmgr.actions.repository.clone.verify_repository") + @patch("pkgmgr.actions.repository.clone.subprocess.run") + @patch("pkgmgr.actions.repository.clone.os.makedirs") + @patch("pkgmgr.actions.repository.clone.os.path.exists") + @patch("pkgmgr.actions.repository.clone.get_repo_dir") + @patch("pkgmgr.actions.repository.clone.get_repo_identifier") def test_preview_mode_does_not_call_subprocess_run( self, mock_get_repo_identifier, diff --git a/tests/unit/pkgmgr/test_context.py b/tests/unit/pkgmgr/test_context.py index 8512f3f..9713f7e 100644 --- a/tests/unit/pkgmgr/test_context.py +++ b/tests/unit/pkgmgr/test_context.py @@ -1,5 +1,5 @@ import unittest -from pkgmgr.context import RepoContext +from pkgmgr.actions.repository.install.context import RepoContext class TestRepoContext(unittest.TestCase): diff --git a/tests/unit/pkgmgr/test_create_ink.py b/tests/unit/pkgmgr/test_create_ink.py index 674f1a7..d4065c7 100644 --- a/tests/unit/pkgmgr/test_create_ink.py +++ b/tests/unit/pkgmgr/test_create_ink.py @@ -1,32 +1,32 @@ -# tests/unit/pkgmgr/test_create_ink.py +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- import unittest from unittest.mock import patch -import pkgmgr.create_ink as create_ink_module +import pkgmgr.core.command.ink as create_ink_module class TestCreateInk(unittest.TestCase): - @patch("pkgmgr.create_ink.get_repo_dir") - @patch("pkgmgr.create_ink.get_repo_identifier") + @patch("pkgmgr.core.command.ink.get_repo_dir") + @patch("pkgmgr.core.command.ink.get_repo_identifier") def test_create_ink_skips_when_no_command( self, mock_get_repo_identifier, mock_get_repo_dir, ): - repo = {} # no 'command' key + repo = {"name": "test-repo", "command": None} mock_get_repo_identifier.return_value = "test-id" mock_get_repo_dir.return_value = "/repos/test-id" - with patch("pkgmgr.create_ink.os.makedirs") as mock_makedirs, \ - patch("pkgmgr.create_ink.os.symlink") as mock_symlink, \ - patch("pkgmgr.create_ink.os.chmod") as mock_chmod: + with patch("pkgmgr.core.command.ink.os.makedirs") as mock_makedirs, \ + patch("pkgmgr.core.command.ink.os.symlink") as mock_symlink, \ + patch("pkgmgr.core.command.ink.os.chmod") as mock_chmod: create_ink_module.create_ink( repo=repo, repositories_base_dir="/repos", bin_dir="/bin", all_repos=[repo], - quiet=True, preview=False, ) @@ -34,26 +34,25 @@ class TestCreateInk(unittest.TestCase): mock_symlink.assert_not_called() mock_chmod.assert_not_called() - @patch("pkgmgr.create_ink.get_repo_dir") - @patch("pkgmgr.create_ink.get_repo_identifier") + @patch("pkgmgr.core.command.ink.get_repo_dir") + @patch("pkgmgr.core.command.ink.get_repo_identifier") def test_create_ink_preview_only( self, mock_get_repo_identifier, mock_get_repo_dir, ): - repo = {"command": "/repos/test-id/main.py"} + repo = {"name": "test-repo", "command": "repo-cmd"} mock_get_repo_identifier.return_value = "test-id" mock_get_repo_dir.return_value = "/repos/test-id" - with patch("pkgmgr.create_ink.os.makedirs") as mock_makedirs, \ - patch("pkgmgr.create_ink.os.symlink") as mock_symlink, \ - patch("pkgmgr.create_ink.os.chmod") as mock_chmod: + with patch("pkgmgr.core.command.ink.os.makedirs") as mock_makedirs, \ + patch("pkgmgr.core.command.ink.os.symlink") as mock_symlink, \ + patch("pkgmgr.core.command.ink.os.chmod") as mock_chmod: create_ink_module.create_ink( repo=repo, repositories_base_dir="/repos", bin_dir="/bin", all_repos=[repo], - quiet=True, preview=True, ) @@ -61,8 +60,8 @@ class TestCreateInk(unittest.TestCase): mock_symlink.assert_not_called() mock_chmod.assert_not_called() - @patch("pkgmgr.create_ink.get_repo_dir") - @patch("pkgmgr.create_ink.get_repo_identifier") + @patch("pkgmgr.core.command.ink.get_repo_dir") + @patch("pkgmgr.core.command.ink.get_repo_identifier") def test_create_ink_creates_symlink_and_alias( self, mock_get_repo_identifier, @@ -75,19 +74,18 @@ class TestCreateInk(unittest.TestCase): mock_get_repo_identifier.return_value = "test-id" mock_get_repo_dir.return_value = "/repos/test-id" - with patch("pkgmgr.create_ink.os.makedirs") as mock_makedirs, \ - patch("pkgmgr.create_ink.os.symlink") as mock_symlink, \ - patch("pkgmgr.create_ink.os.chmod") as mock_chmod, \ - patch("pkgmgr.create_ink.os.path.exists", return_value=False), \ - patch("pkgmgr.create_ink.os.path.islink", return_value=False), \ - patch("pkgmgr.create_ink.os.remove") as mock_remove, \ - patch("pkgmgr.create_ink.os.path.realpath", side_effect=lambda p: p): + with patch("pkgmgr.core.command.ink.os.makedirs") as mock_makedirs, \ + patch("pkgmgr.core.command.ink.os.symlink") as mock_symlink, \ + patch("pkgmgr.core.command.ink.os.chmod") as mock_chmod, \ + patch("pkgmgr.core.command.ink.os.path.exists", return_value=False), \ + patch("pkgmgr.core.command.ink.os.path.islink", return_value=False), \ + patch("pkgmgr.core.command.ink.os.remove") as mock_remove, \ + patch("pkgmgr.core.command.ink.os.path.realpath", side_effect=lambda p: p): create_ink_module.create_ink( repo=repo, repositories_base_dir="/repos", bin_dir="/bin", all_repos=[repo], - quiet=True, preview=False, ) @@ -95,8 +93,6 @@ class TestCreateInk(unittest.TestCase): self.assertEqual(mock_symlink.call_count, 2) mock_makedirs.assert_called_once() mock_chmod.assert_called_once() - mock_remove.assert_not_called() - if __name__ == "__main__": unittest.main() diff --git a/tests/unit/pkgmgr/test_git_utils.py b/tests/unit/pkgmgr/test_git_utils.py index a9c33ac..46739b0 100644 --- a/tests/unit/pkgmgr/test_git_utils.py +++ b/tests/unit/pkgmgr/test_git_utils.py @@ -6,7 +6,7 @@ import unittest from types import SimpleNamespace from unittest.mock import patch -from pkgmgr.git_utils import ( +from pkgmgr.core.git import ( GitError, run_git, get_tags, @@ -16,7 +16,7 @@ from pkgmgr.git_utils import ( class TestGitUtils(unittest.TestCase): - @patch("pkgmgr.git_utils.subprocess.run") + @patch("pkgmgr.core.git.subprocess.run") def test_run_git_success(self, mock_run): mock_run.return_value = SimpleNamespace( stdout="ok\n", @@ -33,7 +33,7 @@ class TestGitUtils(unittest.TestCase): self.assertEqual(args[0][0], "git") self.assertEqual(kwargs.get("cwd"), "/tmp/repo") - @patch("pkgmgr.git_utils.subprocess.run") + @patch("pkgmgr.core.git.subprocess.run") def test_run_git_failure_raises_giterror(self, mock_run): mock_run.side_effect = subprocess.CalledProcessError( returncode=1, @@ -51,7 +51,7 @@ class TestGitUtils(unittest.TestCase): self.assertIn("bad", msg) self.assertIn("error", msg) - @patch("pkgmgr.git_utils.subprocess.run") + @patch("pkgmgr.core.git.subprocess.run") def test_get_tags_empty(self, mock_run): mock_run.return_value = SimpleNamespace( stdout="", @@ -62,7 +62,7 @@ class TestGitUtils(unittest.TestCase): tags = get_tags(cwd="/tmp/repo") self.assertEqual(tags, []) - @patch("pkgmgr.git_utils.subprocess.run") + @patch("pkgmgr.core.git.subprocess.run") def test_get_tags_non_empty(self, mock_run): mock_run.return_value = SimpleNamespace( stdout="v1.0.0\nv1.1.0\n", @@ -73,7 +73,7 @@ class TestGitUtils(unittest.TestCase): tags = get_tags(cwd="/tmp/repo") self.assertEqual(tags, ["v1.0.0", "v1.1.0"]) - @patch("pkgmgr.git_utils.subprocess.run") + @patch("pkgmgr.core.git.subprocess.run") def test_get_head_commit_success(self, mock_run): mock_run.return_value = SimpleNamespace( stdout="abc123\n", @@ -84,7 +84,7 @@ class TestGitUtils(unittest.TestCase): commit = get_head_commit(cwd="/tmp/repo") self.assertEqual(commit, "abc123") - @patch("pkgmgr.git_utils.subprocess.run") + @patch("pkgmgr.core.git.subprocess.run") def test_get_head_commit_failure_returns_none(self, mock_run): mock_run.side_effect = subprocess.CalledProcessError( returncode=1, @@ -96,7 +96,7 @@ class TestGitUtils(unittest.TestCase): commit = get_head_commit(cwd="/tmp/repo") self.assertIsNone(commit) - @patch("pkgmgr.git_utils.subprocess.run") + @patch("pkgmgr.core.git.subprocess.run") def test_get_current_branch_success(self, mock_run): mock_run.return_value = SimpleNamespace( stdout="main\n", @@ -107,7 +107,7 @@ class TestGitUtils(unittest.TestCase): branch = get_current_branch(cwd="/tmp/repo") self.assertEqual(branch, "main") - @patch("pkgmgr.git_utils.subprocess.run") + @patch("pkgmgr.core.git.subprocess.run") def test_get_current_branch_failure_returns_none(self, mock_run): mock_run.side_effect = subprocess.CalledProcessError( returncode=1, diff --git a/tests/unit/pkgmgr/test_install_repos.py b/tests/unit/pkgmgr/test_install_repos.py index 05c8116..8271c44 100644 --- a/tests/unit/pkgmgr/test_install_repos.py +++ b/tests/unit/pkgmgr/test_install_repos.py @@ -3,9 +3,9 @@ import unittest from unittest.mock import patch, MagicMock -from pkgmgr.context import RepoContext -import pkgmgr.install_repos as install_module -from pkgmgr.installers.base import BaseInstaller +from pkgmgr.actions.repository.install.context import RepoContext +import pkgmgr.actions.repository.install as install_module +from pkgmgr.actions.repository.install.installers.base import BaseInstaller class DummyInstaller(BaseInstaller): @@ -25,12 +25,12 @@ class DummyInstaller(BaseInstaller): class TestInstallReposOrchestration(unittest.TestCase): - @patch("pkgmgr.install_repos.create_ink") - @patch("pkgmgr.install_repos.resolve_command_for_repo") - @patch("pkgmgr.install_repos.verify_repository") - @patch("pkgmgr.install_repos.get_repo_dir") - @patch("pkgmgr.install_repos.get_repo_identifier") - @patch("pkgmgr.install_repos.clone_repos") + @patch("pkgmgr.actions.repository.install.create_ink") + @patch("pkgmgr.actions.repository.install.resolve_command_for_repo") + @patch("pkgmgr.actions.repository.install.verify_repository") + @patch("pkgmgr.actions.repository.install.get_repo_dir") + @patch("pkgmgr.actions.repository.install.get_repo_identifier") + @patch("pkgmgr.actions.repository.install.clone_repos") def test_install_repos_runs_pipeline_for_each_repo( self, mock_clone_repos, @@ -82,10 +82,10 @@ class TestInstallReposOrchestration(unittest.TestCase): self.assertEqual(mock_verify_repository.call_count, 2) self.assertEqual(mock_resolve_command_for_repo.call_count, 2) - @patch("pkgmgr.install_repos.verify_repository") - @patch("pkgmgr.install_repos.get_repo_dir") - @patch("pkgmgr.install_repos.get_repo_identifier") - @patch("pkgmgr.install_repos.clone_repos") + @patch("pkgmgr.actions.repository.install.verify_repository") + @patch("pkgmgr.actions.repository.install.get_repo_dir") + @patch("pkgmgr.actions.repository.install.get_repo_identifier") + @patch("pkgmgr.actions.repository.install.clone_repos") def test_install_repos_skips_on_failed_verification( self, mock_clone_repos, @@ -104,10 +104,10 @@ class TestInstallReposOrchestration(unittest.TestCase): mock_verify_repository.return_value = (False, ["sig error"], None, None) dummy_installer = DummyInstaller() - with patch("os.path.exists", return_value=True), \ - patch("pkgmgr.install_repos.create_ink") as mock_create_ink, \ - patch("pkgmgr.install_repos.resolve_command_for_repo") as mock_resolve_cmd, \ - patch("builtins.input", return_value="n"): + with patch("pkgmgr.actions.repository.install.create_ink") as mock_create_ink, \ + patch("pkgmgr.actions.repository.install.resolve_command_for_repo") as mock_resolve_cmd, \ + patch("os.path.exists", return_value=True), \ + patch("builtins.input", return_value="n"): old_installers = install_module.INSTALLERS install_module.INSTALLERS = [dummy_installer] try: diff --git a/tests/unit/pkgmgr/test_release.py b/tests/unit/pkgmgr/test_release.py index 4231c86..0c7d01b 100644 --- a/tests/unit/pkgmgr/test_release.py +++ b/tests/unit/pkgmgr/test_release.py @@ -6,8 +6,8 @@ import textwrap import unittest from unittest.mock import patch -from pkgmgr.versioning import SemVer -from pkgmgr.release import ( +from pkgmgr.core.version.semver import SemVer +from pkgmgr.actions.release import ( _determine_current_version, _bump_semver, update_pyproject_version, @@ -21,7 +21,7 @@ from pkgmgr.release import ( class TestDetermineCurrentVersion(unittest.TestCase): - @patch("pkgmgr.release.get_tags", return_value=[]) + @patch("pkgmgr.actions.release.get_tags", return_value=[]) def test_determine_current_version_no_tags_returns_zero( self, mock_get_tags, @@ -31,8 +31,8 @@ class TestDetermineCurrentVersion(unittest.TestCase): self.assertEqual((ver.major, ver.minor, ver.patch), (0, 0, 0)) mock_get_tags.assert_called_once() - @patch("pkgmgr.release.find_latest_version") - @patch("pkgmgr.release.get_tags") + @patch("pkgmgr.actions.release.find_latest_version") + @patch("pkgmgr.actions.release.get_tags") def test_determine_current_version_uses_latest_semver_tag( self, mock_get_tags, @@ -365,17 +365,17 @@ class TestUpdateDebianChangelog(unittest.TestCase): class TestReleaseOrchestration(unittest.TestCase): - @patch("pkgmgr.release.sys.stdin.isatty", return_value=False) - @patch("pkgmgr.release._run_git_command") - @patch("pkgmgr.release.update_debian_changelog") - @patch("pkgmgr.release.update_spec_version") - @patch("pkgmgr.release.update_pkgbuild_version") - @patch("pkgmgr.release.update_flake_version") - @patch("pkgmgr.release.get_current_branch", return_value="develop") - @patch("pkgmgr.release.update_changelog") - @patch("pkgmgr.release.update_pyproject_version") - @patch("pkgmgr.release._bump_semver") - @patch("pkgmgr.release._determine_current_version") + @patch("pkgmgr.actions.release.sys.stdin.isatty", return_value=False) + @patch("pkgmgr.actions.release._run_git_command") + @patch("pkgmgr.actions.release.update_debian_changelog") + @patch("pkgmgr.actions.release.update_spec_version") + @patch("pkgmgr.actions.release.update_pkgbuild_version") + @patch("pkgmgr.actions.release.update_flake_version") + @patch("pkgmgr.actions.release.get_current_branch", return_value="develop") + @patch("pkgmgr.actions.release.update_changelog") + @patch("pkgmgr.actions.release.update_pyproject_version") + @patch("pkgmgr.actions.release._bump_semver") + @patch("pkgmgr.actions.release._determine_current_version") def test_release_happy_path_uses_helpers_and_git( self, mock_determine_current_version, @@ -451,17 +451,17 @@ class TestReleaseOrchestration(unittest.TestCase): self.assertIn("git push origin develop", git_calls) self.assertIn("git push origin --tags", git_calls) - @patch("pkgmgr.release.sys.stdin.isatty", return_value=False) - @patch("pkgmgr.release._run_git_command") - @patch("pkgmgr.release.update_debian_changelog") - @patch("pkgmgr.release.update_spec_version") - @patch("pkgmgr.release.update_pkgbuild_version") - @patch("pkgmgr.release.update_flake_version") - @patch("pkgmgr.release.get_current_branch", return_value="develop") - @patch("pkgmgr.release.update_changelog") - @patch("pkgmgr.release.update_pyproject_version") - @patch("pkgmgr.release._bump_semver") - @patch("pkgmgr.release._determine_current_version") + @patch("pkgmgr.actions.release.sys.stdin.isatty", return_value=False) + @patch("pkgmgr.actions.release._run_git_command") + @patch("pkgmgr.actions.release.update_debian_changelog") + @patch("pkgmgr.actions.release.update_spec_version") + @patch("pkgmgr.actions.release.update_pkgbuild_version") + @patch("pkgmgr.actions.release.update_flake_version") + @patch("pkgmgr.actions.release.get_current_branch", return_value="develop") + @patch("pkgmgr.actions.release.update_changelog") + @patch("pkgmgr.actions.release.update_pyproject_version") + @patch("pkgmgr.actions.release._bump_semver") + @patch("pkgmgr.actions.release._determine_current_version") def test_release_preview_mode_skips_git_and_uses_preview_flag( self, mock_determine_current_version, diff --git a/tests/unit/pkgmgr/test_resolve_command.py b/tests/unit/pkgmgr/test_resolve_command.py index c539ce2..484faff 100644 --- a/tests/unit/pkgmgr/test_resolve_command.py +++ b/tests/unit/pkgmgr/test_resolve_command.py @@ -3,7 +3,7 @@ import unittest from unittest.mock import patch -import pkgmgr.resolve_command as resolve_command_module +import pkgmgr.core.command.resolve as resolve_command_module class TestResolveCommandForRepo(unittest.TestCase): @@ -16,7 +16,7 @@ class TestResolveCommandForRepo(unittest.TestCase): ) self.assertEqual(result, "/custom/cmd") - @patch("pkgmgr.resolve_command.shutil.which", return_value="/usr/bin/tool") + @patch("pkgmgr.core.command.resolve.shutil.which", return_value="/usr/bin/tool") def test_system_binary_returns_none_and_no_error(self, mock_which): repo = {} result = resolve_command_module.resolve_command_for_repo( @@ -27,10 +27,10 @@ class TestResolveCommandForRepo(unittest.TestCase): # System binary → no link self.assertIsNone(result) - @patch("pkgmgr.resolve_command.os.access") - @patch("pkgmgr.resolve_command.os.path.exists") - @patch("pkgmgr.resolve_command.shutil.which", return_value=None) - @patch("pkgmgr.resolve_command.os.path.expanduser", return_value="/fakehome") + @patch("pkgmgr.core.command.resolve.os.access") + @patch("pkgmgr.core.command.resolve.os.path.exists") + @patch("pkgmgr.core.command.resolve.shutil.which", return_value=None) + @patch("pkgmgr.core.command.resolve.os.path.expanduser", return_value="/fakehome") def test_nix_profile_binary( self, mock_expanduser, @@ -64,10 +64,10 @@ class TestResolveCommandForRepo(unittest.TestCase): ) self.assertEqual(result, nix_path) - @patch("pkgmgr.resolve_command.os.access") - @patch("pkgmgr.resolve_command.os.path.exists") - @patch("pkgmgr.resolve_command.os.path.expanduser", return_value="/home/user") - @patch("pkgmgr.resolve_command.shutil.which", return_value="/home/user/.local/bin/tool") + @patch("pkgmgr.core.command.resolve.os.access") + @patch("pkgmgr.core.command.resolve.os.path.exists") + @patch("pkgmgr.core.command.resolve.os.path.expanduser", return_value="/home/user") + @patch("pkgmgr.core.command.resolve.shutil.which", return_value="/home/user/.local/bin/tool") def test_non_system_binary_on_path( self, mock_which, @@ -102,10 +102,10 @@ class TestResolveCommandForRepo(unittest.TestCase): ) self.assertEqual(result, non_system_path) - @patch("pkgmgr.resolve_command.os.access") - @patch("pkgmgr.resolve_command.os.path.exists") - @patch("pkgmgr.resolve_command.shutil.which", return_value=None) - @patch("pkgmgr.resolve_command.os.path.expanduser", return_value="/fakehome") + @patch("pkgmgr.core.command.resolve.os.access") + @patch("pkgmgr.core.command.resolve.os.path.exists") + @patch("pkgmgr.core.command.resolve.shutil.which", return_value=None) + @patch("pkgmgr.core.command.resolve.os.path.expanduser", return_value="/fakehome") def test_fallback_to_main_py( self, mock_expanduser, @@ -136,10 +136,10 @@ class TestResolveCommandForRepo(unittest.TestCase): ) self.assertEqual(result, main_py) - @patch("pkgmgr.resolve_command.os.access", return_value=False) - @patch("pkgmgr.resolve_command.os.path.exists", return_value=False) - @patch("pkgmgr.resolve_command.shutil.which", return_value=None) - @patch("pkgmgr.resolve_command.os.path.expanduser", return_value="/fakehome") + @patch("pkgmgr.core.command.resolve.os.access", return_value=False) + @patch("pkgmgr.core.command.resolve.os.path.exists", return_value=False) + @patch("pkgmgr.core.command.resolve.shutil.which", return_value=None) + @patch("pkgmgr.core.command.resolve.os.path.expanduser", return_value="/fakehome") def test_no_command_results_in_system_exit( self, mock_expanduser, diff --git a/tests/unit/pkgmgr/test_versioning.py b/tests/unit/pkgmgr/test_versioning.py index 1744902..c65edd8 100644 --- a/tests/unit/pkgmgr/test_versioning.py +++ b/tests/unit/pkgmgr/test_versioning.py @@ -3,7 +3,7 @@ import unittest -from pkgmgr.versioning import ( +from pkgmgr.core.version.semver import ( SemVer, is_semver_tag, extract_semver_from_tags,