refactor: seperate functionality into files
This commit is contained in:
parent
715e086fcd
commit
3081e34e2b
11 changed files with 871 additions and 792 deletions
792
main.go
792
main.go
|
|
@ -2,801 +2,9 @@ package main
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/charmbracelet/bubbles/progress"
|
||||
"github.com/charmbracelet/bubbles/spinner"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/huh"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type OS struct {
|
||||
ID string
|
||||
Name string
|
||||
Version string
|
||||
PackageManager string
|
||||
InstallCommand string
|
||||
}
|
||||
|
||||
func parseOsRelease(osRelease string) *OS {
|
||||
var result OS
|
||||
result.ID = "Unknown"
|
||||
result.Name = "Unknown"
|
||||
result.Version = "Unknown"
|
||||
result.PackageManager = "Unkown"
|
||||
result.InstallCommand = "Unkown"
|
||||
|
||||
lines := strings.Split(osRelease, "\n")
|
||||
|
||||
for _, line := range lines {
|
||||
splitLine := strings.SplitN(line, "=", 2)
|
||||
if len(splitLine) != 2 {
|
||||
continue
|
||||
}
|
||||
switch splitLine[0] {
|
||||
case "ID":
|
||||
result.ID = strings.ToLower(strings.Trim(splitLine[1], "\""))
|
||||
case "NAME":
|
||||
result.Name = strings.Trim(splitLine[1], "\"")
|
||||
case "VERSION_ID":
|
||||
result.Version = strings.Trim(splitLine[1], "\"")
|
||||
}
|
||||
}
|
||||
err := result.getPackageManager()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = result.getInstallCommand()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return &result
|
||||
}
|
||||
|
||||
func getLinuxDistribution() (*OS, error) {
|
||||
_, err := os.Stat("/etc/os-release")
|
||||
if os.IsNotExist(err) {
|
||||
return nil, fmt.Errorf("unable to read system information")
|
||||
}
|
||||
|
||||
osRelease, _ := os.ReadFile("/etc/os-release")
|
||||
return parseOsRelease(string(osRelease)), nil
|
||||
}
|
||||
|
||||
func (os *OS) getPackageManager() error {
|
||||
switch os.ID {
|
||||
case "debian", "ubuntu":
|
||||
os.PackageManager = "apt"
|
||||
return nil
|
||||
case "arch":
|
||||
os.PackageManager = "pacman"
|
||||
return nil
|
||||
case "fedora":
|
||||
os.PackageManager = "dnf"
|
||||
return nil
|
||||
default:
|
||||
var pmcommands = []string{
|
||||
"apt",
|
||||
"dnf",
|
||||
"pacman",
|
||||
}
|
||||
for _, pmname := range pmcommands {
|
||||
_, err := exec.LookPath(pmname)
|
||||
if err == nil {
|
||||
os.PackageManager = pmname
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("no packagemanager found for os: %s", os)
|
||||
}
|
||||
}
|
||||
|
||||
func getSudoPassword() (string, error) {
|
||||
var password string
|
||||
form := huh.NewForm(
|
||||
huh.NewGroup(
|
||||
huh.NewInput().
|
||||
Title("Bitte geben Sie Ihr sudo-Passwort ein").
|
||||
Password(true).
|
||||
Value(&password),
|
||||
),
|
||||
).WithTheme(huh.ThemeCatppuccin())
|
||||
|
||||
err := form.Run()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Fehler bei der Passwortabfrage: %v", err)
|
||||
}
|
||||
return password, nil
|
||||
}
|
||||
|
||||
func installBuildEssentials(os *OS, sudoPassword string) error {
|
||||
var command string
|
||||
switch os.PackageManager {
|
||||
case "pacman":
|
||||
command = "pacman -S --noconfirm --needed base-devel"
|
||||
case "apt":
|
||||
command = "apt install -y build-essential"
|
||||
case "dnf":
|
||||
command = "dnf install -y @development-tools"
|
||||
default:
|
||||
return fmt.Errorf("keine Build Essentials für OS %s definiert", os.ID)
|
||||
}
|
||||
|
||||
fmt.Printf("Installiere Build Essentials für %s...\n", os.Name)
|
||||
if err := installPackage(command, "", sudoPassword); err != nil {
|
||||
return fmt.Errorf("Fehler bei der Installation der Build Essentials: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (os *OS) getInstallCommand() error {
|
||||
switch os.PackageManager {
|
||||
case "apt":
|
||||
os.InstallCommand = "apt install -y"
|
||||
return nil
|
||||
case "pacman":
|
||||
os.InstallCommand = "pacman -S --noconfirm --needed"
|
||||
return nil
|
||||
case "dnf":
|
||||
os.InstallCommand = "dnf install -y --best"
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("no install command found for package manager: %s", os.ID)
|
||||
}
|
||||
}
|
||||
|
||||
type specialSoftwareModel struct {
|
||||
items []string
|
||||
index int
|
||||
spinner spinner.Model
|
||||
progress progress.Model
|
||||
done bool
|
||||
sudoPassword string
|
||||
}
|
||||
|
||||
func newSpecialSoftwareModel(sudoPassword string) specialSoftwareModel {
|
||||
items := []string{"oh-my-posh", "golang", "rust"}
|
||||
|
||||
p := progress.New(
|
||||
progress.WithDefaultGradient(),
|
||||
progress.WithWidth(40),
|
||||
progress.WithoutPercentage(),
|
||||
)
|
||||
s := spinner.New()
|
||||
s.Style = lipgloss.NewStyle().Foreground(lipgloss.Color("63"))
|
||||
|
||||
return specialSoftwareModel{
|
||||
items: items,
|
||||
spinner: s,
|
||||
progress: p,
|
||||
sudoPassword: sudoPassword,
|
||||
}
|
||||
}
|
||||
|
||||
func (m specialSoftwareModel) Init() tea.Cmd {
|
||||
return tea.Batch(m.installItemCmd(m.items[m.index]), m.spinner.Tick)
|
||||
}
|
||||
|
||||
func (m specialSoftwareModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
switch msg := msg.(type) {
|
||||
case tea.KeyMsg:
|
||||
switch msg.String() {
|
||||
case "ctrl+c", "esc", "q":
|
||||
return m, tea.Quit
|
||||
}
|
||||
case installedItemMsg:
|
||||
if m.index >= len(m.items)-1 {
|
||||
m.done = true
|
||||
return m, tea.Quit
|
||||
}
|
||||
|
||||
m.index++
|
||||
progressCmd := m.progress.SetPercent(float64(m.index) / float64(len(m.items)))
|
||||
|
||||
return m, tea.Batch(
|
||||
progressCmd,
|
||||
tea.Printf("%s %s", checkMark, m.items[m.index-1]),
|
||||
m.installItemCmd(m.items[m.index]),
|
||||
)
|
||||
case spinner.TickMsg:
|
||||
var cmd tea.Cmd
|
||||
m.spinner, cmd = m.spinner.Update(msg)
|
||||
return m, cmd
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (m specialSoftwareModel) View() string {
|
||||
if m.done {
|
||||
return doneStyle.Render(fmt.Sprintf("Spezielle Software Installation abgeschlossen!\n"))
|
||||
}
|
||||
|
||||
spin := m.spinner.View() + " "
|
||||
prog := m.progress.View()
|
||||
info := fmt.Sprintf("Installiere %s", m.items[m.index])
|
||||
|
||||
return spin + info + " " + prog
|
||||
}
|
||||
|
||||
type installedItemMsg string
|
||||
|
||||
func (m specialSoftwareModel) installItemCmd(item string) tea.Cmd {
|
||||
return func() tea.Msg {
|
||||
switch item {
|
||||
case "oh-my-posh":
|
||||
if _, err := exec.LookPath("oh-my-posh"); err == nil {
|
||||
return installedItemMsg(item)
|
||||
}
|
||||
poshCommand := "curl -s https://ohmyposh.dev/install.sh | bash -s"
|
||||
if err := installPackage(poshCommand, "", ""); err != nil {
|
||||
log.Printf("Fehler beim Installieren von oh-my-posh: %v", err)
|
||||
}
|
||||
case "golang":
|
||||
if _, err := exec.LookPath("go"); err == nil {
|
||||
return installedItemMsg(item)
|
||||
}
|
||||
golangVersion := "1.23.4"
|
||||
if err := downloadGolang(golangVersion); err != nil {
|
||||
log.Printf("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, "", m.sudoPassword); err != nil {
|
||||
log.Printf("Fehler beim Installieren von Go: %v", err)
|
||||
}
|
||||
case "rust":
|
||||
if _, err := exec.LookPath("rustc"); err == nil {
|
||||
return installedItemMsg(item)
|
||||
}
|
||||
rustupCommand := "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -q -y"
|
||||
if err := installPackage(rustupCommand, "", ""); err != nil {
|
||||
log.Printf("Fehler beim Installieren von Rust: %v", err)
|
||||
}
|
||||
}
|
||||
return installedItemMsg(item)
|
||||
}
|
||||
}
|
||||
|
||||
var unavailablePackages []string
|
||||
|
||||
func installPackage(cmd, pkg, sudoPassword string) error {
|
||||
fullCmd := fmt.Sprintf("%s %s", cmd, pkg)
|
||||
command := exec.Command("sudo", "-S", "sh", "-c", fullCmd)
|
||||
command.Stdin = strings.NewReader(sudoPassword + "\n")
|
||||
output, err := command.CombinedOutput()
|
||||
if err != nil {
|
||||
if strings.Contains(string(output), "not found") || strings.Contains(string(output), "no matching package") || strings.Contains(string(output), "Keine Übereinstimmung") || strings.Contains(string(output), "Ziel nicht gefunden") {
|
||||
unavailablePackages = append(unavailablePackages, pkg)
|
||||
return 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 (m model) installSpecialSoftware() error {
|
||||
if _, err := exec.LookPath("oh-my-posh"); err == nil {
|
||||
fmt.Println("Oh-my-posh ist bereits installiert")
|
||||
} else {
|
||||
poshCommand := "curl -s https://ohmyposh.dev/install.sh | bash -s"
|
||||
if err := installPackage(poshCommand, "", ""); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
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, "", m.sudoPassword); 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 -s -- -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 {
|
||||
packages []string
|
||||
index int
|
||||
width int
|
||||
height int
|
||||
spinner spinner.Model
|
||||
progress progress.Model
|
||||
done bool
|
||||
sudoPassword string
|
||||
os OS
|
||||
}
|
||||
|
||||
var (
|
||||
currentPkgNameStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("211"))
|
||||
doneStyle = lipgloss.NewStyle().Margin(1, 2)
|
||||
checkMark = lipgloss.NewStyle().Foreground(lipgloss.Color("42")).SetString("✓")
|
||||
)
|
||||
|
||||
func newModel(packages []string, sudoPassword string, os *OS) model {
|
||||
p := progress.New(
|
||||
progress.WithDefaultGradient(),
|
||||
progress.WithWidth(40),
|
||||
progress.WithoutPercentage(),
|
||||
)
|
||||
s := spinner.New()
|
||||
s.Style = lipgloss.NewStyle().Foreground(lipgloss.Color("63"))
|
||||
return model{
|
||||
packages: packages,
|
||||
spinner: s,
|
||||
progress: p,
|
||||
sudoPassword: sudoPassword,
|
||||
os: *os,
|
||||
}
|
||||
}
|
||||
|
||||
func (m model) Init() tea.Cmd {
|
||||
return tea.Batch(m.installPackageCmd(m.packages[m.index]), m.spinner.Tick)
|
||||
}
|
||||
|
||||
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
switch msg := msg.(type) {
|
||||
case tea.WindowSizeMsg:
|
||||
m.width, m.height = msg.Width, msg.Height
|
||||
case tea.KeyMsg:
|
||||
switch msg.String() {
|
||||
case "ctrl+c", "esc", "q":
|
||||
return m, tea.Quit
|
||||
}
|
||||
case installedPkgMsg:
|
||||
pkg := m.packages[m.index]
|
||||
if m.index >= len(m.packages)-1 {
|
||||
m.done = true
|
||||
return m, tea.Sequence(
|
||||
tea.Printf("%s %s", checkMark, pkg),
|
||||
tea.Quit,
|
||||
)
|
||||
}
|
||||
|
||||
m.index++
|
||||
progressCmd := m.progress.SetPercent(float64(m.index) / float64(len(m.packages)))
|
||||
|
||||
return m, tea.Batch(
|
||||
progressCmd,
|
||||
tea.Printf("%s %s", checkMark, pkg),
|
||||
m.installPackageCmd(m.packages[m.index]),
|
||||
)
|
||||
case spinner.TickMsg:
|
||||
var cmd tea.Cmd
|
||||
m.spinner, cmd = m.spinner.Update(msg)
|
||||
return m, cmd
|
||||
case progress.FrameMsg:
|
||||
newModel, cmd := m.progress.Update(msg)
|
||||
if newModel, ok := newModel.(progress.Model); ok {
|
||||
m.progress = newModel
|
||||
}
|
||||
return m, cmd
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (m model) View() string {
|
||||
n := len(m.packages)
|
||||
w := lipgloss.Width(fmt.Sprintf("%d", n))
|
||||
|
||||
if m.done {
|
||||
return doneStyle.Render(fmt.Sprintf("Done! Installed %d packages.\n", n))
|
||||
}
|
||||
|
||||
pkgCount := fmt.Sprintf(" %*d/%*d", w, m.index, w, n)
|
||||
|
||||
spin := m.spinner.View() + " "
|
||||
prog := m.progress.View()
|
||||
cellsAvail := max(0, m.width-lipgloss.Width(spin+prog+pkgCount))
|
||||
|
||||
pkgName := currentPkgNameStyle.Render(m.packages[m.index])
|
||||
info := lipgloss.NewStyle().MaxWidth(cellsAvail).Render("Installing " + pkgName)
|
||||
|
||||
cellsRemaining := max(0, m.width-lipgloss.Width(spin+info+prog+pkgCount))
|
||||
gap := strings.Repeat(" ", cellsRemaining)
|
||||
|
||||
return spin + info + gap + prog + pkgCount
|
||||
}
|
||||
|
||||
type installedPkgMsg string
|
||||
|
||||
func (m model) installPackageCmd(pkg string) tea.Cmd {
|
||||
return func() tea.Msg {
|
||||
if err := installPackage(m.os.InstallCommand, pkg, m.sudoPassword); err != nil {
|
||||
log.Printf("Fehler beim Installieren von %s: %v", pkg, err)
|
||||
}
|
||||
return installedPkgMsg(pkg)
|
||||
}
|
||||
}
|
||||
|
||||
func max(a, b int) int {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
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"`
|
||||
Flatpak FlatpakConfig `mapstructure:"flatpak"`
|
||||
Dotfiles DotfilesConfig `mapstructure:"dotfiles"`
|
||||
}
|
||||
|
||||
type FlatpakConfig struct {
|
||||
Enable bool `mapstructure:"enable"`
|
||||
Remotes []Remote `mapstructure:"remotes"`
|
||||
Packages []string `mapstructure:"packages"`
|
||||
}
|
||||
|
||||
type Remote struct {
|
||||
Name string `mapstructure:"name"`
|
||||
URL string `mapstructure:"url"`
|
||||
}
|
||||
|
||||
type DotfilesConfig struct {
|
||||
Enable bool `mapstructure:"enable"`
|
||||
GitRepo string `mapstructure:"git_repo"`
|
||||
}
|
||||
|
||||
func installFlatpak(os *OS, sudoPassword string) error {
|
||||
var command string
|
||||
switch os.PackageManager {
|
||||
case "pacman":
|
||||
command = "pacman -S --noconfirm --needed flatpak"
|
||||
case "apt":
|
||||
command = "apt install -y flatpak"
|
||||
case "dnf":
|
||||
command = "dnf install -y flatpak"
|
||||
default:
|
||||
return fmt.Errorf("keine Flatpak-Installation für OS %s definiert", os.ID)
|
||||
}
|
||||
|
||||
if err := installPackage(command, "", sudoPassword); err != nil {
|
||||
return fmt.Errorf("Fehler bei der Flatpak-Installation: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func addFlatpakRemotes(remotes []Remote) error {
|
||||
for _, remote := range remotes {
|
||||
cmd := exec.Command("flatpak", "remote-add", "--if-not-exists", remote.Name, remote.URL)
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("Fehler beim Hinzufügen des Remotes %s: %v", remote.Name, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func installFlatpakPackages(packages []string) error {
|
||||
if len(packages) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Println("\nInstalliere Flatpak-Pakete...")
|
||||
p := progress.New(
|
||||
progress.WithDefaultGradient(),
|
||||
progress.WithWidth(40),
|
||||
)
|
||||
|
||||
for i, pkg := range packages {
|
||||
p.SetPercent(float64(i) / float64(len(packages)))
|
||||
fmt.Printf("Installiere Flatpak: %s\n", pkg)
|
||||
|
||||
cmd := exec.Command("flatpak", "install", "-y", pkg)
|
||||
if err := cmd.Run(); err != nil {
|
||||
log.Printf("Fehler bei der Installation von %s: %v", pkg, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setupDotfiles(config DotfilesConfig) error {
|
||||
if _, err := exec.LookPath("git"); err != nil {
|
||||
return fmt.Errorf("git ist nicht installiert")
|
||||
}
|
||||
|
||||
if _, err := exec.LookPath("stow"); err != nil {
|
||||
return fmt.Errorf("gnu stow ist nicht installiert")
|
||||
}
|
||||
|
||||
dotfilesDir := filepath.Join(os.Getenv("HOME"), "dotfiles")
|
||||
|
||||
cmd := exec.Command("git", "clone", config.GitRepo)
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("Fehler beim Klonen der Dotfiles: %v", err)
|
||||
}
|
||||
|
||||
if err := os.Chdir(dotfilesDir); err != nil {
|
||||
return fmt.Errorf("Fehler beim Wechseln in das Dotfiles-Verzeichnis: %v", err)
|
||||
}
|
||||
|
||||
// Module mit stow verlinken
|
||||
cmd = exec.Command("stow", ".")
|
||||
if err := cmd.Run(); err != nil {
|
||||
log.Printf("Fehler beim Linken: %v", err)
|
||||
}
|
||||
fmt.Printf("Alles erfolgreich verlinkt\n")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func run(cmd *cobra.Command, args []string) {
|
||||
os, err := getLinuxDistribution()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
sudoPassword, err := getSudoPassword()
|
||||
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...)
|
||||
}
|
||||
|
||||
unavailablePackages = []string{}
|
||||
|
||||
m := newModel(packages, sudoPassword, os)
|
||||
p := tea.NewProgram(m)
|
||||
if _, err := p.Run(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
printUnavailablePackages()
|
||||
|
||||
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 := installBuildEssentials(os, sudoPassword); err != nil {
|
||||
log.Printf("Warnung: %v", err)
|
||||
}
|
||||
sm := newSpecialSoftwareModel(sudoPassword)
|
||||
p = tea.NewProgram(sm)
|
||||
if _, err := p.Run(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if len(cfg.SpecialPackages.Go) > 0 || len(cfg.SpecialPackages.Cargo) > 0 || len(cfg.SpecialPackages.Pipx) > 0 {
|
||||
var allSpecialPkgs []struct {
|
||||
typ string
|
||||
name string
|
||||
command string
|
||||
}
|
||||
|
||||
for _, pkg := range cfg.SpecialPackages.Go {
|
||||
allSpecialPkgs = append(allSpecialPkgs, struct {
|
||||
typ string
|
||||
name string
|
||||
command string
|
||||
}{"go", pkg, "go install " + pkg + "@latest"})
|
||||
}
|
||||
|
||||
for _, pkg := range cfg.SpecialPackages.Cargo {
|
||||
allSpecialPkgs = append(allSpecialPkgs, struct {
|
||||
typ string
|
||||
name string
|
||||
command string
|
||||
}{"cargo", pkg, "cargo install " + pkg})
|
||||
}
|
||||
|
||||
for _, pkg := range cfg.SpecialPackages.Pipx {
|
||||
allSpecialPkgs = append(allSpecialPkgs, struct {
|
||||
typ string
|
||||
name string
|
||||
command string
|
||||
}{"pipx", pkg, "pipx install " + pkg})
|
||||
}
|
||||
|
||||
if len(allSpecialPkgs) > 0 {
|
||||
fmt.Println("\nInstalliere spezielle Pakete...")
|
||||
p := progress.New(
|
||||
progress.WithDefaultGradient(),
|
||||
progress.WithWidth(40),
|
||||
)
|
||||
|
||||
for i, pkg := range allSpecialPkgs {
|
||||
p.SetPercent(float64(i) / float64(len(allSpecialPkgs)))
|
||||
fmt.Printf("Installiere %s Paket: %s\n", pkg.typ, pkg.name)
|
||||
|
||||
cmd := exec.Command("sh", "-c", pkg.command)
|
||||
if err := cmd.Run(); err != nil {
|
||||
log.Printf("Fehler bei der Installation von %s: %v", pkg.name, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if cfg.Flatpak.Enable {
|
||||
fmt.Println("\nKonfiguriere Flatpak...")
|
||||
if err := installFlatpak(os, sudoPassword); err != nil {
|
||||
log.Printf("Warnung bei Flatpak-Installation: %v", err)
|
||||
}
|
||||
|
||||
if err := addFlatpakRemotes(cfg.Flatpak.Remotes); err != nil {
|
||||
log.Printf("Warnung bei Flatpak-Remotes: %v", err)
|
||||
}
|
||||
|
||||
if err := installFlatpakPackages(cfg.Flatpak.Packages); err != nil {
|
||||
log.Printf("Warnung bei Flatpak-Paketen: %v", err)
|
||||
}
|
||||
}
|
||||
if cfg.Dotfiles.Enable {
|
||||
fmt.Println("\nKonfiguriere Dotfiles...")
|
||||
if err := setupDotfiles(cfg.Dotfiles); err != nil {
|
||||
log.Printf("Warnung bei Dotfiles-Setup: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func printUnavailablePackages() {
|
||||
if len(unavailablePackages) > 0 {
|
||||
fmt.Println("\nFolgende Pakete waren nicht verfügbar:")
|
||||
for _, pkg := range unavailablePackages {
|
||||
fmt.Printf("- %s\n", pkg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
fmt.Println(err)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue