Compare commits
8 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d801678376 | ||
|
|
2060ad088d | ||
|
|
67c0c00fc3 | ||
|
|
e655fb4f71 | ||
|
|
48e191a705 | ||
|
|
a7d708c8ca | ||
|
|
b27f877f8b | ||
|
|
b21bb94e8d |
5 changed files with 323 additions and 0 deletions
37
Makefile
Normal file
37
Makefile
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
DB_PATH := /var/lib/erminig/erminig.db
|
||||
DB_DIR := /var/lib/erminig
|
||||
PAK_USER := pak
|
||||
|
||||
all: prepare_env create_db create_pak_user
|
||||
|
||||
prepare_env:
|
||||
@echo "Création de l'arborescence pour Erminig..."
|
||||
@mkdir -p $(DB_DIR)
|
||||
@mkdir -p /var/govel
|
||||
@chown -R $(PAK_USER):$(PAK_USER) /var/govel || true
|
||||
|
||||
create_db:
|
||||
@echo "Initialisation de la base SQLite Erminig..."
|
||||
@if [ ! -f "$(DB_PATH)" ]; then \
|
||||
sqlite3 $(DB_PATH) < schema.sql; \
|
||||
chown $(PAK_USER):$(PAK_USER) $(DB_PATH); \
|
||||
echo "Base de données créée à $(DB_PATH)"; \
|
||||
else \
|
||||
echo "La base existe déjà, on touche pas."; \
|
||||
fi
|
||||
|
||||
create_pak_user:
|
||||
@echo "Création de l'utilisateur '$(PAK_USER)'..."
|
||||
@if ! id -u $(PAK_USER) >/dev/null 2>&1; then \
|
||||
useradd -r -m -d /var/govel -s /bin/bash $(PAK_USER); \
|
||||
echo "Utilisateur '$(PAK_USER)' créé."; \
|
||||
else \
|
||||
echo "L'utilisateur '$(PAK_USER)' existe déjà."; \
|
||||
fi
|
||||
|
||||
clean:
|
||||
@echo "Suppression de la base et de l'arborescence..."
|
||||
@rm -f $(DB_PATH)
|
||||
@rm -rf /var/govel
|
||||
|
||||
.PHONY: all prepare_env create_db create_pak_user clean
|
||||
37
schema.sql
Normal file
37
schema.sql
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
-- schema.sql - Structure initiale de la base Erminig
|
||||
|
||||
CREATE TABLE packages (
|
||||
name TEXT PRIMARY KEY,
|
||||
version TEXT NOT NULL,
|
||||
revision INTEGER NOT NULL,
|
||||
status TEXT NOT NULL, -- draft, built, failed, validated
|
||||
blocked BOOLEAN NOT NULL DEFAULT 0,
|
||||
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TEXT DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE sources (
|
||||
package_name TEXT,
|
||||
url TEXT,
|
||||
hash TEXT,
|
||||
FOREIGN KEY (package_name) REFERENCES packages(name)
|
||||
);
|
||||
|
||||
CREATE TABLE dependencies (
|
||||
package_name TEXT,
|
||||
dependency TEXT,
|
||||
type TEXT, -- build, runtime, check
|
||||
FOREIGN KEY (package_name) REFERENCES packages(name)
|
||||
);
|
||||
|
||||
CREATE TABLE builds (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
package_name TEXT,
|
||||
version TEXT,
|
||||
revision INTEGER,
|
||||
start_time TEXT DEFAULT CURRENT_TIMESTAMP,
|
||||
end_time TEXT,
|
||||
status TEXT, -- success, failed
|
||||
log TEXT,
|
||||
FOREIGN KEY (package_name) REFERENCES packages(name)
|
||||
);
|
||||
0
src/erminig/common/__init__.py
Normal file
0
src/erminig/common/__init__.py
Normal file
25
src/erminig/common/db.py
Normal file
25
src/erminig/common/db.py
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
#
|
||||
# Erminig - Interface SQLite
|
||||
# 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 sqlite3
|
||||
import os
|
||||
|
||||
DB_PATH = "/var/lib/erminig/erminig.db"
|
||||
|
||||
def get_db_connection():
|
||||
"""
|
||||
Ouvre une connexion à la base SQLite d'Erminig.
|
||||
Si la base n'existe pas, déclenche une exception et laisse Make gérer sa création.
|
||||
"""
|
||||
if not os.path.exists(DB_PATH):
|
||||
raise FileNotFoundError(f"La base de données Erminig est introuvable. Exécutez 'make init-db' avant de continuer.")
|
||||
|
||||
conn = sqlite3.connect(DB_PATH)
|
||||
conn.row_factory = sqlite3.Row
|
||||
return conn
|
||||
224
src/erminig/common/pakfile.py
Normal file
224
src/erminig/common/pakfile.py
Normal file
|
|
@ -0,0 +1,224 @@
|
|||
#
|
||||
# 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()
|
||||
Loading…
Add table
Add a link
Reference in a new issue