diff --git a/internal/dependency/dependency.go b/internal/dependency/dependency.go index cb4bb1d..6853f31 100644 --- a/internal/dependency/dependency.go +++ b/internal/dependency/dependency.go @@ -13,6 +13,7 @@ import ( "strings" "fyne.io/fyne/v2" + "fyne.io/fyne/v2/container" "fyne.io/fyne/v2/dialog" "fyne.io/fyne/v2/widget" "fyne.io/tools" @@ -97,242 +98,279 @@ func InstallDependency(index int, sudoPassword string, dependencies []Dependency switch runtime.GOOS { case "windows": - switch index { - case 0: // VSCode - cmd = tools.CommandInShell("winget", "install", "-e", "--id", - "Microsoft.VisualStudioCode") - case 1: // Docker Desktop - wslCheckCmd := tools.CommandInShell("wsl", "--status") - err := wslCheckCmd.Run() - if err != nil { - wslInstallCmd := tools.CommandInShell("wsl", "--install") - err = wslInstallCmd.Run() - if err != nil { - errMsg := fmt.Errorf("error: installing WSL: %v", err) - dialog.ShowError(errMsg, mainWindow) - log.Error(errMsg.Error()) - return - } - dialog.ShowInformation("WSL wird installiert", "WSL wird installiert. Bitte warten Sie, bis die Installation abgeschlossen ist und starten Sie die Anwendung neu.", mainWindow) - return - } - - cmd = tools.CommandInShell("winget", "install", "-e", "--id", - "Docker.DockerDesktop") + cmd, err = installWindowsDependencies(index, cmd, &mainWindow) + if err != nil { + dialog.ShowError(err, mainWindow) + log.Error(err.Error()) } case "darwin": - brewCheckCmd := tools.CommandInShell("which", "brew") - err := brewCheckCmd.Run() - if err != nil { - brewInstallCmd := tools.CommandInShell("bin/bash", "-c", "\"$(curl -fsSl https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\"") - err := brewInstallCmd.Run() - if err != nil { - errMsg := fmt.Errorf("error: installing homebrew falied: %v", err) - dialog.ShowError(errMsg, mainWindow) - log.Error(errMsg.Error()) - } - } - switch index { - case 0: // VSCode - cmd = tools.CommandInShell("brew", "install", "--cask", "visual-studio-code") - case 1: // Docker - cmd = tools.CommandInShell("brew", "install", "docker") - } + cmd, err = installDarwinDependencies(index, cmd, &mainWindow) + dialog.ShowError(err, mainWindow) + log.Error(err.Error()) case "linux": - osInfo, err := osinfo.GetLinuxDistribution() + err = installLinuxDependencies(index, sudoPassword, cmd, dependencies, &mainWindow) if err != nil { log.Error(err.Error()) - dialog.ShowError(fmt.Errorf("error getting OS info: %v", err), mainWindow) - return - } - - var downloadURL, fileName string - switch index { - case 0: // VSCode - switch osInfo.ID { - case "debian", "ubuntu", "linuxmint": - downloadURL = "https://code.visualstudio.com/sha/download?build=stable&os=linux-deb-x64" - fileName = "vscode.deb" - case "fedora": - downloadURL = "https://code.visualstudio.com/sha/download?build=stable&os=linux-rpm-x64" - fileName = "vscode.rpm" - default: - dialog.ShowInformation("Nicht unterstützt", fmt.Sprintf("Automatische Installation für dieses OS %v nicht verfügbar", osInfo), mainWindow) - return - } - go func() { - progressBar := widget.NewProgressBar() - progressDialog := dialog.NewCustomWithoutButtons("Download in progress", progressBar, mainWindow) - - progressDialog.Show() - - homeDir, _ := os.UserHomeDir() - downloadDir := filepath.Join(homeDir, "Downloads") - filePath := filepath.Join(downloadDir, fileName) - - err := download.WithProgressBar(downloadURL, filePath, progressBar) - progressDialog.Hide() - - if err != nil { - log.Error(err.Error()) - dialog.ShowError(err, mainWindow) - return - } - - var installCmd string - switch osInfo.ID { - case "debian", "ubuntu", "linuxmint": - installCmd = fmt.Sprintf("sudo -S dpkg -i %s", filePath) - case "fedora": - installCmd = fmt.Sprintf("sudo -S dnf install -y %s", filePath) - } - - cmd := exec.Command("sh", "-c", installCmd) - cmd.Stdin = strings.NewReader(sudoPassword + "\n") - - dialog.ShowInformation("Installation gestartet", - "Die Installation von VSCode wurde gestartet.", mainWindow) - - output, err := cmd.CombinedOutput() - if err != nil { - errMsg := fmt.Errorf("installation failed:\n%s", output) - dialog.ShowError(errMsg, mainWindow) - log.Error(errMsg.Error()) - return - } else { - dialog.ShowInformation("Erfolg", "VSCode erfolgreich installiert!", mainWindow) - CheckDependencies(dependencies) - } - }() - case 1: // Docker - go func() { - progressBar := widget.NewProgressBar() - progressDialog := dialog.NewCustomWithoutButtons("Docker Installation läuft...", progressBar, mainWindow) - progressDialog.Show() - - osInfo, err := osinfo.GetLinuxDistribution() - if err != nil { - progressDialog.Hide() - errMsg := fmt.Errorf("error getting os infos: %v", err) - dialog.ShowError(errMsg, mainWindow) - log.Error(errMsg.Error()) - return - } - - var commands []string - var cleanupCommands []string - var totalSteps int - - switch osInfo.ID { - case "ubuntu", "linuxmint", "debian": - // Ubuntu/Debian Commands - distroPath := "ubuntu" - codeName := "$(. /etc/os-release && echo \"${UBUNTU_CODENAME:-$VERSION_CODENAME}\")" - if osInfo.ID == "debian" { - distroPath = "debian" - codeName = "$(. /etc/os-release && echo \"$VERSION_CODENAME\")" - } - arch := "$(dpkg --print-architecture)" - - commands = []string{ - "apt-get update", - "apt-get install -y wget", - fmt.Sprintf("wget -qO- https://download.docker.com/linux/%s/dists/%s/pool/stable/%s/ | grep -oP 'href=\"\\K[^\"]*(?=.*deb)' | xargs -I{} wget https://download.docker.com/linux/%s/dists/%s/pool/stable/%s/{}", - distroPath, codeName, arch, distroPath, codeName, arch), - "dpkg -i ./containerd.io*.deb docker-ce*.deb docker-ce-cli*.deb docker-buildx-plugin*.deb docker-compose-plugin*.deb", - "apt-get install -f -y", - "service docker start", - } - - cleanupCommands = []string{ - "rm -rf ./containerd.io*.deb ./docker-ce*.deb ./docker-ce-cli*.deb ./docker-buildx-plugin*.deb ./docker-compose-plugin*.deb", - "apt-get autoremove -y", - "apt-get clean", - } - - totalSteps = len(commands) + len(cleanupCommands) - - case "fedora": - // Fedora Commands - fedoraVer := "$(rpm -E %fedora)" - commands = []string{ - "dnf install -y wget", - fmt.Sprintf("wget https://download.docker.com/linux/fedora/%s/x86_64/stable/Packages/containerd-*.rpm docker-*.rpm docker-ce-*.rpm", fedoraVer), - "dnf install -y ./*.rpm", - "systemctl enable --now docker", - } - - cleanupCommands = []string{ - "rm -rf ./containerd-*.rpm ./docker-*.rpm ./docker-ce-*.rpm", - "dnf autoremove -y", - "dnf clean all", - } - - totalSteps = len(commands) + len(cleanupCommands) - - default: - progressDialog.Hide() - dialog.ShowInformation("not supported", "Automatic Docker installation not supported for your OS.", mainWindow) - return - } - - progressStep := 1.0 / float64(totalSteps) - currentProgress := 0.0 - - for _, cmd := range commands { - command := exec.Command("sudo", "-S", "sh", "-c", cmd) - command.Stdin = strings.NewReader(sudoPassword + "\n") - - if output, err := command.CombinedOutput(); err != nil { - progressDialog.Hide() - errMsg := fmt.Errorf("error at %s:\n%s", cmd, output) - dialog.ShowError(errMsg, mainWindow) - log.Error(errMsg.Error()) - return - } - - currentProgress += progressStep - progressBar.SetValue(currentProgress) - } - - for _, cmd := range cleanupCommands { - command := exec.Command("sudo", "-S", "sh", "-c", cmd) - command.Stdin = strings.NewReader(sudoPassword + "\n") - - if output, err := command.CombinedOutput(); err != nil { - progressDialog.Hide() - errMsg := fmt.Errorf("cleanup error at %s:\n%s", cmd, output) - dialog.ShowError(errMsg, mainWindow) - log.Error(errMsg.Error()) - return - } - - currentProgress += progressStep - progressBar.SetValue(currentProgress) - } - - progressDialog.Hide() - - dialog.ShowInformation( - "Installation finished", - "Docker was succesfully installed! Please re-start your system to let the changes take effect.", - mainWindow, - ) - - CheckDependencies(dependencies) - }() + dialog.ShowError(err, mainWindow) } } if cmd != nil { - err = cmd.Start() - if err != nil { - errMsg := fmt.Errorf("error starting installation process: %v", err) - dialog.ShowError(errMsg, mainWindow) - log.Error(errMsg.Error()) - } else { - dialog.ShowInformation("Installation started", - fmt.Sprintf("Installation of %s was started", depName), mainWindow) - } + showInstallProgressBar(mainWindow, fmt.Sprintf("installing %v", depName), cmd, depName, dependencies) } } + +func installWindowsDependencies(index int, cmd *exec.Cmd, mainWindow *fyne.Window) (*exec.Cmd, error) { + switch index { + case 0: // VSCode + cmd = tools.CommandInShell("winget", "install", "-e", "--id", + "Microsoft.VisualStudioCode") + case 1: // Docker Desktop + wslCheckCmd := tools.CommandInShell("wsl", "--status") + err := wslCheckCmd.Run() + if err != nil { + wslInstallCmd := tools.CommandInShell("wsl", "--install", "ubuntu") + dialog.ShowInformation("WSL wird installiert", "WSL wird installiert. Bitte warten Sie, bis die Installation abgeschlossen ist und starten Sie die Anwendung neu.", *mainWindow) + err = wslInstallCmd.Run() + if err != nil { + errMsg := fmt.Errorf("error: installing WSL: %v", err) + return nil, errMsg + } + return nil, err + } + + cmd = tools.CommandInShell("winget", "install", "-e", "--id", + "Docker.DockerDesktop") + } + return cmd, nil +} + +func installDarwinDependencies(index int, cmd *exec.Cmd, mainWindow *fyne.Window) (*exec.Cmd, error) { + brewCheckCmd := tools.CommandInShell("which", "brew") + err := brewCheckCmd.Run() + if err != nil { + brewInstallCmd := tools.CommandInShell("bin/bash", "-c", "\"$(curl -fsSl https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\"") + err := brewInstallCmd.Run() + if err != nil { + errMsg := fmt.Errorf("error: installing homebrew falied: %v", err) + return nil, errMsg + } + } + switch index { + case 0: // VSCode + cmd = tools.CommandInShell("brew", "install", "--cask", "visual-studio-code") + case 1: // Docker + cmd = tools.CommandInShell("brew", "install", "docker") + } + return cmd, nil +} + +func installLinuxDependencies(index int, sudoPassword string, cmd *exec.Cmd, dependencies []Dependency, mainWindow *fyne.Window) error { + osInfo, err := osinfo.GetLinuxDistribution() + if err != nil { + errMsg := fmt.Errorf("error getting OS info: %v", err) + return errMsg + } + + var downloadURL, fileName string + switch index { + case 0: // VSCode + switch osInfo.ID { + case "debian", "ubuntu", "linuxmint": + downloadURL = "https://code.visualstudio.com/sha/download?build=stable&os=linux-deb-x64" + fileName = "vscode.deb" + case "fedora": + downloadURL = "https://code.visualstudio.com/sha/download?build=stable&os=linux-rpm-x64" + fileName = "vscode.rpm" + default: + dialog.ShowInformation("Nicht unterstützt", fmt.Sprintf("Automatische Installation für dieses OS %v nicht verfügbar", osInfo), *mainWindow) + return nil + } + go func() { + progressBar := widget.NewProgressBar() + progressDialog := dialog.NewCustomWithoutButtons("Download in progress", progressBar, *mainWindow) + + progressDialog.Show() + + homeDir, _ := os.UserHomeDir() + downloadDir := filepath.Join(homeDir, "Downloads") + filePath := filepath.Join(downloadDir, fileName) + + err := download.WithProgressBar(downloadURL, filePath, progressBar) + progressDialog.Hide() + + if err != nil { + log.Error(err.Error()) + dialog.ShowError(err, *mainWindow) + return + } + + var installCmd string + switch osInfo.ID { + case "debian", "ubuntu", "linuxmint": + installCmd = fmt.Sprintf("sudo -S dpkg -i %s", filePath) + case "fedora": + installCmd = fmt.Sprintf("sudo -S dnf install -y %s", filePath) + } + + cmd := exec.Command("sh", "-c", installCmd) + cmd.Stdin = strings.NewReader(sudoPassword + "\n") + + dialog.ShowInformation("Installation started", + "Installation of VSCode started.", *mainWindow) + + output, err := cmd.CombinedOutput() + if err != nil { + errMsg := fmt.Errorf("installation failed:\n%s", output) + dialog.ShowError(errMsg, *mainWindow) + log.Error(errMsg.Error()) + return + } else { + dialog.ShowInformation("Success", "VSCode succesfully installed!", *mainWindow) + CheckDependencies(dependencies) + } + }() + case 1: // Docker + go func() { + progressBar := widget.NewProgressBar() + progressDialog := dialog.NewCustomWithoutButtons("Docker installation in progress...", progressBar, *mainWindow) + progressDialog.Show() + + osInfo, err := osinfo.GetLinuxDistribution() + if err != nil { + progressDialog.Hide() + errMsg := fmt.Errorf("error getting os infos: %v", err) + dialog.ShowError(errMsg, *mainWindow) + log.Error(errMsg.Error()) + return + } + + var commands []string + var cleanupCommands []string + var totalSteps int + + switch osInfo.ID { + case "ubuntu", "linuxmint", "debian": + // Ubuntu/Debian Commands + distroPath := "ubuntu" + codeName := "$(. /etc/os-release && echo \"${UBUNTU_CODENAME:-$VERSION_CODENAME}\")" + if osInfo.ID == "debian" { + distroPath = "debian" + codeName = "$(. /etc/os-release && echo \"$VERSION_CODENAME\")" + } + arch := "$(dpkg --print-architecture)" + + commands = []string{ + "apt-get update", + "apt-get install -y wget", + fmt.Sprintf("wget -qO- https://download.docker.com/linux/%s/dists/%s/pool/stable/%s/ | grep -oP 'href=\"\\K[^\"]*(?=.*deb)' | xargs -I{} wget https://download.docker.com/linux/%s/dists/%s/pool/stable/%s/{}", + distroPath, codeName, arch, distroPath, codeName, arch), + "dpkg -i ./containerd.io*.deb docker-ce*.deb docker-ce-cli*.deb docker-buildx-plugin*.deb docker-compose-plugin*.deb", + "apt-get install -f -y", + "service docker start", + } + + cleanupCommands = []string{ + "rm -rf ./containerd.io*.deb ./docker-ce*.deb ./docker-ce-cli*.deb ./docker-buildx-plugin*.deb ./docker-compose-plugin*.deb", + "apt-get autoremove -y", + "apt-get clean", + } + + totalSteps = len(commands) + len(cleanupCommands) + + case "fedora": + fedoraVer := "$(rpm -E %fedora)" + commands = []string{ + "dnf install -y wget", + fmt.Sprintf("wget https://download.docker.com/linux/fedora/%s/x86_64/stable/Packages/containerd-*.rpm docker-*.rpm docker-ce-*.rpm", fedoraVer), + "dnf install -y ./*.rpm", + "systemctl enable --now docker", + } + + cleanupCommands = []string{ + "rm -rf ./containerd-*.rpm ./docker-*.rpm ./docker-ce-*.rpm", + "dnf autoremove -y", + "dnf clean all", + } + + totalSteps = len(commands) + len(cleanupCommands) + + default: + progressDialog.Hide() + dialog.ShowInformation("not supported", "Automatic Docker installation not supported for your OS.", *mainWindow) + return + } + + progressStep := 1.0 / float64(totalSteps) + currentProgress := 0.0 + + for _, cmd := range commands { + command := exec.Command("sudo", "-S", "sh", "-c", cmd) + command.Stdin = strings.NewReader(sudoPassword + "\n") + + if output, err := command.CombinedOutput(); err != nil { + progressDialog.Hide() + errMsg := fmt.Errorf("error at %s:\n%s", cmd, output) + dialog.ShowError(errMsg, *mainWindow) + log.Error(errMsg.Error()) + return + } + + currentProgress += progressStep + progressBar.SetValue(currentProgress) + } + + for _, cmd := range cleanupCommands { + command := exec.Command("sudo", "-S", "sh", "-c", cmd) + command.Stdin = strings.NewReader(sudoPassword + "\n") + + if output, err := command.CombinedOutput(); err != nil { + progressDialog.Hide() + errMsg := fmt.Errorf("cleanup error at %s:\n%s", cmd, output) + dialog.ShowError(errMsg, *mainWindow) + log.Error(errMsg.Error()) + return + } + + currentProgress += progressStep + progressBar.SetValue(currentProgress) + } + + progressDialog.Hide() + + dialog.ShowInformation( + "Installation finished", + "Docker was succesfully installed! Please re-start your system to let the changes take effect.", + *mainWindow, + ) + + CheckDependencies(dependencies) + }() + } + return nil +} + +func showInstallProgressBar(window fyne.Window, message string, cmd *exec.Cmd, depName string, dependencies []Dependency) { + progress := widget.NewProgressBarInfinite() + content := container.NewVBox( + widget.NewLabel(message), + progress, + ) + popup := widget.NewModalPopUp(content, window.Canvas()) + popup.Show() + + go func() { + err := cmd.Run() + + popup.Hide() + + if err != nil { + errMsg := fmt.Errorf("error during installation of %s: %v", depName, err) + dialog.ShowError(errMsg, window) + log.Error(errMsg.Error()) + } else { + dialog.ShowInformation("Installation finished", fmt.Sprintf("%s successfully installed!", depName), window) + CheckDependencies(dependencies) + } + }() +} diff --git a/internal/gui/dependency_screen.go b/internal/gui/dependency_screen.go index d6921ac..a6b7eb3 100644 --- a/internal/gui/dependency_screen.go +++ b/internal/gui/dependency_screen.go @@ -64,7 +64,7 @@ func ShowDependencyScreen() { ShowProjectScreen() } else { dialog.ShowInformation("dependencies missing", - "Bitte installieren Sie alle erforderlichen Abhängigkeiten, bevor Sie fortfahren.", mainWindow) + "Please install all required dependencies before proceeding.", mainWindow) } })