mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-22 14:38:12 +02:00
Rebaptise AJEAN en Loki (fork, lignée conservée)
- module github.com/R0m1k3/Loki, cmd/loki, internal/loki (package loki) - LOKI_HOME, LOKI_MODEL_DIRS, LOKI_SERVICE, LOKI_DL_CONNS ; /etc/loki ; units loki-engine / loki-ui ; binaire et aide CLI - updateRepo pointe sur R0m1k3/Loki (l'auto-update ne tirera plus les binaires AJEAN amont) Conservé à l'identique : le domaine ajean.link (service de tunnel amont), les littéraux de migration 0.7.x (migrate_07.go), RELEASE_NOTES.md et LICENSE (historique et licence de l'amont). go build/vet/test : verts.
This commit is contained in:
157 files changed
+928
-928
No files matched your search
@@ -0,0 +1,162 @@
|
||||
//go:build !windows
|
||||
|
||||
package loki
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// sys_install_unix.go — installation et désinstallation, partie commune à Linux
|
||||
// et macOS. Les deux plateformes font exactement la même chose, à un détail
|
||||
// près : la façon de déclarer les services (systemd d'un côté, launchd de
|
||||
// l'autre). Ce détail vit dans sys_install_linux.go / sys_install_darwin.go,
|
||||
// derrière deux fonctions : installServices et uninstallServices.
|
||||
//
|
||||
// Avant, chaque plateforme avait sa copie intégrale du parcours : deux fichiers
|
||||
// identiques à 60 %, donc toute correction à faire deux fois. Comme le support
|
||||
// macOS n'est pas testé sur une vraie machine, c'est précisément là qu'une
|
||||
// divergence passait inaperçue.
|
||||
|
||||
func cmdInstall(args []string) error {
|
||||
if os.Geteuid() != 0 {
|
||||
return fmt.Errorf("loki install doit être exécuté en root (sudo loki install)")
|
||||
}
|
||||
targetUser := os.Getenv("SUDO_USER")
|
||||
if targetUser == "" {
|
||||
targetUser = "root"
|
||||
}
|
||||
for _, a := range args {
|
||||
if strings.HasPrefix(a, "--user=") {
|
||||
targetUser = strings.TrimPrefix(a, "--user=")
|
||||
}
|
||||
}
|
||||
u, err := user.Lookup(targetUser)
|
||||
if err != nil {
|
||||
return fmt.Errorf("utilisateur '%s' introuvable: %w", targetUser, err)
|
||||
}
|
||||
lokiHome := defaultLokiHome()
|
||||
if v := os.Getenv("LOKI_HOME"); v != "" {
|
||||
lokiHome = v
|
||||
}
|
||||
|
||||
fmt.Printf("Installation pour utilisateur %s\n", cyan(targetUser))
|
||||
fmt.Printf(" LOKI_HOME = %s\n", lokiHome)
|
||||
fmt.Printf(" services = %s + %s\n", serviceName(), uiUnitName)
|
||||
|
||||
// 0. Reprise d'une installation 0.7, si c'en est une. AVANT provisionDataDir :
|
||||
// elle déplace des dossiers entiers, donc la destination ne doit pas
|
||||
// encore exister. Code temporaire, voir migrate_07.go.
|
||||
if err := migrateFrom07(lokiHome); err != nil {
|
||||
return fmt.Errorf("reprise de l'installation 0.7 : %w", err)
|
||||
}
|
||||
|
||||
// 1. Arborescence + configuration de départ.
|
||||
if err := provisionDataDir(); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf(" %s arborescence prête\n", green("✓"))
|
||||
|
||||
// 2. Lien vers /usr/local/bin/loki.
|
||||
if err := linkInstalledExe(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 3. /etc/default/loki, pour que les invocations root résolvent LOKI_HOME.
|
||||
if err := os.MkdirAll("/etc/default", 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
defaults := fmt.Sprintf("# Généré par loki install — racine des données\nLOKI_HOME=%s\n", lokiHome)
|
||||
if err := os.WriteFile("/etc/default/loki", []byte(defaults), 0o644); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf(" %s /etc/default/loki\n", green("✓"))
|
||||
|
||||
// 4. Déclaration des services (systemd ou launchd selon la plateforme).
|
||||
if err := installServices(targetUser, lokiHome); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 5. LOKI_HOME appartient à l'utilisateur cible : les deux services et les
|
||||
// commandes qu'il tape doivent pouvoir écrire dans la base.
|
||||
chown(lokiHome, u)
|
||||
|
||||
fmt.Println()
|
||||
fmt.Printf("%s installation terminée.\n", green("[ok]"))
|
||||
fmt.Printf("\nProchaines étapes :\n")
|
||||
fmt.Printf(" 1. installe le moteur : %s (compile llama.cpp, renseigne BIN)\n", bold("sudo -u "+targetUser+" loki llamacpp install"))
|
||||
fmt.Printf(" 2. choisis le modèle : %s (renseigne MODEL, un fichier .gguf)\n", bold("sudo -u "+targetUser+" loki edit"))
|
||||
fmt.Printf(" 3. démarre le moteur : %s\n", bold("sudo loki start"))
|
||||
fmt.Printf(" 4. vérifie : %s\n", bold("loki test"))
|
||||
fmt.Printf(" 5. démarre l'interface : %s (http://localhost:8090)\n", bold("sudo loki ui start"))
|
||||
return nil
|
||||
}
|
||||
|
||||
// linkInstalledExe pose /usr/local/bin/loki sur le binaire courant.
|
||||
//
|
||||
// Garde-fou (issue #5) : si on tourne DÉJÀ depuis la cible — l'utilisateur a
|
||||
// posé le binaire là puis lancé `sudo loki install` — il ne faut surtout pas
|
||||
// Remove+Symlink sur soi-même : ça effacerait le vrai binaire et créerait un
|
||||
// lien vers lui-même (« Too many levels of symbolic links »). On compare les
|
||||
// chemins réels, résolus.
|
||||
func linkInstalledExe() error {
|
||||
self, err := os.Executable()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rp, e := filepath.EvalSymlinks(self); e == nil {
|
||||
self = rp
|
||||
}
|
||||
target := installedExePath()
|
||||
if err := os.MkdirAll(filepath.Dir(target), 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
if tp, e := filepath.EvalSymlinks(target); e == nil && tp == self {
|
||||
fmt.Printf(" %s %s est déjà le binaire installé (aucun lien à créer)\n", green("✓"), target)
|
||||
return nil
|
||||
}
|
||||
_ = os.Remove(target)
|
||||
if err := os.Symlink(self, target); err != nil {
|
||||
// Repli sur une copie si le lien échoue (volumes différents).
|
||||
data, rerr := os.ReadFile(self)
|
||||
if rerr != nil {
|
||||
return rerr
|
||||
}
|
||||
if werr := os.WriteFile(target, data, 0o755); werr != nil {
|
||||
return werr
|
||||
}
|
||||
}
|
||||
fmt.Printf(" %s %s -> %s\n", green("✓"), target, self)
|
||||
return nil
|
||||
}
|
||||
|
||||
func cmdUninstall(args []string) error {
|
||||
if os.Geteuid() != 0 {
|
||||
return fmt.Errorf("loki uninstall doit être exécuté en root")
|
||||
}
|
||||
uninstallServices()
|
||||
for _, p := range []string{"/etc/default/loki", installedExePath()} {
|
||||
if err := os.Remove(p); err == nil {
|
||||
fmt.Printf(" %s %s\n", green("✓"), p)
|
||||
}
|
||||
}
|
||||
fmt.Println(dim("(données utilisateur conservées — supprime $LOKI_HOME manuellement si tu veux purger)"))
|
||||
fmt.Println(green("[ok]") + " désinstallé")
|
||||
return nil
|
||||
}
|
||||
|
||||
// chown donne récursivement path à l'utilisateur/groupe indiqué.
|
||||
func chown(path string, u *user.User) {
|
||||
var uid, gid int
|
||||
fmt.Sscanf(u.Uid, "%d", &uid)
|
||||
fmt.Sscanf(u.Gid, "%d", &gid)
|
||||
_ = filepath.Walk(path, func(p string, info os.FileInfo, err error) error {
|
||||
if err == nil {
|
||||
_ = os.Chown(p, uid, gid)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user