356 lines
8.3 KiB
Go
356 lines
8.3 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"os/exec"
|
|
"runtime"
|
|
"strings"
|
|
|
|
"github.com/charmbracelet/bubbles/list"
|
|
tea "github.com/charmbracelet/bubbletea"
|
|
"github.com/charmbracelet/huh"
|
|
"github.com/charmbracelet/lipgloss"
|
|
"github.com/spf13/cobra"
|
|
"github.com/spf13/viper"
|
|
)
|
|
|
|
func getLinuxDistribution() (string, error) {
|
|
data, err := os.ReadFile("/etc/os-release")
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
lines := strings.Split(string(data), "\n")
|
|
for _, line := range lines {
|
|
if strings.HasPrefix(line, "ID=") {
|
|
return strings.Trim(strings.TrimPrefix(line, "ID="), "\""), nil
|
|
}
|
|
}
|
|
return "", fmt.Errorf("Linux distribution not found")
|
|
}
|
|
|
|
func getPackageManager(os string) (string, error) {
|
|
switch os {
|
|
case "debian", "ubuntu":
|
|
return "apt", nil
|
|
case "arch":
|
|
return "pacman", nil
|
|
case "fedora":
|
|
return "dnf", nil
|
|
default:
|
|
return "", fmt.Errorf("no packagemanager found for os: %s", os)
|
|
}
|
|
}
|
|
|
|
func getInstallCommand(pm string) (string, error) {
|
|
switch pm {
|
|
case "apt":
|
|
return "apt install -y", nil
|
|
case "pacman":
|
|
return "pacman -S --noconfirm", nil
|
|
case "dnf":
|
|
return "dnf install -y", nil
|
|
default:
|
|
return "", fmt.Errorf("no install command found for package manager: %s", pm)
|
|
}
|
|
}
|
|
|
|
func installPackage(cmd, pkg string) error {
|
|
fullCmd := fmt.Sprintf("%s %s", cmd, pkg)
|
|
command := exec.Command("sudo", "sh", "-c", fullCmd)
|
|
output, err := command.CombinedOutput()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to install %s: %v\n%s", pkg, err, string(output))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func downloadGolang(golangVersion string) error {
|
|
var link string
|
|
if runtime.GOARCH == "arm64" {
|
|
link = "https://go.dev/dl/go" + golangVersion + ".linux-arm64.tar.gz"
|
|
} else {
|
|
link = "https://go.dev/dl/go" + golangVersion + ".linux-amd64.tar.gz"
|
|
}
|
|
|
|
resp, err := http.Get(link)
|
|
if err != nil {
|
|
return fmt.Errorf("Fehler beim Herunterladen von Go: %v", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
return fmt.Errorf("Unerwarteter HTTP-Status: %s", resp.Status)
|
|
}
|
|
|
|
outFile, err := os.Create("go" + golangVersion + ".linux-" + runtime.GOARCH + ".tar.gz")
|
|
if err != nil {
|
|
return fmt.Errorf("Fehler beim Erstellen der Ausgabedatei: %v", err)
|
|
}
|
|
defer outFile.Close()
|
|
|
|
_, err = io.Copy(outFile, resp.Body)
|
|
if err != nil {
|
|
return fmt.Errorf("Fehler beim Schreiben der Ausgabedatei: %v", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func installSpecialSoftware() error {
|
|
if _, err := exec.LookPath("go"); err == nil {
|
|
fmt.Println("Go ist bereits installiert.")
|
|
} else {
|
|
golangVersion := "1.23.4"
|
|
if err := downloadGolang(golangVersion); err != nil {
|
|
return fmt.Errorf("Fehler beim Herunterladen von Go: %v", err)
|
|
}
|
|
golangCommand := fmt.Sprintf("sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go%s.linux-%s.tar.gz", golangVersion, runtime.GOARCH)
|
|
if err := installPackage(golangCommand, ""); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if _, err := exec.LookPath("rustc"); err == nil {
|
|
fmt.Println("Rust ist bereits installiert.")
|
|
} else {
|
|
rustupCommand := "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -- -q -y"
|
|
if err := installPackage(rustupCommand, ""); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if _, err := exec.LookPath("pipx"); err == nil {
|
|
fmt.Println("Pipx ist bereits installiert.")
|
|
} else {
|
|
pipXCommand := "python3 -m pip install --user pipx && python3 -m pipx ensurepath"
|
|
if err := installPackage(pipXCommand, ""); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
type model struct {
|
|
list list.Model
|
|
packages []string
|
|
}
|
|
|
|
func (m model) Init() tea.Cmd {
|
|
return nil
|
|
}
|
|
|
|
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|
switch msg := msg.(type) {
|
|
case tea.KeyMsg:
|
|
if msg.String() == "q" {
|
|
return m, tea.Quit
|
|
}
|
|
case tea.WindowSizeMsg:
|
|
h, v := lipgloss.NewStyle().Margin(1, 2).GetFrameSize()
|
|
m.list.SetSize(msg.Width-h, msg.Height-v)
|
|
}
|
|
|
|
var cmd tea.Cmd
|
|
m.list, cmd = m.list.Update(msg)
|
|
return m, cmd
|
|
}
|
|
|
|
func (m model) View() string {
|
|
return "\n" + m.list.View()
|
|
}
|
|
|
|
var rootCmd = &cobra.Command{
|
|
Use: "package-installer",
|
|
Short: "Installiert Pakete basierend auf TOML-Konfiguration",
|
|
Run: run,
|
|
}
|
|
|
|
type Packages struct {
|
|
Headless []string `mapstructure:"headless"`
|
|
NonHeadless []string `mapstructure:"non_headless"`
|
|
}
|
|
|
|
type SpecialPackages struct {
|
|
Go []string `mapstructure:"go"`
|
|
Cargo []string `mapstructure:"cargo"`
|
|
Pipx []string `mapstructure:"pipx"`
|
|
}
|
|
|
|
type Config struct {
|
|
Headless bool `mapstructure:"headless"`
|
|
Packages Packages `mapstructure:"packages"`
|
|
SpecialPackages SpecialPackages `mapstructure:"special_packages"`
|
|
}
|
|
|
|
func init() {
|
|
cobra.OnInitialize(initConfig)
|
|
rootCmd.PersistentFlags().StringP("config", "c", "", "Pfad zur Konfigurationsdatei")
|
|
viper.BindPFlag("config", rootCmd.PersistentFlags().Lookup("config"))
|
|
}
|
|
|
|
func initConfig() {
|
|
if cfgFile := viper.GetString("config"); cfgFile != "" {
|
|
viper.SetConfigFile(cfgFile)
|
|
} else {
|
|
viper.SetConfigName("config")
|
|
viper.AddConfigPath(".")
|
|
}
|
|
|
|
if err := viper.ReadInConfig(); err != nil {
|
|
fmt.Println("Fehler beim Lesen der Konfigurationsdatei:", err)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
func installSpecialPackages(sp SpecialPackages) error {
|
|
for _, pkg := range sp.Go {
|
|
cmd := exec.Command("go", "install", pkg+"@latest")
|
|
if err := cmd.Run(); err != nil {
|
|
return fmt.Errorf("Fehler bei der Installation von %s: %v", pkg, err)
|
|
}
|
|
}
|
|
|
|
for _, pkg := range sp.Cargo {
|
|
cmd := exec.Command("cargo", "install", pkg)
|
|
if err := cmd.Run(); err != nil {
|
|
return fmt.Errorf("Fehler bei der Installation von %s: %v", pkg, err)
|
|
}
|
|
}
|
|
|
|
for _, pkg := range sp.Pipx {
|
|
cmd := exec.Command("pipx", "install", pkg)
|
|
if err := cmd.Run(); err != nil {
|
|
return fmt.Errorf("Fehler bei der Installation von %s: %v", pkg, err)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
type item string
|
|
|
|
func (i item) FilterValue() string { return string(i) }
|
|
|
|
func run(cmd *cobra.Command, args []string) {
|
|
os, err := getLinuxDistribution()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
pm, err := getPackageManager(os)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
installCommand, err := getInstallCommand(pm)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
var cfg Config
|
|
if err := viper.Unmarshal(&cfg); err != nil {
|
|
log.Fatalf("Fehler beim Lesen der Konfiguration: %v", err)
|
|
}
|
|
|
|
form := huh.NewForm(
|
|
huh.NewGroup(
|
|
huh.NewConfirm().
|
|
Title("Möchten Sie eine headless Installation durchführen?").
|
|
Value(&cfg.Headless),
|
|
),
|
|
).WithTheme(huh.ThemeCatppuccin())
|
|
|
|
if err := form.Run(); err != nil {
|
|
log.Fatalf("Fehler bei der Benutzerabfrage: %v", err)
|
|
}
|
|
|
|
var packages []string
|
|
packages = append(packages, cfg.Packages.Headless...)
|
|
if !cfg.Headless {
|
|
packages = append(packages, cfg.Packages.NonHeadless...)
|
|
}
|
|
|
|
// packages := viper.GetStringSlice("packages")
|
|
|
|
items := make([]list.Item, len(packages))
|
|
for i, pkg := range packages {
|
|
items[i] = item(pkg)
|
|
}
|
|
|
|
m := model{
|
|
list: list.New(items, list.NewDefaultDelegate(), 0, 0),
|
|
packages: packages,
|
|
}
|
|
m.list.Title = "Zu installierende Pakete"
|
|
|
|
p := tea.NewProgram(m)
|
|
if _, err := p.Run(); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
var confirm bool
|
|
form = huh.NewForm(
|
|
huh.NewGroup(
|
|
huh.NewConfirm().
|
|
Title("Möchten Sie mit der Installation fortfahren?").
|
|
Value(&confirm),
|
|
),
|
|
).WithTheme(huh.ThemeCatppuccin())
|
|
|
|
err = form.Run()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
if !confirm {
|
|
fmt.Println("Installation abgebrochen.")
|
|
return
|
|
}
|
|
|
|
for _, pkg := range packages {
|
|
if err := installPackage(installCommand, pkg); err != nil {
|
|
log.Printf("Fehler beim Installieren von %s: %v", pkg, err)
|
|
} else {
|
|
fmt.Printf("Erfolgreich installiert: %s\n", pkg)
|
|
}
|
|
}
|
|
|
|
var installSpecial bool
|
|
form = huh.NewForm(
|
|
huh.NewGroup(
|
|
huh.NewConfirm().
|
|
Title("Möchten Sie die speziellen Pakete (Go, Rust, Pipx) installieren?").
|
|
Value(&installSpecial),
|
|
),
|
|
).WithTheme(huh.ThemeCatppuccin())
|
|
|
|
err = form.Run()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
if installSpecial {
|
|
if err := installSpecialSoftware(); err != nil {
|
|
log.Printf("Fehler beim Installieren der speziellen Software: %v", err)
|
|
} else {
|
|
fmt.Println("Spezielle Software erfolgreich installiert")
|
|
}
|
|
} else {
|
|
fmt.Println("Installation der speziellen Pakete übersprungen")
|
|
}
|
|
if err := installSpecialPackages(cfg.SpecialPackages); err != nil {
|
|
log.Printf("Fehler bei der Installation spezieller Pakete: %v", err)
|
|
}
|
|
|
|
}
|
|
|
|
func main() {
|
|
if err := rootCmd.Execute(); err != nil {
|
|
fmt.Println(err)
|
|
os.Exit(1)
|
|
}
|
|
}
|