224 lines
7.2 KiB
Python
224 lines
7.2 KiB
Python
#
|
||
# 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()
|