""" Configuration management for deb-mock """ import os from pathlib import Path from typing import Any, Dict import yaml from .exceptions import ConfigurationError class Config: """Configuration class for deb-mock""" def __init__(self, **kwargs): # Default configuration self.chroot_name = kwargs.get("chroot_name", "bookworm-amd64") self.architecture = kwargs.get("architecture", "amd64") self.suite = kwargs.get("suite", "bookworm") self.output_dir = kwargs.get("output_dir", "./output") self.keep_chroot = kwargs.get("keep_chroot", False) self.verbose = kwargs.get("verbose", False) self.debug = kwargs.get("debug", False) # Chroot configuration self.basedir = kwargs.get("basedir", "/var/lib/deb-mock") self.chroot_dir = kwargs.get("chroot_dir", "/var/lib/deb-mock/chroots") self.chroot_config_dir = kwargs.get("chroot_config_dir", "/etc/schroot/chroot.d") self.chroot_home = kwargs.get("chroot_home", "/home/build") # sbuild configuration self.sbuild_config = kwargs.get("sbuild_config", "/etc/sbuild/sbuild.conf") self.sbuild_log_dir = kwargs.get("sbuild_log_dir", "/var/log/sbuild") # Build configuration self.build_deps = kwargs.get("build_deps", []) self.build_env = kwargs.get("build_env", {}) self.build_options = kwargs.get("build_options", []) # Metadata configuration self.metadata_dir = kwargs.get("metadata_dir", "./metadata") self.capture_logs = kwargs.get("capture_logs", True) self.capture_changes = kwargs.get("capture_changes", True) # Speed optimization (Mock-inspired features) self.cache_dir = kwargs.get("cache_dir", "/var/cache/deb-mock") self.use_root_cache = kwargs.get("use_root_cache", True) self.root_cache_dir = kwargs.get("root_cache_dir", "/var/cache/deb-mock/root-cache") self.root_cache_age = kwargs.get("root_cache_age", 7) # days self.use_package_cache = kwargs.get("use_package_cache", True) self.package_cache_dir = kwargs.get("package_cache_dir", "/var/cache/deb-mock/package-cache") self.use_ccache = kwargs.get("use_ccache", False) self.ccache_dir = kwargs.get("ccache_dir", "/var/cache/deb-mock/ccache") self.use_tmpfs = kwargs.get("use_tmpfs", False) self.tmpfs_size = kwargs.get("tmpfs_size", "2G") # Parallel builds self.parallel_jobs = kwargs.get("parallel_jobs", 4) self.parallel_compression = kwargs.get("parallel_compression", True) # Advanced parallel build support self.parallel_builds = kwargs.get("parallel_builds", 2) # Number of parallel chroots self.parallel_chroot_prefix = kwargs.get("parallel_chroot_prefix", "parallel") self.parallel_build_timeout = kwargs.get("parallel_build_timeout", 3600) # seconds self.parallel_build_cleanup = kwargs.get("parallel_build_cleanup", True) # Advanced mount management self.advanced_mounts = kwargs.get("advanced_mounts", {}) self.bind_mounts = kwargs.get("bind_mounts", []) self.tmpfs_mounts = kwargs.get("tmpfs_mounts", []) self.overlay_mounts = kwargs.get("overlay_mounts", []) self.mount_options = kwargs.get("mount_options", {}) # Mount isolation and security self.mount_proc = kwargs.get("mount_proc", True) self.mount_sys = kwargs.get("mount_sys", True) self.mount_dev = kwargs.get("mount_dev", True) self.mount_devpts = kwargs.get("mount_devpts", True) self.mount_tmp = kwargs.get("mount_tmp", True) self.mount_home = kwargs.get("mount_home", False) # Advanced chroot features self.use_namespaces = kwargs.get("use_namespaces", False) self.uid_mapping = kwargs.get("uid_mapping", None) self.gid_mapping = kwargs.get("gid_mapping", None) self.capabilities = kwargs.get("capabilities", []) self.seccomp_profile = kwargs.get("seccomp_profile", None) # UID/GID management self.chroot_user = kwargs.get("chroot_user", "build") self.chroot_group = kwargs.get("chroot_group", "build") self.chroot_uid = kwargs.get("chroot_uid", 1000) self.chroot_gid = kwargs.get("chroot_gid", 1000) self.use_host_user = kwargs.get("use_host_user", False) self.copy_host_users = kwargs.get("copy_host_users", []) self.preserve_uid_gid = kwargs.get("preserve_uid_gid", True) # Plugin system self.plugins = kwargs.get("plugins", []) self.plugin_conf = kwargs.get("plugin_conf", {}) self.plugin_dir = kwargs.get("plugin_dir", "/usr/share/deb-mock/plugins") # Performance monitoring and optimization self.enable_performance_monitoring = kwargs.get("enable_performance_monitoring", True) self.performance_metrics_dir = kwargs.get("performance_metrics_dir", "./performance-metrics") self.performance_retention_days = kwargs.get("performance_retention_days", 30) self.performance_auto_optimization = kwargs.get("performance_auto_optimization", False) self.performance_benchmark_iterations = kwargs.get("performance_benchmark_iterations", 3) self.performance_reporting = kwargs.get("performance_reporting", True) # Network and proxy self.use_host_resolv = kwargs.get("use_host_resolv", True) self.http_proxy = kwargs.get("http_proxy", None) self.https_proxy = kwargs.get("https_proxy", None) self.no_proxy = kwargs.get("no_proxy", None) # Mirror configuration self.mirror = kwargs.get("mirror", "http://deb.debian.org/debian/") self.security_mirror = kwargs.get("security_mirror", None) self.backports_mirror = kwargs.get("backports_mirror", None) # Isolation and security self.isolation = kwargs.get("isolation", "schroot") # schroot, simple, nspawn self.enable_network = kwargs.get("enable_network", True) self.selinux_enabled = kwargs.get("selinux_enabled", False) # Bootstrap chroot support (Mock FAQ #2 - Cross-distribution builds) self.use_bootstrap_chroot = kwargs.get("use_bootstrap_chroot", False) self.bootstrap_chroot_name = kwargs.get("bootstrap_chroot_name", None) self.bootstrap_arch = kwargs.get("bootstrap_arch", None) self.bootstrap_suite = kwargs.get("bootstrap_suite", None) # Build environment customization self.chroot_setup_cmd = kwargs.get("chroot_setup_cmd", []) self.chroot_additional_packages = kwargs.get("chroot_additional_packages", []) # Environment variable preservation (Mock FAQ #1) self.preserve_environment = kwargs.get("preserve_environment", []) self.environment_sanitization = kwargs.get("environment_sanitization", True) self.allowed_environment_vars = kwargs.get( "allowed_environment_vars", [ "DEB_BUILD_OPTIONS", "DEB_BUILD_PROFILES", "CC", "CXX", "CFLAGS", "CXXFLAGS", "LDFLAGS", "MAKEFLAGS", "CCACHE_DIR", "CCACHE_HASHDIR", "http_proxy", "https_proxy", "no_proxy", "DISPLAY", "XAUTHORITY", ], ) # Advanced build options (Mock-inspired) self.run_tests = kwargs.get("run_tests", True) self.build_timeout = kwargs.get("build_timeout", 0) # 0 = no timeout self.force_architecture = kwargs.get("force_architecture", None) self.unique_extension = kwargs.get("unique_extension", None) self.config_dir = kwargs.get("config_dir", None) self.cleanup_after = kwargs.get("cleanup_after", True) # APT configuration self.apt_sources = kwargs.get("apt_sources", []) self.apt_preferences = kwargs.get("apt_preferences", []) self.apt_command = kwargs.get("apt_command", "apt-get") self.apt_install_command = kwargs.get("apt_install_command", "apt-get install -y") @classmethod def from_file(cls, config_path: str) -> "Config": """Load configuration from a YAML file""" try: with open(config_path, "r") as f: config_data = yaml.safe_load(f) return cls(**config_data) except FileNotFoundError: raise ConfigurationError(f"Configuration file not found: {config_path}") except yaml.YAMLError as e: raise ConfigurationError(f"Invalid YAML in configuration file: {e}") except Exception as e: raise ConfigurationError(f"Error loading configuration: {e}") @classmethod def default(cls) -> "Config": """Create default configuration""" return cls() def to_dict(self) -> Dict[str, Any]: """Convert configuration to dictionary""" return { "chroot_name": self.chroot_name, "architecture": self.architecture, "suite": self.suite, "output_dir": self.output_dir, "keep_chroot": self.keep_chroot, "verbose": self.verbose, "debug": self.debug, "chroot_dir": self.chroot_dir, "chroot_config_dir": self.chroot_config_dir, "sbuild_config": self.sbuild_config, "sbuild_log_dir": self.sbuild_log_dir, "build_deps": self.build_deps, "build_env": self.build_env, "build_options": self.build_options, "metadata_dir": self.metadata_dir, "capture_logs": self.capture_logs, "capture_changes": self.capture_changes, "use_root_cache": self.use_root_cache, "root_cache_dir": self.root_cache_dir, "root_cache_age": self.root_cache_age, "use_package_cache": self.use_package_cache, "package_cache_dir": self.package_cache_dir, "use_ccache": self.use_ccache, "ccache_dir": self.ccache_dir, "use_tmpfs": self.use_tmpfs, "tmpfs_size": self.tmpfs_size, "parallel_jobs": self.parallel_jobs, "parallel_compression": self.parallel_compression, "parallel_builds": self.parallel_builds, "parallel_chroot_prefix": self.parallel_chroot_prefix, "parallel_build_timeout": self.parallel_build_timeout, "parallel_build_cleanup": self.parallel_build_cleanup, "advanced_mounts": self.advanced_mounts, "bind_mounts": self.bind_mounts, "tmpfs_mounts": self.tmpfs_mounts, "overlay_mounts": self.overlay_mounts, "mount_options": self.mount_options, "mount_proc": self.mount_proc, "mount_sys": self.mount_sys, "mount_dev": self.mount_dev, "mount_devpts": self.mount_devpts, "mount_tmp": self.mount_tmp, "mount_home": self.mount_home, "use_namespaces": self.use_namespaces, "uid_mapping": self.uid_mapping, "gid_mapping": self.gid_mapping, "capabilities": self.capabilities, "seccomp_profile": self.seccomp_profile, "chroot_user": self.chroot_user, "chroot_group": self.chroot_group, "chroot_uid": self.chroot_uid, "chroot_gid": self.chroot_gid, "use_host_user": self.use_host_user, "copy_host_users": self.copy_host_users, "preserve_uid_gid": self.preserve_uid_gid, "plugins": self.plugins, "plugin_conf": self.plugin_conf, "plugin_dir": self.plugin_dir, "enable_performance_monitoring": self.enable_performance_monitoring, "performance_metrics_dir": self.performance_metrics_dir, "performance_retention_days": self.performance_retention_days, "performance_auto_optimization": self.performance_auto_optimization, "performance_benchmark_iterations": self.performance_benchmark_iterations, "performance_reporting": self.performance_reporting, "use_host_resolv": self.use_host_resolv, "http_proxy": self.http_proxy, "https_proxy": self.https_proxy, "no_proxy": self.no_proxy, "mirror": self.mirror, "security_mirror": self.security_mirror, "backports_mirror": self.backports_mirror, "isolation": self.isolation, "enable_network": self.enable_network, "selinux_enabled": self.selinux_enabled, "use_bootstrap_chroot": self.use_bootstrap_chroot, "bootstrap_chroot_name": self.bootstrap_chroot_name, "bootstrap_arch": self.bootstrap_arch, "bootstrap_suite": self.bootstrap_suite, "chroot_setup_cmd": self.chroot_setup_cmd, "chroot_additional_packages": self.chroot_additional_packages, "preserve_environment": self.preserve_environment, "environment_sanitization": self.environment_sanitization, "allowed_environment_vars": self.allowed_environment_vars, } def save(self, config_path: str) -> None: """Save configuration to a YAML file""" try: config_dir = Path(config_path).parent config_dir.mkdir(parents=True, exist_ok=True) with open(config_path, "w") as f: yaml.dump(self.to_dict(), f, default_flow_style=False) except Exception as e: raise ConfigurationError(f"Error saving configuration: {e}") def validate(self) -> None: """Validate configuration""" errors = [] # Check required directories if not os.path.exists(self.chroot_config_dir): errors.append(f"Chroot config directory does not exist: {self.chroot_config_dir}") if not os.path.exists(self.sbuild_config): errors.append(f"sbuild config file does not exist: {self.sbuild_config}") # Check architecture valid_architectures = ["amd64", "i386", "arm64", "armhf", "ppc64el", "s390x"] if self.architecture not in valid_architectures: errors.append(f"Invalid architecture: {self.architecture}") # Check suite valid_suites = [ "trixie", # Debian 13+ (trixie) - required for OSTree support "bookworm", "sid", "bullseye", "buster", "jammy", "noble", "focal", ] if self.suite not in valid_suites: errors.append(f"Invalid suite: {self.suite}") # Check isolation method valid_isolation = ["schroot", "simple", "nspawn"] if self.isolation not in valid_isolation: errors.append(f"Invalid isolation method: {self.isolation}") # Check parallel jobs if self.parallel_jobs < 1: errors.append("Parallel jobs must be at least 1") if errors: raise ConfigurationError("Configuration validation failed:\n" + "\n".join(errors)) def get_chroot_path(self) -> str: """Get the full path to the chroot directory""" return os.path.join(self.chroot_dir, self.chroot_name) def get_output_path(self) -> str: """Get the full path to the output directory""" return os.path.abspath(self.output_dir) def get_metadata_path(self) -> str: """Get the full path to the metadata directory""" return os.path.abspath(self.metadata_dir) def get_root_cache_path(self) -> str: """Get the full path to the root cache directory""" return os.path.join(self.root_cache_dir, self.chroot_name) def get_package_cache_path(self) -> str: """Get the full path to the package cache directory""" return os.path.join(self.package_cache_dir, self.chroot_name) def get_ccache_path(self) -> str: """Get the full path to the ccache directory""" return os.path.join(self.ccache_dir, self.chroot_name) def setup_build_environment(self) -> Dict[str, str]: """Setup build environment variables""" env = {} # Set parallel build options if self.parallel_jobs > 1: env["DEB_BUILD_OPTIONS"] = f"parallel={self.parallel_jobs},nocheck" env["MAKEFLAGS"] = f"-j{self.parallel_jobs}" # Set ccache if enabled if self.use_ccache: env["CCACHE_DIR"] = self.get_ccache_path() env["CCACHE_HASHDIR"] = "1" # Set proxy if configured if self.http_proxy: env["http_proxy"] = self.http_proxy if self.https_proxy: env["https_proxy"] = self.https_proxy if self.no_proxy: env["no_proxy"] = self.no_proxy # Merge with user-defined build environment env.update(self.build_env) return env