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 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 } return errors.New("Asset-Typ wird noch nicht unterstützt") }