package main import ( "fmt" "log" "os/exec" "runtime" "strings" "github.com/charmbracelet/bubbles/progress" "github.com/charmbracelet/bubbles/spinner" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" ) type model struct { packages []string index int width int height int spinner spinner.Model progress progress.Model done bool sudoPassword string os OS } 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 } 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 } 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) } }