From 3764e8b5463c6adacb2d47fa31fe3b0e6dfbee42 Mon Sep 17 00:00:00 2001 From: musabe24 Date: Tue, 18 Nov 2025 21:09:18 +0100 Subject: [PATCH] Installierte Package Version wird ab jetzt erfasst. --- cmd/ginie/main.go | 124 ++++++++-------- internal/install/install.go | 287 +++++++++++++++++++----------------- internal/repos/repos.go | 163 +++++++++++--------- 3 files changed, 306 insertions(+), 268 deletions(-) diff --git a/cmd/ginie/main.go b/cmd/ginie/main.go index 05544f1..497aec5 100644 --- a/cmd/ginie/main.go +++ b/cmd/ginie/main.go @@ -1,45 +1,44 @@ package main import ( - "fmt" - "os" - - "gitea.home.musaberdem.de/musabe24/ginie/internal/repos" - "gitea.home.musaberdem.de/musabe24/ginie/internal/version" - "gitea.home.musaberdem.de/musabe24/ginie/internal/install" + "fmt" + "os" + "gitea.home.musaberdem.de/musabe24/ginie/internal/install" + "gitea.home.musaberdem.de/musabe24/ginie/internal/repos" + "gitea.home.musaberdem.de/musabe24/ginie/internal/version" ) func main() { - if len(os.Args) < 2 { - fmt.Println("Ginie – Git Native Installer Engine") - fmt.Println("Usage: ginie [arguments]") - return - } + if len(os.Args) < 2 { + fmt.Println("Ginie – Git Native Installer Engine") + fmt.Println("Usage: ginie [arguments]") + return + } - cmd := os.Args[1] + cmd := os.Args[1] - switch cmd { + switch cmd { - case "--version", "-v": - fmt.Println("ginie version", version.Version) - return + case "--version", "-v": + fmt.Println("ginie version", version.Version) + return - case "add": - if len(os.Args) < 3 { - fmt.Println("Usage: ginie add ") - return - } - url := os.Args[2] + case "add": + if len(os.Args) < 3 { + fmt.Println("Usage: ginie add ") + return + } + url := os.Args[2] - err := repos.AddRepo(url) - if err != nil { - fmt.Println("Fehler:", err) - return - } + err := repos.AddRepo(url) + if err != nil { + fmt.Println("Fehler:", err) + return + } - fmt.Println("Repo hinzugefügt:", url) - return + fmt.Println("Repo hinzugefügt:", url) + return case "remove": if len(os.Args) < 3 { @@ -57,42 +56,45 @@ func main() { fmt.Println("Repo entfernt:", url) return - case "list": - reposList, err := repos.ListRepos() - if err != nil { - fmt.Println("Fehler beim Laden:", err) - return - } + case "list": + reposList, err := repos.ListRepos() + if err != nil { + fmt.Println("Fehler beim Laden:", err) + return + } - if len(reposList) == 0 { - fmt.Println("Noch keine Repositories hinzugefügt. Nutze:") - fmt.Println(" ginie add ") - return - } + if len(reposList) == 0 { + fmt.Println("Noch keine Repositories hinzugefügt. Nutze:") + fmt.Println(" ginie add ") + return + } - fmt.Println("Registrierte Repositories:") - for _, r := range reposList { - fmt.Printf("- %s/%s (%s)\n", r.Owner, r.Name, r.URL) - } - return - - case "install": - if len(os.Args) < 3 { - fmt.Println("Usage: ginie install ") + fmt.Println("Registrierte Repositories:") + for _, r := range reposList { + version := r.InstalledVersion + if version == "" { + version = "nicht installiert" + } + fmt.Printf("- %s/%s (%s) – Version: %s\n", r.Owner, r.Name, r.URL, version) + } return - } - target := os.Args[2] - err := install.Install(target) - if err != nil { - fmt.Println("Fehler:", err) + case "install": + if len(os.Args) < 3 { + fmt.Println("Usage: ginie install ") + return + } + target := os.Args[2] + + err := install.Install(target) + if err != nil { + fmt.Println("Fehler:", err) + return + } return - } - return - - default: - fmt.Println("Unbekannter Befehl:", cmd) - return - } + default: + fmt.Println("Unbekannter Befehl:", cmd) + return + } } diff --git a/internal/install/install.go b/internal/install/install.go index dc5dde2..087d052 100644 --- a/internal/install/install.go +++ b/internal/install/install.go @@ -1,183 +1,196 @@ package install import ( - "encoding/json" - "errors" - "fmt" - "io" - "net/http" - "os" - "os/exec" - "path/filepath" - "strings" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "os" + "os/exec" + "path/filepath" + "strings" - "gitea.home.musaberdem.de/musabe24/ginie/internal/repos" + "gitea.home.musaberdem.de/musabe24/ginie/internal/repos" ) type Installed struct { - Name string `json:"name"` - Version string `json:"version"` - AssetName string `json:"asset"` - AssetURL string `json:"url"` + Name string `json:"name"` + Version string `json:"version"` + AssetName string `json:"asset"` + AssetURL string `json:"url"` } func configPath() (string, error) { - home, err := os.UserHomeDir() - if err != nil { - return "", err - } + home, err := os.UserHomeDir() + if err != nil { + return "", err + } - dir := filepath.Join(home, ".config", "ginie") - os.MkdirAll(dir, 0755) + dir := filepath.Join(home, ".config", "ginie") + os.MkdirAll(dir, 0755) - return filepath.Join(dir, "installed.json"), nil + return filepath.Join(dir, "installed.json"), nil } func LoadInstalled() ([]Installed, error) { - path, _ := configPath() - data, err := os.ReadFile(path) + path, _ := configPath() + data, err := os.ReadFile(path) - if os.IsNotExist(err) { - return []Installed{}, nil - } - if err != nil { - return nil, err - } + if os.IsNotExist(err) { + return []Installed{}, nil + } + if err != nil { + return nil, err + } - var out []Installed - err = json.Unmarshal(data, &out) - return out, err + var out []Installed + err = json.Unmarshal(data, &out) + return out, err } func SaveInstalled(list []Installed) error { - path, _ := configPath() - data, err := json.MarshalIndent(list, "", " ") - if err != nil { - return err - } - return os.WriteFile(path, data, 0644) + path, _ := configPath() + data, err := json.MarshalIndent(list, "", " ") + if err != nil { + return err + } + return os.WriteFile(path, data, 0644) } func parseNameVersion(input string) (name string, version string) { - parts := strings.Split(input, ":") - if len(parts) == 1 { - return parts[0], "" // keine Version - } - return parts[0], parts[1] + parts := strings.Split(input, ":") + if len(parts) == 1 { + return parts[0], "" // keine Version + } + return parts[0], parts[1] } func Install(pkg string) error { - name, version := parseNameVersion(pkg) + name, version := parseNameVersion(pkg) - // Repo suchen - all, err := repos.LoadRepos() - if err != nil { - return err - } + // Repo suchen + all, err := repos.LoadRepos() + if err != nil { + return err + } - var repo repos.Repo - found := false - for _, r := range all { - if r.Name == name { - repo = r - found = true - break - } - } - if !found { - return errors.New("Repo nicht gefunden. Nutze: ginie add ") - } + var repo repos.Repo + found := false + for _, r := range all { + if r.Name == name { + repo = r + found = true + break + } + } + if !found { + return errors.New("Repo nicht gefunden. Nutze: ginie add ") + } - // Release-API-URL - api := "" - if version == "" { - api = fmt.Sprintf("https://api.github.com/repos/%s/%s/releases/latest", repo.Owner, repo.Name) - } else { - api = fmt.Sprintf("https://api.github.com/repos/%s/%s/releases/tags/%s", repo.Owner, repo.Name, version) - } + // Release-API-URL + api := "" + if version == "" { + api = fmt.Sprintf("https://api.github.com/repos/%s/%s/releases/latest", repo.Owner, repo.Name) + } else { + api = fmt.Sprintf("https://api.github.com/repos/%s/%s/releases/tags/%s", repo.Owner, repo.Name, version) + } - // API abfragen - resp, err := http.Get(api) - if err != nil { - return err - } - defer resp.Body.Close() + // API abfragen + resp, err := http.Get(api) + if err != nil { + return err + } + defer resp.Body.Close() - if resp.StatusCode != 200 { - return fmt.Errorf("GitHub API Fehler: %s", resp.Status) - } + if resp.StatusCode != 200 { + return fmt.Errorf("GitHub API Fehler: %s", resp.Status) + } - var release struct { - TagName string `json:"tag_name"` - Assets []struct { - Name string `json:"name"` - BrowserDownloadURL string `json:"browser_download_url"` - } `json:"assets"` - } + var release struct { + TagName string `json:"tag_name"` + Assets []struct { + Name string `json:"name"` + BrowserDownloadURL string `json:"browser_download_url"` + } `json:"assets"` + } - if err := json.NewDecoder(resp.Body).Decode(&release); err != nil { - return err - } + if err := json.NewDecoder(resp.Body).Decode(&release); err != nil { + return err + } - if len(release.Assets) == 0 { - return errors.New("Release hat keine Assets") - } + if len(release.Assets) == 0 { + return errors.New("Release hat keine Assets") + } - // Asset-Auswahl - fmt.Println("Verfügbare Assets:") - for i, a := range release.Assets { - fmt.Printf("[%d] %s\n", i, a.Name) - } + installedVersion := release.TagName + if installedVersion == "" { + installedVersion = version + } - fmt.Print("Wähle Asset-Nummer: ") - var choice int - fmt.Scan(&choice) + // Asset-Auswahl + fmt.Println("Verfügbare Assets:") + for i, a := range release.Assets { + fmt.Printf("[%d] %s\n", i, a.Name) + } - if choice < 0 || choice >= len(release.Assets) { - return errors.New("Ungültige Auswahl") - } + fmt.Print("Wähle Asset-Nummer: ") + var choice int + fmt.Scan(&choice) - asset := release.Assets[choice] + if choice < 0 || choice >= len(release.Assets) { + return errors.New("Ungültige Auswahl") + } - // Datei herunterladen - fmt.Println("Lade herunter:", asset.Name) + asset := release.Assets[choice] - out, err := os.Create(asset.Name) - if err != nil { - return err - } - defer out.Close() + // Datei herunterladen + fmt.Println("Lade herunter:", asset.Name) - dl, err := http.Get(asset.BrowserDownloadURL) - if err != nil { - return err - } - defer dl.Body.Close() + out, err := os.Create(asset.Name) + if err != nil { + return err + } + defer out.Close() - _, err = io.Copy(out, dl.Body) - if err != nil { - return err - } + dl, err := http.Get(asset.BrowserDownloadURL) + if err != nil { + return err + } + defer dl.Body.Close() - fmt.Println("Download abgeschlossen.") + _, err = io.Copy(out, dl.Body) + if err != nil { + return err + } - // Installation (unterstützt .deb) - if strings.HasSuffix(asset.Name, ".deb") { - fmt.Println("Installiere .deb Datei…") - cmd := exec.Command("sudo", "dpkg", "-i", asset.Name) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - err := cmd.Run() - if err != nil { - fmt.Println("Package konnte nicht installiert werden.") - } - err = os.Remove(asset.Name) - if err != nil { - fmt.Println("Package konnte nicht gelöscht werden.") - } - return err - } + fmt.Println("Download abgeschlossen.") - return errors.New("Asset-Typ wird noch nicht unterstützt") + // Installation (unterstützt .deb) + if strings.HasSuffix(asset.Name, ".deb") { + fmt.Println("Installiere .deb Datei…") + cmd := exec.Command("sudo", "dpkg", "-i", asset.Name) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + // Run the install command + err := cmd.Run() + if err != nil { + fmt.Println("Package konnte nicht installiert werden.") + } + // Remove debian package file + err = os.Remove(asset.Name) + if err != nil { + fmt.Println("Package konnte nicht gelöscht werden.") + } + if installedVersion == "" { + installedVersion = "unknown" + } + if err := repos.SetInstalledVersion(repo.Name, installedVersion); err != nil { + fmt.Println("Speichern der installierten Version nicht möglich.") + return err + } + return nil + } + + return errors.New("Asset-Typ wird noch nicht unterstützt") } - diff --git a/internal/repos/repos.go b/internal/repos/repos.go index d9069ec..4ddb0fb 100644 --- a/internal/repos/repos.go +++ b/internal/repos/repos.go @@ -1,106 +1,107 @@ package repos import ( - "encoding/json" - "errors" - "os" - "path/filepath" - "strings" + "encoding/json" + "errors" + "os" + "path/filepath" + "strings" ) type Repo struct { - URL string `json:"url"` - Owner string `json:"owner"` - Name string `json:"name"` + URL string `json:"url"` + Owner string `json:"owner"` + Name string `json:"name"` + InstalledVersion string `json:"installed_version,omitempty"` } func configPath() (string, error) { - home, err := os.UserHomeDir() - if err != nil { - return "", err - } + home, err := os.UserHomeDir() + if err != nil { + return "", err + } - dir := filepath.Join(home, ".config", "ginie") - os.MkdirAll(dir, 0755) + dir := filepath.Join(home, ".config", "ginie") + os.MkdirAll(dir, 0755) - return filepath.Join(dir, "repos.json"), nil + return filepath.Join(dir, "repos.json"), nil } func LoadRepos() ([]Repo, error) { - path, err := configPath() - if err != nil { - return nil, err - } + path, err := configPath() + if err != nil { + return nil, err + } - data, err := os.ReadFile(path) - if os.IsNotExist(err) { - return []Repo{}, nil - } - if err != nil { - return nil, err - } + data, err := os.ReadFile(path) + if os.IsNotExist(err) { + return []Repo{}, nil + } + if err != nil { + return nil, err + } - var repos []Repo - err = json.Unmarshal(data, &repos) - if err != nil { - return nil, err - } + var repos []Repo + err = json.Unmarshal(data, &repos) + if err != nil { + return nil, err + } - return repos, nil + return repos, nil } func SaveRepos(repos []Repo) error { - path, err := configPath() - if err != nil { - return err - } + path, err := configPath() + if err != nil { + return err + } - data, err := json.MarshalIndent(repos, "", " ") - if err != nil { - return err - } + data, err := json.MarshalIndent(repos, "", " ") + if err != nil { + return err + } - return os.WriteFile(path, data, 0644) + return os.WriteFile(path, data, 0644) } func ParseGitHubURL(raw string) (Repo, error) { - if !strings.HasPrefix(raw, "https://github.com/") { - return Repo{}, errors.New("URL ist kein gültiger GitHub-Link") - } + if !strings.HasPrefix(raw, "https://github.com/") { + return Repo{}, errors.New("URL ist kein gültiger GitHub-Link") + } - parts := strings.Split(strings.TrimPrefix(raw, "https://github.com/"), "/") - if len(parts) < 2 { - return Repo{}, errors.New("GitHub-Link hat nicht das Format: https://github.com//") - } + parts := strings.Split(strings.TrimPrefix(raw, "https://github.com/"), "/") + if len(parts) < 2 { + return Repo{}, errors.New("GitHub-Link hat nicht das Format: https://github.com//") + } - return Repo{ - URL: raw, - Owner: parts[0], - Name: parts[1], - }, nil + return Repo{ + URL: raw, + Owner: parts[0], + Name: parts[1], + }, nil } func AddRepo(url string) error { - repo, err := ParseGitHubURL(url) - if err != nil { - return err - } + repo, err := ParseGitHubURL(url) + if err != nil { + return err + } - repos, err := LoadRepos() - if err != nil { - return err - } + repos, err := LoadRepos() + if err != nil { + return err + } - // prüfen ob bereits vorhanden - for _, r := range repos { - if r.URL == repo.URL { - return errors.New("Repo existiert bereits") - } - } + // prüfen ob bereits vorhanden + for _, r := range repos { + if r.URL == repo.URL { + return errors.New("Repo existiert bereits") + } + } - repos = append(repos, repo) + repos = append(repos, repo) - return SaveRepos(repos) + return SaveRepos(repos) } func RemoveRepo(url string) error { @@ -127,5 +128,27 @@ func RemoveRepo(url string) error { } func ListRepos() ([]Repo, error) { - return LoadRepos() + return LoadRepos() +} + +func SetInstalledVersion(name string, version string) error { + repos, err := LoadRepos() + if err != nil { + return err + } + + updated := false + for i := range repos { + if repos[i].Name == name { + repos[i].InstalledVersion = version + updated = true + break + } + } + + if !updated { + return errors.New("Repo nicht gefunden") + } + + return SaveRepos(repos) }