Coverage for src/ezcompiler/adapters/base_installer.py: 100.00%
13 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-16 16:37 +0000
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-16 16:37 +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
22from ..shared import InstallerConfig
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: InstallerConfig) -> None:
38 self._config = config
40 # ////////////////////////////////////////////////
41 # ABSTRACT METHODS
42 # ////////////////////////////////////////////////
44 @abstractmethod
45 def build(
46 self,
47 bundle_dir: Path,
48 app_name: str,
49 version: str,
50 output_dir: Path,
51 *,
52 company_name: str = "",
53 icon: str = "",
54 main_file: str = "",
55 ) -> Path:
56 """Build the installer executable. Raises InstallerError on failure."""
58 @abstractmethod
59 def get_installer_name(self) -> str:
60 """Human-readable installer backend name."""
62 # ////////////////////////////////////////////////
63 # VALIDATION METHODS
64 # ////////////////////////////////////////////////
66 def _validate_bundle_dir(self, bundle_dir: Path) -> None:
67 """Ensure the bundle directory exists and is non-empty."""
68 if not bundle_dir.is_dir():
69 raise InstallerConfigError(f"Bundle directory does not exist: {bundle_dir}")
70 if not any(bundle_dir.iterdir()):
71 raise InstallerConfigError(f"Bundle directory is empty: {bundle_dir}")