Skip to content

Commit

Permalink
feat: make updates extendable and modular + verbose/dryrun implentata…
Browse files Browse the repository at this point in the history
…tion
  • Loading branch information
tulilirockz committed Dec 4, 2024
1 parent aaaeeb3 commit 7114564
Show file tree
Hide file tree
Showing 9 changed files with 456 additions and 162 deletions.
3 changes: 2 additions & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,5 +73,6 @@ func init() {
isTerminal := term.IsTerminal(int(os.Stdout.Fd()))
rootCmd.Flags().BoolP("no-progress", "p", !isTerminal, "Do not show progress bars")
rootCmd.Flags().BoolP("hw-check", "c", false, "Run hardware check before running updates")
rootCmd.Flags().BoolP("dry-run", "n", false, "Do a dry run (used for testing)")
rootCmd.Flags().BoolP("dry-run", "n", false, "Do a dry run")
rootCmd.Flags().BoolP("verbose", "v", false, "Display command outputs after run")
}
220 changes: 89 additions & 131 deletions cmd/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,38 +3,21 @@ package cmd
import (
"fmt"
"log"
"os/exec"
"strings"

"github.com/jedib0t/go-pretty/v6/progress"
"github.com/spf13/cobra"
"github.com/ublue-os/uupd/checks"
"github.com/ublue-os/uupd/drv"
"github.com/ublue-os/uupd/lib"
"gopkg.in/yaml.v3"
)

type Failure struct {
Err error
Output string
}

