# # Erminig - Librairie Pakfile # Copyright (C) 2025 L0m1g # Sous licence DOUARN - Voir le fichier LICENCE pour les détails # # Ce fichier fait partie du projet Erminig. # Libre comme l’air, stable comme un menhir, et salé comme le beurre. # import os import toml from .db import get_db_connection class Pakfile: """ Représente un fichier de description de paquet (pakfile.toml) dans le système de gestion de paquets Erminig. Cette classe offre les opérations essentielles pour manipuler, vérifier et modifier les pakfiles. """ BASE_PATH = "/var/govel" def __init__(self, package: str): """ Initialise la gestion du pakfile pour un paquet donné. :param package: Nom du paquet """ self.package = package self.data = {} def _load(self): """ Charge le contenu du pakfile en mémoire Appelé automatiquement à l'initialisation si le fichier existe """ package_dir, pakfile_path = self._get_paths(package) if not os.path.exists(pakfile_path): raise FileNotFoundError(f"Pakfile introuvable pour {self.package} dans {pakfile_path}") with open(pakfile_path, "r", encoding="utf-8") as f: self.data = toml.load(f) def _get_paths(self, package: str) -> tuple[str, str]: """ Retourne le chemin du dossier du paquet et du pakfile associé. """ package_dir = f"{self.BASE_PATH}/{package}" pakfile_path = f"{package_dir}/pakfile.toml" return package_dir, pakfile_path def new(self, package: str): """ Crée un nouveau pakfile avec un squelette de base prêt à être édité. Soulève une erreur si le pakfile existe déjà. """ package_dir, pakfile_path = self._get_paths(package) if os.path.exists(pakfile_path): raise FileExistsError(f"Le pakfile existe déjà pour {package}") data = { "header": { "copyright": "Copyright (C) 2025 L0m1g", "license": "Sous licence DOUARN - Voir le fichier LICENCE pour les détails", "author": "L0m1g", "maintainer": "L0m1g", "description": "Description à compléter" }, "name": package, "ver": "", "rev": 1, "src": [], "build": "# Ajouter les commandes de construction ici", "check": "# Ajouter les commandes check ou équivalentes ici", "install": "# Ajouter les commandes d’installation ici" } os.makedirs(package_dir, exist_ok=True) with open(pakfile_path, "w", encoding="utf-8") as f: toml.dump(data, f) self.update_db() def set(self, key: str, value): """ Définit une valeur dans le pakfile et sauvegarde immédiatement """ self.data[key] = value self._save() self.update_db() def _save(self): """ Ecrit le contenu actuel de self.data dans le pakfile correspondant Créé le répertoire s'il n'existe pas. """ package_dir, pakfile_path = self._get_paths(self.package) os.makedirs(package_dir, exist_ok=True) with open(pakfile_path, "w", encoding="utf-8") as f: toml.dump(self.data, f) def get(self, key: str): """ Récupère la valeur d'une clé dans le pakfile Redirige vers de getters spécifiques :param key: Clé à récupérer :return: Valeur associée ou None """ match key: case "name" | "ver" | "rev": return self._get(key) case "src" | "deps" | "bdeps": return self._get_list(key) case _: return None def _get(self, key: str) -> str | None : """ Getter générique pour les chaines """ return self.data.get(key) def _get_list(self, key: str) -> list[str]: """ Getter spécifique aux listes """ return self.data.get(key,[]) def delete(self, package: str) -> bool: """ Supprime un paquet non installé et son répertoire associé. :param package: Nom du paquet à supprimer :return: True si la suppression a réussi, False sinon """ package_dir, pakfile_path = self._get_paths(package) if not os.path.exists(package_dir): print(f"Le paquet '{package}' n'existe pas. Rien à supprimer.") return False try: # On vire tout le répertoire et son contenu for root, dirs, files in os.walk(package_dir, topdown=False): for file in files: os.remove(os.path.join(root, file)) for dir in dirs: os.rmdir(os.path.join(root, dir)) os.rmdir(package_dir) print(f"Paquet '{package}' supprimé avec succès.") return True except Exception as e: print(f"Erreur lors de la suppression de '{package}': {e}") return False self.update_db() def check(self): """ Vérifie la validité de la structure du pakfile. Contrôle la présence et la cohérence des clés obligatoires. Ne vérifie PAS la disponibilité des sources ou la qualité du code, juste la structure. """ required_keys = {"name", "ver", "rev", "src"} optional_keys = {"deps", "bdeps", "build", "make", "install"} all_keys = set(self.data.keys()) unknown_keys = all_keys - required_keys - optional_keys missing_keys = required_keys - all_keys if missing_keys: print(f"Clés manquantes dans {self.package}: {', '.join(missing_keys)}") return False if unknown_keys: print(f"Clés inconnues dans {self.package}: {', '.join(unknown_keys)}") return False return True def update_db(self): """ Met à jour la base SQLite avec les informations actuelles du pakfile. Si le paquet existe, il est mis à jour. Sinon, il est inséré. """ conn = get_db_connection() cursor = conn.cursor() # Récupère les données à jour name = self.get("name") version = self.get("ver") revision = self.get("rev") sources = self.get("src") deps = self.get("deps") bdeps = self.get("bdeps") # Conversion des listes en string (on se complique pas la vie) src_str = ",".join(sources) if sources else "" deps_str = ",".join(deps) if deps else "" bdeps_str = ",".join(bdeps) if bdeps else "" cursor.execute(""" INSERT INTO paquets (name, version, revision, sources, deps, build_deps) VALUES (?, ?, ?, ?, ?, ?) ON CONFLICT(name) DO UPDATE SET version = excluded.version, revision = excluded.revision, sources = excluded.sources, deps = excluded.deps, build_deps = excluded.build_deps """, (name, version, revision, src_str, deps_str, bdeps_str)) conn.commit() conn.close()