# Assuming this is in builder/core/install_desktop.py # -*- coding: utf-8 -*- """ SPDX-FileCopyrightText: 2023-2025 PeppermintOS Team (peppermintosteam@proton.me) SPDX-License-Identifier: GPL-3.0-or-later This module provides a function to install a specified desktop environment and enable associated services, using the host-execution strategy and handling architecture-specific package lists and services defined in YAML configuration files. Credits: - PeppermintOS Team (peppermintosteam@proton.me) - Development and maintenance of the project. License: This code is distributed under the GNU General Public License version 3 or later (GPL-3.0-or-later). For more details, please refer to the LICENSE file included in the project or visit: https://www.gnu.org/licenses/gpl-3.0.html """ import os import sys import logging import yaml # Keep if load_yaml_config needs it or for other reasons # Adjust BASE_DIR calculation as needed based on where this file is relative to the project root # Assuming install_desktop.py is in builder/core/, and project root is parent of builder/ BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) sys.path.insert(0, BASE_DIR) try: # Import modules used by this function from builder.core.command_runner import run_command # xbps_commands is likely not needed here as construct_xbps_install_args handles it internally # from builder.core.xbps_commands import xbps_commands # enable_services_in_chroot implementation might need checking/adaptation later from builder.core.enable_services import enable_services_in_chroot # paths is passed as argument, so not needed as direct import here # from builder.core.bootstrap.paths import paths from builder.core.config_loader import load_yaml_config # Needed to load desktop YAML from builder.configs import logger_config from builder.core import bootstrap # Import bootstrap module for construct_xbps_install_args # Module-level logger setup logger = logger_config.setup_logger('install_desktop') except ImportError as e: # Handle import error for this module try: basic_logger = logging.getLogger(__name__) logging.basicConfig(level=logging.ERROR) basic_logger.error(f"Error importing necessary modules for install_desktop: {e}. Ensure your environment is set up correctly.") except Exception: print(f"Error importing necessary modules for install_desktop: {e}. Ensure your environment is set up correctly.") sys.exit(1) # MODIFIED FUNCTION SIGNATURE to accept arguments passed from iso_builder_main # Matching the order and names from the intended call def install_desktop_environment(arch, desktop_environment_name, desktops_config, target_env='rootfs', paths=None, host_arch=None, repositories_data=None): """ Installs the specified desktop environment and enables associated services in the chroot system, using the desktop configuration from the YAML, employing the host-execution strategy. Args: arch (str): Architecture (e.g., 'aarch64'). desktop_environment_name (str): Name of the desktop environment (e.g., 'xfce'). desktops_config (dict): Desktop configuration loaded from the YAML (desktops_config.yaml file).  This contains info about the desktop, including the path to its specific YAML. target_env (str): Target environment to install the desktop ('rootfs'). Defaults to 'rootfs'. paths (dict): Dictionary of build paths. Required to get target path and cache dir. host_arch (str): The architecture of the host system. repositories_data (list): List of repository dictionaries from YAML config. Expected structure: [{'name': '...', 'uri': '...', 'architectures': [...]}, ...] Raises: subprocess.CalledProcessError: If the xbps-install command fails. ValueError: If configuration data is missing or incorrect. KeyError: If required paths are not found. """ # Remove the logger = logging.getLogger(__name__) line here, use the module-level logger logger.info(f"=> Starting installation of desktop environment: {desktop_environment_name} in environment: {target_env} ({arch})...") # Ensure paths dict is available if paths is None or not isinstance(paths, dict): logger.error("Paths dictionary is missing or incorrect.") raise ValueError("Paths dictionary is required.") if target_env == 'rootfs': target_path = paths.get('ROOTFS') else: # Desktop environments are typically only installed in rootfs in this build process logger.error(f"Invalid target environment for desktop installation: {target_env}. Installation is only supported in ROOTFS.") raise ValueError(f"Invalid target environment for desktop installation: {target_env}") if not target_path: logger.error(f"Path for environment '{target_env}' not found in paths dictionary.") raise KeyError(f"Path for environment '{target_env}' not found.") desktop_yaml_file = None total_desktop_packages_list = [] services_to_enable_list = [] # Services are still handled separately # Get the specific desktop YAML file path from the main desktops_config if desktops_config and 'desktops' in desktops_config and desktop_environment_name.lower() in desktops_config['desktops']: desktop_info = desktops_config['desktops'][desktop_environment_name.lower()] # package_group is not used in this YAML-driven approach # desktop_packages_group = desktop_info.get('package_group') desktop_yaml_file = desktop_info.get('yaml_file') # This is the path like 'builder/configs/desktops/xfce.yaml' else: logger.error(f"Configuration for desktop environment '{desktop_environment_name}' not found in desktops_config.yaml.") raise ValueError(f"Configuration for desktop environment '{desktop_environment_name}' not found.") if desktop_yaml_file: logger.info(f"=> Loading desktop configuration from YAML file: {desktop_yaml_file}") # Need to construct the full path if desktop_yaml_file is relative desktop_yaml_full_path = os.path.join(BASE_DIR, desktop_yaml_file) # Assumes BASE_DIR is project root try: # Use load_yaml_config helper desktop_packages_config = load_yaml_config(desktop_yaml_full_path, os.path.basename(desktop_yaml_full_path)) except Exception as e: logger.error(f"Error loading desktop packages YAML '{desktop_yaml_full_path}': {e}") raise # Re-raise the exception if desktop_packages_config: logger.debug(f"=> Desktop YAML file loaded successfully from: {desktop_yaml_full_path}") # Expected structure: {: {desktop_packages: [...], login_manager_packages: [...], ...}} # Get the configuration specifically for the target architecture desktop_config_for_arch = desktop_packages_config.get(arch, {}) # Use 'arch' parameter if desktop_config_for_arch: logger.debug(f"=> Configuration found for architecture: {arch}") # Get package lists for this architecture desktop_packages = desktop_config_for_arch.get('desktop_packages', []) login_manager_packages = desktop_config_for_arch.get('login_manager_packages', []) default_packages = desktop_config_for_arch.get('default_packages', []) # Assuming this key exists # Combine all package lists if isinstance(desktop_packages, list): total_desktop_packages_list.extend(desktop_packages) else: logger.warning(f"'desktop_packages' for {arch} in {desktop_yaml_full_path} is not a list. Ignoring.") if isinstance(login_manager_packages, list): total_desktop_packages_list.extend(login_manager_packages) else: logger.warning(f"'login_manager_packages' for {arch} in {desktop_yaml_full_path} is not a list. Ignoring.") if isinstance(default_packages, list): total_desktop_packages_list.extend(default_packages) else: logger.warning(f"'default_packages' for {arch} in {desktop_yaml_full_path} is not a list. Ignoring.") # Get the services list for this architecture services_to_enable_list = desktop_config_for_arch.get('services_enable', []) if not isinstance(services_to_enable_list, list): logger.warning(f"'services_enable' for {arch} in {desktop_yaml_full_path} is not a list. Ignoring.") services_to_enable_list = [] else: logger.warning(f"No configuration found for architecture {arch} in {desktop_yaml_full_path}. No desktop packages or services loaded.") else: logger.error(f"=> Failed to load desktop YAML configuration from: {desktop_yaml_full_path}") # Decide if this should be a fatal error or if a package group fallback is possible # Currently, if YAML fails or has no arch config, total_desktop_packages_list will be empty # --- Install Desktop Packages --- if total_desktop_packages_list: logger.info(f"=> Desktop package list for {arch} extracted successfully. Building XBPS_INSTALL_CMD command...") # Construct the xbps-install command arguments using the helper from bootstrap module # construct_xbps_install_args expects the package list in the structure {'env_name': {'arch': [package_list]}} # We are installing into the "rootfs" environment here. adapted_package_structure = {"rootfs": {arch: total_desktop_packages_list}} # construct_xbps_install_args handles repository filtering, -r, -c args internally # It needs paths, target_architecture, repositories_data, and the adapted package structure desktop_packages_args = bootstrap.construct_xbps_install_args( "rootfs", # Environment name (determines target path like paths['ROOTFS']) paths, # Paths dictionary (needed by construct_xbps_install_args for rootfs path and cache dir) arch, # Target architecture (use the 'arch' parameter passed to this function) repositories_data, # All repository data (passed to this function) adapted_package_structure # Pass desktop packages in the structure expected by construct_xbps_install_args ) if not desktop_packages_args: logger.warning("Skipping desktop packages installation due to failure in constructing xbps-install arguments.") # The error was likely logged within construct_xbps_install_args else: # Execute the xbps-install command directly on the host, targeting the rootfs # Prepend the xbps-install binary path on the host desktop_install_command = ["/usr/bin/xbps-install"] + desktop_packages_args logger.info(f"Executing desktop packages installation command (host): {' '.join(desktop_install_command)}") try: run_command(desktop_install_command) # Use the existing run_command helper logger.info(f"=> Installation of desktop packages completed successfully in environment: {target_env}.") except Exception as e: # Catching general Exception, consider more specific CalledProcessError logger.error(f"Error executing XBPS_INSTALL_CMD for desktop packages in environment: {target_env}: {e}") raise # Re-raise the exception to stop the build # --- Enable Services --- # The enable_services_in_chroot function might need to be adapted if it uses chroot execution internally. # For the host-execution strategy, it should either manipulate files directly or use a helper # that runs commands in the target context without a full chroot wrapper if possible, # or use the run_command_in_chroot if that's still deemed necessary for this specific step. # Assuming enable_services_in_chroot receives the target path and service list and handles execution internally. if services_to_enable_list: logger.info("=> Services to enable found in the desktop YAML configuration.") try: # enable_services_in_chroot needs the target_path (rootfs_path) enable_services_in_chroot(target_path, services_to_enable_list) # target_path is derived from paths logger.info(f"=> Services specified in the desktop YAML enabled successfully in environment: {target_env}.") except Exception as e: logger.error(f"Error enabling services specified in the desktop YAML in environment: {target_env}: {e}") raise else: logger.warning("=> No service list to enable found in the desktop YAML configuration. Skipping service activation.") logger.info(f"=> Installation and configuration of desktop environment: {desktop_environment_name} ({target_env}) completed.") logger.info(f"--------------------------------------------------------------------\n")