Coverage for src/ezcompiler/adapters/base_installer.py: 100.00%
13 statements
« prev ^ index » next coverage.py v7.14.3, created at 2026-07-04 11:22 +0000
« prev ^ index » next coverage.py v7.14.3, created at 2026-07-04 11:22 +0000
1# ///////////////////////////////////////////////////////////////
2# BASE_INSTALLER - Abstract base installer interface
3# Project: ezcompiler
4# ///////////////////////////////////////////////////////////////
6"""
7Base installer - Abstract base class for first-deployment installer builders.
9Defines the interface (conforming to ``types.InstallerPort``) and shared
10validation. The structural contract is the Port; this base factors common
11behaviour. Boundaries are typed via the Port, not this base.
12"""
14from __future__ import annotations
16# ///////////////////////////////////////////////////////////////
17# IMPORTS
18# ///////////////////////////////////////////////////////////////
19from abc import ABC, abstractmethod
20from pathlib import Path
21from typing import Any
23from ..shared.exceptions import InstallerConfigError
25# ///////////////////////////////////////////////////////////////
26# CLASSES
27# ///////////////////////////////////////////////////////////////
30class BaseInstaller(ABC):
31 """Abstract base class for first-deployment installer builders."""
33 # ////////////////////////////////////////////////
34 # INITIALIZATION
35 # ////////////////////////////////////////////////
37 def __init__(self, config: dict[str, Any] | None = None) -> None:
38 self._config = config or {}
40 # ////////////////////////////////////////////////
41 # ABSTRACT METHODS
42 # ////////////////////////////////////////////////
44 @abstractmethod
45 def build(
46 self, bundle_dir: Path, app_name: str, version: str, output_dir: Path
47 ) -> Path:
48 """Build the installer executable. Raises InstallerError on failure."""
50 @abstractmethod
51 def get_installer_name(self) -> str:
52 """Human-readable installer backend name."""
54 # ////////////////////////////////////////////////
55 # VALIDATION METHODS
56 # ////////////////////////////////////////////////
58 def _validate_bundle_dir(self, bundle_dir: Path) -> None:
59 """Ensure the bundle directory exists and is non-empty."""
60 if not bundle_dir.is_dir():
61 raise InstallerConfigError(f"Bundle directory does not exist: {bundle_dir}")
62 if not any(bundle_dir.iterdir()):
63 raise InstallerConfigError(f"Bundle directory is empty: {bundle_dir}")