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