From 61d7a9a7aa1e5d647fedae9bad029e4c0a5931cd Mon Sep 17 00:00:00 2001 From: musabe24 Date: Mon, 17 Nov 2025 23:05:15 +0100 Subject: [PATCH] =?UTF-8?q?ginie=20installiert=20eingef=C3=BChrt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Makefile | 2 +- cmd/ginie/main.go | 17 ++++ internal/install/install.go | 175 ++++++++++++++++++++++++++++++++++++ 3 files changed, 193 insertions(+), 1 deletion(-) create mode 100644 internal/install/install.go diff --git a/Makefile b/Makefile index b4ad17a..d07dde9 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ BINARY=ginie -VERSION=0.1.2 +VERSION=0.1.3 build: go build -o $(BINARY) ./cmd/ginie diff --git a/cmd/ginie/main.go b/cmd/ginie/main.go index 97b0734..dc4108c 100644 --- a/cmd/ginie/main.go +++ b/cmd/ginie/main.go @@ -6,6 +6,8 @@ import ( "gitea.home.musaberdem.de/musabe24/ginie/internal/repos" "gitea.home.musaberdem.de/musabe24/ginie/internal/version" + "gitea.home.musaberdem.de/musabe24/ginie/internal/install" + ) func main() { @@ -57,6 +59,21 @@ func main() { 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 ") + return + } + target := os.Args[2] + + err := install.Install(target) + if err != nil { + fmt.Println("Fehler:", err) + return + } + return + default: fmt.Println("Unbekannter Befehl:", cmd) diff --git a/internal/install/install.go b/internal/install/install.go new file mode 100644 index 0000000..8704641 --- /dev/null +++ b/internal/install/install.go @@ -0,0 +1,175 @@ +package install + +import ( + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "os" + "os/exec" + "path/filepath" + "strings" + + "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"` +} + +func configPath() (string, error) { + home, err := os.UserHomeDir() + if err != nil { + return "", err + } + + dir := filepath.Join(home, ".config", "ginie") + os.MkdirAll(dir, 0755) + + return filepath.Join(dir, "installed.json"), nil +} + +func LoadInstalled() ([]Installed, error) { + path, _ := configPath() + data, err := os.ReadFile(path) + + if os.IsNotExist(err) { + return []Installed{}, nil + } + if err != nil { + return nil, 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) +} + +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] +} + +func Install(pkg string) error { + name, version := parseNameVersion(pkg) + + // 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 ") + } + + // 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() + + 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"` + } + + if err := json.NewDecoder(resp.Body).Decode(&release); err != nil { + return err + } + + 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) + } + + fmt.Print("Wähle Asset-Nummer: ") + var choice int + fmt.Scan(&choice) + + if choice < 0 || choice >= len(release.Assets) { + return errors.New("Ungültige Auswahl") + } + + asset := release.Assets[choice] + + // Datei herunterladen + fmt.Println("Lade herunter:", asset.Name) + + out, err := os.Create(asset.Name) + if err != nil { + return err + } + defer out.Close() + + dl, err := http.Get(asset.BrowserDownloadURL) + if err != nil { + return err + } + defer dl.Body.Close() + + _, err = io.Copy(out, dl.Body) + if err != nil { + return err + } + + fmt.Println("Download abgeschlossen.") + + // 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 + return cmd.Run() + } + + return errors.New("Asset-Typ wird noch nicht unterstützt") +} +