func Update(cmd *cobra.Command, args []string) {
lock, err := lib.AcquireLock()
if err != nil {
log.Fatalf("%v, is uupd already running?", err)
}
systemDriver, err := drv.GetSystemUpdateDriver()
if err != nil {
log.Fatalf("Failed to get system update driver: %v", err)
}
outdated, err := systemDriver.ImageOutdated()
if err != nil {
log.Fatalf("Unable to determine if image is outdated: %v", err)
}
if outdated {
lib.Notify("System Warning", "There hasn't been an update in over a month. Consider rebooting or running updates manually")
log.Printf("There hasn't been an update in over a month. Consider rebooting or running updates manually")
}
defer lib.ReleaseLock(lock)

hwCheck, err := cmd.Flags().GetBool("hw-check")
if err != nil {
Expand All @@ -44,6 +27,10 @@ func Update(cmd *cobra.Command, args []string) {
if err != nil {
log.Fatalf("Failed to get dry-run flag: %v", err)
}
verboseRun, err := cmd.Flags().GetBool("verbose")
if err != nil {
log.Fatalf("Failed to get verbose flag: %v", err)
}

if hwCheck {
err := checks.RunHwChecks()
Expand All @@ -58,153 +45,124 @@ func Update(cmd *cobra.Command, args []string) {
log.Fatalf("Failed to list users")
}

// Check if system update is available
log.Printf("Checking for system updates (%s)", systemDriver.Name)
updateAvailable, err := systemDriver.UpdateAvailable()
// ignore error on dry run
if err != nil && !dryRun {
log.Fatalf("Failed to check for image updates: %v", err)
systemUpdater, err := drv.SystemUpdater{}.New(dryRun)
if err != nil {
systemUpdater.Config.Enabled = false
} else {
systemUpdater.Check()
}

brewUpdater, err := drv.BrewUpdater{}.New(dryRun)
if err != nil {
brewUpdater.Config.Enabled = false
}
log.Printf("System updates available: %t (%s)", updateAvailable, systemDriver.Name)
// don't update system if there's a dry run
updateAvailable = updateAvailable && !dryRun
systemUpdate := 0
if updateAvailable {
systemUpdate = 1

flatpakUpdater, err := drv.FlatpakUpdater{}.New(dryRun)
if err != nil {
flatpakUpdater.Config.Enabled = false
}
flatpakUpdater.SetUsers(users)

// Check if brew is installed
brewUid, brewErr := drv.GetBrewUID()
brewUpdate := 0
if brewErr == nil {
brewUpdate = 1
distroboxUpdater, err := drv.DistroboxUpdater{}.New(dryRun)
if err != nil {
distroboxUpdater.Config.Enabled = false
}
distroboxUpdater.SetUsers(users)

totalSteps := brewUpdate + systemUpdate + 1 + len(users) + 1 + len(users) // system + Brew + Flatpak (users + root) + Distrobox (users + root)
totalSteps := brewUpdater.Steps() + systemUpdater.Steps() + flatpakUpdater.Steps() + distroboxUpdater.Steps()
pw := lib.NewProgressWriter()
pw.SetNumTrackersExpected(1)
pw.SetAutoStop(false)

noProgress, err := cmd.Flags().GetBool("no-progress")
progressEnabled, err := cmd.Flags().GetBool("no-progress")
if err != nil {
log.Fatalf("Failed to get no-progress flag: %v", err)
}
// Move this to its actual boolean value (~no-progress)
progressEnabled = !progressEnabled

if !noProgress {
if progressEnabled {
go pw.Render()
}
// move this to its actual boolean value (progress bar = false)
noProgress = !noProgress

// -1 because 0 index
tracker := lib.NewIncrementTracker(&progress.Tracker{Message: "Updating", Units: progress.UnitsDefault, Total: int64(totalSteps - 1)}, totalSteps-1)
pw.AppendTracker(tracker.Tracker)

failures := make(map[string]Failure)
var trackerConfig = &drv.TrackerConfiguration{
Tracker: tracker,
Writer: &pw,
Progress: progressEnabled,
}
flatpakUpdater.Tracker = trackerConfig
distroboxUpdater.Tracker = trackerConfig

if updateAvailable {
lib.ChangeTrackerMessageFancy(pw, tracker, noProgress, "Updating System")
out, err := systemDriver.Update()
if err != nil {
failures[systemDriver.Name] = Failure{
err,
string(out),
}
tracker.IncrementSectionError()
} else {
tracker.IncrementSection()
}
var outputs = []drv.CommandOutput{}

if systemUpdater.Outdated {
lib.Notify("System Warning", "There hasn't been an update in over a month. Consider rebooting or running updates manually")
log.Printf("There hasn't been an update in over a month. Consider rebooting or running updates manually")
}

if brewUpdate == 1 {
lib.ChangeTrackerMessageFancy(pw, tracker, noProgress, "Updating CLI apps (Brew)")
out, err := drv.BrewUpdate(brewUid)
if err != nil {
failures["Brew"] = Failure{
err,
string(out),
}
tracker.IncrementSectionError()
} else {
tracker.IncrementSection()
}
if systemUpdater.Config.Enabled {
lib.ChangeTrackerMessageFancy(pw, tracker, progressEnabled, fmt.Sprintf("Updating %s (%s)", systemUpdater.Config.Description, systemUpdater.Config.Title))
out, err := systemUpdater.Update()
outputs = append(outputs, *out...)
tracker.IncrementSection(err)
}

// Run flatpak updates
lib.ChangeTrackerMessageFancy(pw, tracker, noProgress, "Updating System Apps (Flatpak)")
flatpakCmd := exec.Command("/usr/bin/flatpak", "update", "-y")
out, err := flatpakCmd.CombinedOutput()
if err != nil {
failures["Flatpak"] = Failure{
err,
string(out),
}
tracker.IncrementSectionError()
} else {
tracker.IncrementSection()
if brewUpdater.Config.Enabled {
lib.ChangeTrackerMessageFancy(pw, tracker, progressEnabled, fmt.Sprintf("Updating %s (%s)", brewUpdater.Config.Description, brewUpdater.Config.Title))
out, err := brewUpdater.Update()
outputs = append(outputs, *out...)
tracker.IncrementSection(err)
}
for _, user := range users {
lib.ChangeTrackerMessageFancy(pw, tracker, noProgress, fmt.Sprintf("Updating Apps for User: %s (Flatpak)", user.Name))
out, err := lib.RunUID(user.UID, []string{"/usr/bin/flatpak", "update", "-y"}, nil)
if err != nil {
failures[fmt.Sprintf("Flatpak User: %s", user.Name)] = Failure{
err,
string(out),
}
tracker.IncrementSectionError()
} else {
tracker.IncrementSection()
}

if flatpakUpdater.Config.Enabled {
out, err := flatpakUpdater.Update()
outputs = append(outputs, *out...)
tracker.IncrementSection(err)
}

// Run distrobox updates
lib.ChangeTrackerMessageFancy(pw, tracker, noProgress, "Updating System Distroboxes")
// distrobox doesn't support sudo, run with systemd-run
out, err = lib.RunUID(0, []string{"/usr/bin/distrobox", "upgrade", "-a"}, nil)
if err != nil {
failures["Distrobox"] = Failure{
err,
string(out),
}
tracker.IncrementSectionError()
} else {
tracker.IncrementSection()
if distroboxUpdater.Config.Enabled {
out, err := distroboxUpdater.Update()
outputs = append(outputs, *out...)
tracker.IncrementSection(err)
}
for _, user := range users {
lib.ChangeTrackerMessageFancy(pw, tracker, noProgress, fmt.Sprintf("Updating Distroboxes for User: %s", user.Name))
out, err := lib.RunUID(user.UID, []string{"/usr/bin/distrobox", "upgrade", "-a"}, nil)

pw.Stop()

if verboseRun {
log.Println("Verbose run requested")

b, err := yaml.Marshal(outputs)
if err != nil {
failures[fmt.Sprintf("Distrobox User: %s", user.Name)] = Failure{
err,
string(out),
}
tracker.IncrementSectionError()
} else {
tracker.IncrementSection()
log.Fatalf("Failure!")
return
}
log.Printf("%s", string(b))
return
}

pw.Stop()
if len(failures) > 0 {
failedSystemsList := make([]string, 0, len(failures))
for systemName := range failures {
failedSystemsList = append(failedSystemsList, systemName)
var failures = []drv.CommandOutput{}
for _, output := range outputs {
if output.Failure {
failures = append(failures, output)
}
failedSystemsStr := strings.Join(failedSystemsList, ", ")
lib.Notify("Updates failed", fmt.Sprintf("uupd failed to update: %s, consider seeing logs with `journalctl -exu uupd.service`", failedSystemsStr))
log.Printf("Updates Completed with Failures:")
for name, fail := range failures {
indentedOutput := "\t | "
lines := strings.Split(fail.Output, "\n")
for i, line := range lines {
if i > 0 {
indentedOutput += "\n\t | "
}
indentedOutput += line
}
log.Printf("---> %s \n\t | Failure error: %v \n\t | Command Output: \n%s", name, fail.Err, indentedOutput)
}

if len(failures) > 0 {
log.Println("Exited with failed updates.\nFailures found:")

b, err := yaml.Marshal(failures)
if err != nil {
log.Fatalf("Failure!")
return
}
log.Printf("%s", string(b))

return
}

log.Printf("Updates Completed")
lib.ReleaseLock(lock)
}
Loading

0 comments on commit 7114564

Please sign in to comment.