Compare commits

...
Sign in to create a new pull request.

8 commits
main ... dev

Author SHA1 Message Date
L0m1g
d801678376 Fix: Delete old License file 2025-03-03 11:57:21 +01:00
L0m1g
2060ad088d feat(db) update db when pakfile make changes 2025-03-03 11:49:17 +01:00
L0m1g
67c0c00fc3 feat(makefile): Makefile ind sql schema 2025-03-03 11:29:56 +01:00
L0m1g
e655fb4f71 feat(makefile): Makefile ind sql schema 2025-03-03 11:27:13 +01:00
L0m1g
48e191a705 feat(db) database connection 2025-03-03 11:25:08 +01:00
L0m1g
a7d708c8ca Fix: Readme history 2025-03-03 10:13:59 +01:00
L0m1g
b27f877f8b feat(common/pakfile): impl??mentation initiale de la classe Pakfile 2025-03-03 10:03:30 +01:00
L0m1g
b21bb94e8d Premier souffle d’Erminig.
Les fondations sont posées, la licence DOUARN flotte fièrement.
Le goéland nous surveille, l’hermine affûte ses griffes.
Archers en sucre, tremblez. Le vent de l’ouest se lève.
2025-03-02 11:35:18 +01:00
5 changed files with 323 additions and 0 deletions

37
Makefile Normal file
View 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
View 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)
);

View file

25
src/erminig/common/db.py Normal file
View 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 lair, 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

View 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 lair, 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 dinstallation 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()