bubbles/python_modules/infra_modules/infra_general.py

711 lines
27 KiB
Python

"""
* SPDX-FileCopyrightText: 2023-2025 PeppermintOS Team
* (peppermintosteam@proton.me)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Set the infrastructure for bubbles to begin the ISO build
* This copies needed config files to the binary and chroot
* locations, based on the build base and architecture.
"""
import os
import collections
from pathlib import Path
import shutil
from python_modules.logger_modules import builder_logger as logger
from python_modules.paths_modules import (HOME_FOLDER, WPCHROOT,
BINARYPTH, BOOTSTRAP_FOLDER, FUSATOCONFIG, allowed_bases,
symlinks, copyfolderfiles, copyfilesspecified)
def copy_multimedia_files(sbase, sarch):
"""
Copy the multimedia to the ARCHIVES folder depending on
the architecture. It will copy folders as needed.
Args:
sbase (str): Base architecture could be ("deb" or "dev")
sarch (str): Architecture specifics could be ("32" or "64")
"""
logger.info("Copy multimedia Archive")
src_archive = f'/multimedia/{sbase}{sarch}'
des_archive = '/archives'
source_path = HOME_FOLDER + src_archive
dest_path = HOME_FOLDER + '/' + FUSATOCONFIG + des_archive
logger.info("Source path: %s", source_path)
logger.info("Destination path: %s", dest_path)
try:
shutil.copytree(source_path, dest_path, dirs_exist_ok=True)
logger.info("Successfully copied files from %s to %s",
source_path, dest_path)
except Exception as _:
logger.error("Error copying files: %s", str(_), exc_info=True)
raise
exit()
logger.info("Archive copy completed")
def copy_binary_folders(sbase, sarch):
"""
Copy all the needed folders to BINARY depending on the architecture.
"""
logger.info("Starting binary folder copy process")
folder_pairs = [
(f'splash/{sbase}{sarch}/boot', 'boot'),
(f'splash/{sbase}{sarch}/isolinux', 'isolinux'),
(f'splash/{sbase}_live-theme', 'boot/grub'),
(f'splash/{sbase}_splash', 'isolinux'),
(f'splash/{sbase}_splash', 'boot/grub')
]
for src, dest in folder_pairs:
source_path = os.path.join(HOME_FOLDER, src)
dest_path = os.path.join(HOME_FOLDER, BINARYPTH, dest)
logger.info("Copying from %s to %s", source_path, dest_path)
try:
shutil.copytree(source_path, dest_path, dirs_exist_ok=True)
logger.info("Successfully copied to %s", dest_path)
except(ValueError, TypeError) as _:
logger.error("Error copying to %s: %s", dest_path, str(_))
exit()
logger.info("Binary folder copy process completed")
def copy_fusato_configs(sbase, sarch):
"""
Copy all the needed files to the hooks folders depending on
the architecture.
"""
logger.info("Starting Fusato config copy process")
source = f'/hooks/normal/{sbase}{sarch}'
destination = '/hooks/normal/'
source_path = HOME_FOLDER + source + '/'
dest_path = (
Path(HOME_FOLDER) / Path(FUSATOCONFIG) / destination.lstrip('/'))
logger.info("Copying from %s to %s", source_path, dest_path)
try:
shutil.copytree(source_path, dest_path, dirs_exist_ok=True)
logger.info("Successfully copied Fusato configs to %s",
dest_path)
except (ValueError, TypeError) as _:
logger.error("Error copying Fusato configs to %s: %s",
dest_path, str(_))
exit()
logger.info("Fusato config copy process completed")
def copy_server_config_files(base):
"""
Copies server initial config files to CHROOT depending on the base.
"""
if base not in allowed_bases:
logger.warning("Base '%s' is not allowed. Skipping.", base)
return
logger.info("Copy Server Files for base: %s", base)
server_path = f'/server/scripts/{base}/'
file_paths = [
'welcome.sh',
'configure_apache2.sh',
'configure_firewalld.sh',
'configure_hostname.sh',
'configure_mariadb.sh',
'configure_nginx.sh',
'configure_php_and_docker.sh',
'configure_postfix.sh',
'configure_postgresql.sh',
'configure_sqlite.sh',
'configure_ssh.sh',
'configure_static_ip.sh',
'create_user.sh',
'update_and_install.sh'
]
destination = '/usr/local/bin'
full_dest_path = (
Path(HOME_FOLDER) / Path(WPCHROOT) / destination.lstrip('/'))
full_dest_path.mkdir(parents=True, exist_ok=True)
logger.info("Ensured directory exists: %s", full_dest_path)
for file in file_paths:
src_path = Path(HOME_FOLDER) / server_path.lstrip('/') / file
dest_path = full_dest_path / file
logger.info("Copying %s to %s", src_path, dest_path)
try:
if src_path.exists():
if src_path.is_dir():
shutil.copytree(src_path, dest_path,
dirs_exist_ok=True)
else:
shutil.copy2(src_path, dest_path)
logger.info("Successfully copied to %s", dest_path)
else:
logger.error("Source path does not exist: %s", src_path)
except (ValueError, TypeError) as _:
logger.error(
"Error copying %s to %s: %s", src_path, dest_path, _)
exit()
logger.info(
"Completed copying server config files for base: %s", base)
def copy_architecture_files(sbase, sarch, force_copy=False):
"""
Copy all the needed files and folders to CHROOT. Depending on the
architecture, it will copy files and folders as needed.
"""
logger.debug("Starting copy process for architecture-specific files")
sources_path = '/sources/'
file_pairs = [
(f'{sources_path}{sbase}{sarch}/sources.list',
f'/opt/pepconf/sources.list'),
('/id_files/pep_id', '/usr/share/peppermint/pep_id'),
(f'/osrelease/{sbase}{sarch}/os-release', '/usr/lib/os-release'),
(f'/osrelease/{sbase}{sarch}/os-release', '/opt/pepconf/os-release'),
(f'/grub/{sbase}{sarch}/grub', '/etc/default/grub'),
(f'/grub/{sbase}_themes', '/boot/grub/themes')
]
for src, dest in file_pairs:
src_path = Path(HOME_FOLDER) / src.lstrip('/')
dest_path = (
Path(HOME_FOLDER) / Path(WPCHROOT) / dest.lstrip('/'))
if dest_path.exists() and not force_copy:
logger.info(
"Skipping %s as it already exists at %s",
src_path, dest_path)
continue
logger.info("Copying %s to %s", src_path, dest_path)
try:
if src_path.is_dir():
shutil.copytree(src_path, dest_path, symlinks=True,
ignore_dangling_symlinks=True, dirs_exist_ok=True)
else:
dest_path.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(src_path, dest_path)
except (ValueError, TypeError) as _:
logger.error("Error copying %s to %s: %s", src_path,
dest_path, _)
exit()
else:
logger.info("Successfully copied %s to %s", src_path,
dest_path)
logger.debug("Architecture-specific file copy process completed")
def get_id_build_type():
"""
This will get the type of build that is taking place and copy
relevant files.
"""
logger.debug("Starting the build type identification process")
source_folder = os.path.expanduser(f'{HOME_FOLDER}/build_type')
dest_folder = os.path.join(os.path.expanduser('~'), 'bubbles',
'iso_configs', 'fusato', 'config', 'includes.chroot', 'opt',
'pepconf'
)
logger.info("Source folder: %s", source_folder)
logger.info("Destination folder: %s", dest_folder)
try:
os.makedirs(dest_folder, exist_ok=True)
logger.info("Ensured destination folder exists: %s",
dest_folder)
except (ValueError, TypeError) as _:
logger.error("Failed to create destination folder: %s", str(_))
exit()
return
files_copied = 0
for filename in os.listdir(source_folder):
src_file = os.path.join(source_folder, filename)
dest_file = os.path.join(dest_folder, filename)
if os.path.isfile(src_file):
try:
shutil.copy(src_file, dest_file)
logger.info('Successfully copied: %s to %s', src_file,
dest_file)
files_copied += 1
except (ValueError, TypeError) as _:
logger.error("Failed to copy %s to %s: %s", src_file,
dest_file, str(_))
exit()
else:
logger.warning("Skipped non-file item: %s", src_file)
logger.info("Build type identification process completed. %d files "
"copied.", files_copied)
def ignore_missing_files(src, names):
"""
This will keep the build going even if files are missing from from
the confiigs
"""
logger.info("Checking for missing files in directory: %s", src)
missing_files = []
for name in names:
full_path = os.path.join(src, name)
if not os.path.exists(full_path):
missing_files.append(name)
logger.warning("File not found: %s", full_path)
if missing_files:
logger.info("Total missing files: %d", len(missing_files))
else:
logger.info("No missing files found in %s", src)
return missing_files
def add_web_profile():
"""
This will move the web profile depending on the build type.
"""
logger.debug("Starting to copy Web Profiles")
path_to_bldtype_file = os.path.expanduser('~/start/bldtype')
lw_src_path = '/browser_profiles/lw/'
ff_src_path = '/browser_profiles/ff/'
des_src_path = '/etc/skel/.local/share/pmostools'
build_id_list = [
os.path.join(path_to_bldtype_file, f"{base}.{arch}{desktop}")
for base in ['deb', 'dev']
for arch, desktop in [('64', 'xfc'), ('arm', 'xfc'),
('64', 'gfb'), ('64', 'opb')
]
]
profile_copied = False
for build_id in build_id_list:
if os.path.exists(build_id):
logger.info(
"Found build id file: %s. Copying lw profile.",
build_id)
try:
shutil.copytree(
os.path.join(HOME_FOLDER, lw_src_path.lstrip('/')),
os.path.join(HOME_FOLDER, WPCHROOT.lstrip('/'),
des_src_path.lstrip('/')),
dirs_exist_ok=True,
ignore=ignore_missing_files
)
logger.info("Successfully copied lw profile")
profile_copied = True
break
except (ValueError, TypeError) as _:
logger.error("Error copying lw profile: %s", str(_))
exit()
if not profile_copied:
logger.info("No matching build file found. Copying ff profile.")
try:
shutil.copytree(
os.path.join(HOME_FOLDER, ff_src_path.lstrip('/')),
os.path.join(
HOME_FOLDER, WPCHROOT.lstrip('/'),
des_src_path.lstrip('/')),
dirs_exist_ok=True,
ignore=ignore_missing_files
)
logger.info("Successfully copied ff profile")
except (ValueError, TypeError) as _:
logger.error("Error copying ff profile: %s", str(_))
exit()
logger.debug("Web profile copying process completed")
def set_symlinks():
"""
Set the symlinks that are used for all builds.
"""
logger.info("Starting to set symlinks for all builds")
for target_path, source in symlinks.items():
full_target_path = os.path.join(HOME_FOLDER,
WPCHROOT.lstrip('/'), target_path.lstrip('/'))
logger.info("Creating symlink: %s -> %s", full_target_path,
source)
try:
if os.path.islink(full_target_path):
os.unlink(full_target_path)
logger.info("Removed existing symlink: %s",
full_target_path)
os.makedirs(os.path.dirname(full_target_path),
exist_ok=True)
os.symlink(source, full_target_path)
logger.info("Successfully created symlink: %s -> %s",
full_target_path, source)
except FileExistsError:
logger.warning(
"Symlink already exists and is not a symbolic link: %s",
full_target_path)
exit()
except PermissionError:
logger.error(
"Permission denied when creating symlink: %s",
full_target_path)
exit()
except (ValueError, TypeError) as _:
logger.error("Error creating symlink %s -> %s: %s",
full_target_path, source, str(_))
exit()
logger.info("Finished setting symlinks for all builds")
def icons_themes():
"""
Copy icons and themes for all builds using paths defined in
copyfolderfiles.
"""
logger.debug("Starting to copy icons and themes")
copy_paths = [
("SRC_DEBIAN_DARK", "DES_THEMES"),
("SRC_MANJARO_DARK", "DES_THEMES"),
("SRC_PEPPERMINT_DARK", "DES_THEMES"),
("SRC_DEBIAN", "DES_THEMES"),
("SRC_MANJARO", "DES_THEMES"),
("SRC_PEPPERMINT", "DES_THEMES"),
("SRC_TELA_BLUE_DARK", "DES_ICONS"),
("SRC_TELA_GREEN_DARK", "DES_ICONS"),
("SRC_TELA_RED_DARK", "DES_ICONS"),
("SRC_TELA_BLUE_BASE", "DES_ICONS"),
("SRC_TELA_GREEN_BASE", "DES_ICONS"),
("SRC_TELA_RED_BASE", "DES_ICONS"),
("SRC_TELA", "DES_ICONS"),
]
for src_key, dest_key in copy_paths:
if src_key in copyfolderfiles and dest_key in copyfolderfiles:
src = copyfolderfiles[src_key].strip()
dest = copyfolderfiles[dest_key].strip()
source_path = Path(HOME_FOLDER) / src.lstrip('/')
dest_path = Path(HOME_FOLDER) / WPCHROOT / dest.lstrip('/')
folder_name = Path(src).name
full_dest_path = dest_path / folder_name
logger.info("Copying from %s to %s", source_path,
full_dest_path)
try:
shutil.copytree(source_path, full_dest_path,
dirs_exist_ok=True)
logger.info("Successfully copied %s to %s",
src_key, dest_key)
except (ValueError, TypeError) as _:
logger.error("Error copying %s to %s: %s", src_key,
dest_key, str(_))
exit()
else:
logger.warning(
"Skipping copy for %s to %s: Keys not found in"
" copyfolderfiles", src_key, dest_key)
logger.debug("Finished copying icons and themes")
def shared_files():
"""
Copy all specific files that are used for all builds.
"""
logger.debug("Starting to copy shared files")
copy_pairs = [
("SRC_ALIAS", "DES_ALIAS"),
("SRC_XDAILY", "DES_XDAILY"),
("SRC_PEPREPO", "DES_PEPREPO"),
("SRC_GPG_BINARY", "DES_GPG_BINARY"),
("SRC_GPG_CHROOT", "DES_GPG_CHROOY"),
("SRC_XDAILYGUI", "DES_XDAILYGUI"),
("SRC_SUGGESTED", "DES_SUGGESTED"),
("SRC_PFETCH", "DES_PFETCH"),
("SRC_WELCOME", "DES_WELCOME"),
("SRC_KUMO", "DES_KUMO"),
("SRC_LIGHTDM", "DES_LIGHTDM"),
("SRC_LIGHTDM_GREETER", "DES_LIGHTDM_GREETER"),
("SRC_PLY", "DES_PLY"),
]
for src_key, des_key in copy_pairs:
if src_key in copyfilesspecified and des_key in copyfilesspecified:
source = copyfilesspecified[src_key].strip()
destination = copyfilesspecified[des_key].strip()
source_path = Path(HOME_FOLDER) / source.lstrip('/')
dest_path = (
Path(HOME_FOLDER) / WPCHROOT / destination.lstrip('/')
)
logger.info(
"Attempting to copy: %s to %s", source_path, dest_path)
try:
dest_path.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(source_path, dest_path)
logger.info("Successfully copied: %s to %s",
source_path, dest_path)
if des_key in ["DES_GPG_BINARY", "DES_GPG_CHROOT"]:
os.chmod(dest_path, 0o644)
logger.info("Set permissions to 644 for %s", dest_path)
except FileNotFoundError:
logger.error("Source file not found: %s", source_path)
exit()
except PermissionError:
logger.error("Permission denied when copying to: %s",
dest_path)
exit()
except (ValueError, TypeError) as _:
logger.error("Error copying %s to %s: %s", source_path,
dest_path, str(_))
exit()
else:
logger.warning(
"Skipping copy for %s to %s: Keys not found in"
" copyfilesspecified", src_key, des_key)
logger.info("Finished copying shared files")
def setup_hooks_minis():
"""
Copy all hooks files that are used for mini builds.
"""
logger.debug("Starting to copy hooks files")
copy_pairs = [
("SRC_HOOKS_MINI_NORMAL", "DES_HOOKS_MINI_NORMAL")
]
for src_key, des_key in copy_pairs:
if src_key in copyfilesspecified and des_key in copyfilesspecified:
source = copyfilesspecified[src_key].strip()
destination = copyfilesspecified[des_key].strip()
source_path = Path(HOME_FOLDER) / source.lstrip('/')
dest_path = (
Path(HOME_FOLDER) / destination.lstrip('/')
)
logger.info(
"Attempting to copy: %s to %s", source_path, dest_path)
try:
dest_path.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(source_path, dest_path)
logger.info("Successfully copied: %s to %s",
source_path, dest_path)
except FileNotFoundError:
logger.error("Source file not found: %s", source_path)
exit()
except PermissionError:
logger.error("Permission denied when copying to: %s",
dest_path)
exit()
except (ValueError, TypeError) as _:
logger.error("Error copying %s to %s: %s", source_path,
dest_path, str(_))
exit()
else:
logger.warning(
"Skipping copy for %s to %s: Keys not found in"
" copyfilesspecified", src_key, des_key)
logger.info("Finished copying Hook files")
def setup_hooks():
"""
Copy all hooks files that are used for all builds minus the mini.
"""
logger.debug("Starting to copy hooks files")
copy_pairs = [
("SRC_HOOKS_LIVE", "DES_HOOKS_LIVE"),
("SRC_HOOKS_NORMAL", "DES_HOOKS_NORMAL")
]
for src_key, des_key in copy_pairs:
if src_key in copyfilesspecified and des_key in copyfilesspecified:
source = copyfilesspecified[src_key].strip()
destination = copyfilesspecified[des_key].strip()
source_path = Path(HOME_FOLDER) / source.lstrip('/')
dest_path = (
Path(HOME_FOLDER) / destination.lstrip('/')
)
logger.info(
"Attempting to copy: %s to %s", source_path, dest_path)
try:
dest_path.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(source_path, dest_path)
logger.info("Successfully copied: %s to %s",
source_path, dest_path)
except FileNotFoundError:
logger.error("Source file not found: %s", source_path)
exit()
except PermissionError:
logger.error("Permission denied when copying to: %s",
dest_path)
exit()
except (ValueError, TypeError) as _:
logger.error("Error copying %s to %s: %s", source_path,
dest_path, str(_))
exit()
else:
logger.warning(
"Skipping copy for %s to %s: Keys not found in"
" copyfilesspecified", src_key, des_key)
logger.info("Finished copying Hook files")
def shared_server_files():
"""
This will copy all specific files that are used for the server builds.
"""
logger.info("Starting to copy shared server files")
src_paths = ('/server/firewall/public.xml',
)
des_paths = ('/etc/firewalld/zones',
)
src_q = collections.deque(src_paths)
des_q = collections.deque(des_paths)
size_q = len(src_q)
logger.info("Preparing to copy %d files", size_q)
for size_length in range(size_q):
source = src_q.popleft()
des = des_q.popleft()
full_source_path = HOME_FOLDER + source
full_dest_path = HOME_FOLDER + WPCHROOT + des
logger.info("Copying file %d of %d", size_length + 1, size_q)
logger.info("Source: %s", full_source_path)
logger.info("Destination: %s", full_dest_path)
try:
shutil.copy(full_source_path, full_dest_path)
logger.info(
"Successfully copied file to %s", full_dest_path)
except FileNotFoundError:
logger.error(
"Source file not found: %s", full_source_path)
exit()
except PermissionError:
logger.error("Permission denied when copying to: %s",
full_dest_path)
exit()
except IOError as _:
logger.error(
"I/O error occurred while copying %s to %s: %s",
full_source_path, full_dest_path, str(_))
exit()
logger.info("Completed copying shared server files")
def boostrap_shared():
"""
Copy specific folders in the bootstrap location
"""
logger.debug("Starting to copy shared bootstrap files")
src_paths = ('issue',)
des_paths = ('etc',)
src_q = collections.deque(src_paths)
des_q = collections.deque(des_paths)
size_q = len(src_q)
logger.info("Preparing to copy %d directories", size_q)
for size_length in range(size_q):
source = src_q.popleft()
des = des_q.popleft()
full_source_path = os.path.join(HOME_FOLDER, source)
full_dest_path = os.path.join(HOME_FOLDER, BOOTSTRAP_FOLDER, des)
logger.info("Copying directory %d of %d", size_length + 1,
size_q)
logger.info("Source: %s", full_source_path)
logger.info("Destination: %s", full_dest_path)
try:
shutil.copytree(
full_source_path, full_dest_path, dirs_exist_ok=True)
logger.info("Successfully copied directory to %s",
full_dest_path)
except FileNotFoundError:
logger.error("Source directory not found: %s",
full_source_path)
exit()
except PermissionError:
logger.error("Permission denied when copying to: %s",
full_dest_path)
exit()
except shutil.Error as _:
logger.error("Error occurred while copying %s to %s: %s",
full_source_path, full_dest_path, str(_))
exit()
logger.debug("Completed copying shared bootstrap files")
def shared_folders():
"""
Copy shared folders that are common amongst all the builds.
"""
logger.debug("Starting to copy shared folders")
copy_pairs = [
("SRC_PLY_LINES", "DES_PLY_LINES"),
("SRC_APPS", "DES_APPS"),
("SRC_FONT", "DES_FONT"),
("SRC_ISSUE", "DES_ISSUE"),
("SRC_BUILD_TYPE", "DES_BUILD_TYPE"),
("SRC_ISSUE", "DES_ISSUES_OPT"),
("SRC_POLKIT", "DES_POLKIT"),
("SRC_USER_CONFIGS", "DES_USER_CONFIGS"),
("SRC_PIXMAPS", "DES_PIXMAPS"),
("SRC_WALLPAPER", "DES_WALLPAPER"),
("SRC_MENU", "DES_MENU"),
("SRC_FACE", "DES_FACE"),
("SRC_PMOSTOOLS", "DES_PMOSTOOLS"),
("SRC_AUTO_START", "DES_AUTO_START"),
("SRC_PYLIBS", "DES_PYLIBS"),
]
for src_key, des_key in copy_pairs:
if src_key in copyfolderfiles and des_key in copyfolderfiles:
source = copyfolderfiles[src_key].strip()
destination = copyfolderfiles[des_key].strip()
source_path = Path(HOME_FOLDER) / source.lstrip('/')
dest_path = (
Path(HOME_FOLDER) / WPCHROOT / destination.lstrip('/')
)
logger.info("Attempting to copy folder: %s to %s",
source_path, dest_path)
try:
shutil.copytree(source_path, dest_path,
dirs_exist_ok=True)
logger.info("Successfully copied folder: %s to %s",
source_path, dest_path)
except FileNotFoundError:
logger.error("Source folder not found: %s",
source_path)
exit()
except PermissionError:
logger.error(
"Permission denied when copying to: %s",
dest_path)
exit()
except (ValueError, TypeError) as _:
logger.error("Error copying folder %s to %s: %s",
source_path, dest_path, str(_))
exit()
else:
logger.warning(
"Skipping copy for %s to %s: Keys not found in "
"copyfolderfiles", src_key, des_key)
logger.info("Finished copying shared folders")
get_id_build_type()
def loaded_folders():
"""
This function will get the files that are used by the loaded builds.
"""
logger.debug("Starting to copy loaded folders")
src_paths = ('/loaded/application',
'/loaded/wallpaper')
des_paths = ('/usr/share/applications',
'/usr/share/backgrounds')
src_q = collections.deque(src_paths)
des_q = collections.deque(des_paths)
size_q = len(des_q)
logger.info("Preparing to copy %d directories", size_q)
for size_length in range(size_q):
source = src_q.popleft()
des = des_q.popleft()
full_source_path = HOME_FOLDER + source
full_dest_path = HOME_FOLDER + '/' + WPCHROOT + des
logger.info("Copying directory %d of %d", size_length + 1,
size_q)
logger.info("Source: %s", full_source_path)
logger.info("Destination: %s", full_dest_path)
try:
shutil.copytree(full_source_path, full_dest_path,
dirs_exist_ok=True)
logger.info(
"Successfully copied directory from %s to %s",
full_source_path, full_dest_path)
except FileNotFoundError:
logger.error("Source directory not found: %s",
full_source_path)
exit()
except PermissionError:
logger.error("Permission denied when copying to: %s",
full_dest_path)
exit()
except shutil.Error as _:
logger.error("Error occurred while copying %s to %s: %s",
full_source_path, full_dest_path, str(_))
exit()
logger.debug("Completed copying loaded folders")