From 0de044d9490ad60c94ca69717dc81a118343b5d7 Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Thu, 28 Nov 2024 19:09:54 -0800 Subject: [PATCH] almost done --- checks/hardware.go | 16 +++++ cmd/hw-check.go | 15 +---- cmd/root.go | 4 +- cmd/update.go | 142 +++++++++++++++++++++++++++++++++++++-------- drv/bootc.go | 8 +-- drv/brew.go | 18 +++--- drv/flatpak.go | 15 ----- lib/session.go | 16 ++++- main.go | 14 ++++- 9 files changed, 179 insertions(+), 69 deletions(-) delete mode 100644 drv/flatpak.go diff --git a/checks/hardware.go b/checks/hardware.go index d27c340..d0b77ce 100644 --- a/checks/hardware.go +++ b/checks/hardware.go @@ -183,3 +183,19 @@ func cpu() Info { nil, } } + +func RunHwChecks() error { + // (some hardware checks require dbus access) + conn, err := dbus.SystemBus() + if err != nil { + return err + } + defer conn.Close() + checkInfo := Hardware(conn) + for _, info := range checkInfo { + if info.Err != nil { + return fmt.Errorf("%s, returned error: %v", info.Name, info.Err) + } + } + return nil +} diff --git a/cmd/hw-check.go b/cmd/hw-check.go index 66efdba..2b8d28e 100644 --- a/cmd/hw-check.go +++ b/cmd/hw-check.go @@ -1,25 +1,16 @@ package cmd import ( - "log" - "github.com/gerblesh/update-next/checks" - "github.com/godbus/dbus/v5" "github.com/spf13/cobra" + "log" ) func HwCheck(cmd *cobra.Command, args []string) { // (some hardware checks require dbus access) - conn, err := dbus.SystemBus() + err := checks.RunHwChecks() if err != nil { - log.Fatalf("Failed to connect to session bus: %v", err) - } - defer conn.Close() - checkInfo := checks.Hardware(conn) - for _, info := range checkInfo { - if info.Err != nil { - log.Fatalf("Hardware checks failed for %s, returned error: %v", info.Name, info.Err) - } + log.Fatalf("Hardware checks failed: %v", err) } log.Println("Hardware checks passed") } diff --git a/cmd/root.go b/cmd/root.go index 66122ca..c493f39 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -15,8 +15,7 @@ var ( waitCmd = &cobra.Command{ Use: "wait", - Short: "Wait for a condition or time period", - Long: "This command simulates waiting for some event or process to complete.", + Short: "Waits for ostree sysroot to unlock", Run: Wait, } @@ -44,4 +43,5 @@ func init() { rootCmd.AddCommand(waitCmd) rootCmd.AddCommand(updateCheckCmd) rootCmd.AddCommand(hardwareCheckCmd) + rootCmd.Flags().BoolP("hw-check", "c", false, "run hardware check before running updates") } diff --git a/cmd/update.go b/cmd/update.go index b377546..b9efa12 100644 --- a/cmd/update.go +++ b/cmd/update.go @@ -1,54 +1,150 @@ package cmd import ( + "fmt" + "log" + "os/exec" + "strings" + "github.com/gerblesh/update-next/checks" "github.com/gerblesh/update-next/drv" "github.com/gerblesh/update-next/lib" - "github.com/godbus/dbus/v5" - // "github.com/schollz/progressbar/v3" "github.com/spf13/cobra" - "log" ) -func Update(cmd *cobra.Command, args []string) { +type Failure struct { + Err error + Output string +} - // (some hardware checks require dbus access) - conn, err := dbus.SystemBus() +func Update(cmd *cobra.Command, args []string) { + hwCheck, err := cmd.Flags().GetBool("hw-check") if err != nil { - log.Fatalf("Failed to connect to session bus: %v", err) + log.Fatalf("Failed to get hw-check flag: %v", err) } - defer conn.Close() - checkInfo := checks.Hardware(conn) - for _, info := range checkInfo { - if info.Err != nil { - log.Fatalf("Hardware checks failed for %s, returned error: %v", info.Name, info.Err) + + if hwCheck { + err := checks.RunHwChecks() + if err != nil { + log.Fatalf("Hardware checks failed: %v", err) } + log.Println("Hardware checks passed") } - log.Println("Hardware checks passed") + users, err := lib.ListUsers() + if err != nil { + log.Fatalf("Failed to list users") + } + // Check if bootc update is available updateAvailable, err := drv.CheckForUpdate() + bootcUpdate := 0 + if updateAvailable { + bootcUpdate = 1 + } + + // Check if brew is installed + brewUid, brewErr := drv.GetBrewUID() + brewUpdate := 0 + if brewErr == nil { + brewUpdate = 1 + } + + totalUpdates := brewUpdate + bootcUpdate + 1 + len(users) + 1 + len(users) // Bootc + Brew + Flatpak (users + root) + Distrobox (users + root) + currentUpdate := 0 if err != nil { log.Fatalf("Failed to check for image updates: %v", err) } - log.Printf("update available: %v", updateAvailable) + failures := make(map[string]Failure) + if updateAvailable { - err = drv.BootcUpdate() + currentUpdate++ + log.Printf("[%d/%d] Updating System (Bootc)", currentUpdate, totalUpdates) + out, err := drv.BootcUpdate() if err != nil { - log.Fatalf("Failed to update system: %v", err) + failures["Bootc"] = Failure{ + err, + string(out), + } } } - err = drv.BrewUpdate() - if err != nil { - log.Fatalf("Failed to update brew: %v", err) + + if brewUpdate == 1 { + currentUpdate++ + log.Printf("[%d/%d] Updating CLI Apps (Brew)", currentUpdate, totalUpdates) + out, err := drv.BrewUpdate(brewUid) + if err != nil { + failures["Brew"] = Failure{ + err, + string(out), + } + } } - users, err := lib.ListUsers() + // Run flatpak updates + currentUpdate++ + log.Printf("[%d/%d] Updating System Apps (Flatpak)", currentUpdate, totalUpdates) + flatpakCmd := exec.Command("/usr/bin/flatpak", "update", "-y") + out, err := flatpakCmd.CombinedOutput() if err != nil { - log.Fatalf("ERROR: %v", err) + failures["Flatpak"] = Failure{ + err, + string(out), + } } - err = drv.FlatpakUpdate(users) + for _, user := range users { + currentUpdate++ + log.Printf("[%d/%d] Updating User Apps for user: %s (Flatpak)", currentUpdate, totalUpdates, 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), + } + } + } + + // Run distrobox updates + log.Printf("[%d/%d] Updating System Distroboxes", currentUpdate, totalUpdates) + currentUpdate++ + // distrobox doesn't support sudo, run with systemd-run + out, err = lib.RunUID(0, []string{"/usr/bin/distrobox", "upgrade", "-a"}, nil) if err != nil { - log.Fatalf("Failed to update flatpak: %v", err) + failures["Distrobox"] = Failure{ + err, + string(out), + } + } + for _, user := range users { + currentUpdate++ + log.Printf("[%d/%d] Updating User Distroboxes: %s", currentUpdate, totalUpdates, user.Name) + out, err := lib.RunUID(user.UID, []string{"/usr/bin/distrobox", "upgrade", "-a"}, nil) + if err != nil { + failures[fmt.Sprintf("Distrobox User: %s", user.Name)] = Failure{ + err, + string(out), + } + } } + if len(failures) > 0 { + failedSystemsList := make([]string, 0, len(failures)) + for systemName := range failures { + failedSystemsList = append(failedSystemsList, systemName) + } + failedSystemsStr := strings.Join(failedSystemsList, ", ") + lib.Notify("Updates failed", fmt.Sprintf("ublue-upd failed to update: %s", failedSystemsStr)) + + log.Printf("Update 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) + } + } } diff --git a/drv/bootc.go b/drv/bootc.go index 01a57ac..a8df3b2 100644 --- a/drv/bootc.go +++ b/drv/bootc.go @@ -5,13 +5,13 @@ import ( "strings" ) -func BootcUpdate() error { +func BootcUpdate() ([]byte, error) { cmd := exec.Command("/usr/bin/bootc", "upgrade") - err := cmd.Run() + out, err := cmd.CombinedOutput() if err != nil { - return err + return out, err } - return nil + return out, nil } func CheckForUpdate() (bool, error) { diff --git a/drv/brew.go b/drv/brew.go index 7bb4104..52dfd51 100644 --- a/drv/brew.go +++ b/drv/brew.go @@ -15,7 +15,7 @@ var ( brewPath = fmt.Sprintf("%s/bin/brew", brewPrefix) ) -func getBrewUID() (int, error) { +func GetBrewUID() (int, error) { inf, err := os.Stat(brewPrefix) if err != nil { log.Printf("Unable to stat: %v, got error: %v", brewPrefix, err) @@ -32,25 +32,21 @@ func getBrewUID() (int, error) { return int(stat.Uid), nil } -func BrewUpdate() error { - uid, err := getBrewUID() - if err != nil { - return err - } +func BrewUpdate(uid int) ([]byte, error) { env := map[string]string{ "HOMEBREW_PREFIX": brewPrefix, "HOMEBREW_REPOSITORY": brewRepo, "HOMEBREW_CELLAR": brewCellar, } - _, err = lib.RunUID(uid, []string{brewPath, "update"}, env) + out, err := lib.RunUID(uid, []string{brewPath, "update"}, env) if err != nil { - return err + return out, err } - _, err = lib.RunUID(uid, []string{brewPath, "upgrade"}, env) + out, err = lib.RunUID(uid, []string{brewPath, "upgrade"}, env) if err != nil { - return err + return out, err } - return nil + return out, nil } diff --git a/drv/flatpak.go b/drv/flatpak.go deleted file mode 100644 index 25182e6..0000000 --- a/drv/flatpak.go +++ /dev/null @@ -1,15 +0,0 @@ -package drv - -import ( - "github.com/gerblesh/update-next/lib" - "os/exec" -) - -func FlatpakUpdate(users []lib.User) error { - cmd := exec.Command("/usr/bin/flatpak", "update") - _, err := cmd.Output() - if err != nil { - return err - } - return nil -} diff --git a/lib/session.go b/lib/session.go index 25b6e4a..3791c83 100644 --- a/lib/session.go +++ b/lib/session.go @@ -15,12 +15,14 @@ func RunUID(uid int, command []string, env map[string]string) ([]byte, error) { // Just fork systemd-run, using the systemd API gave me a massive headache cmdArgs := []string{ "/usr/bin/systemd-run", - "--user", "--machine", fmt.Sprintf("%d@", uid), "--pipe", "--quiet", } + if uid != 0 { + cmdArgs = append(cmdArgs, "--user") + } cmdArgs = append(cmdArgs, command...) cmd := exec.Command(cmdArgs[0], cmdArgs[1:]...) @@ -67,3 +69,15 @@ func ListUsers() ([]User, error) { } return users, nil } + +func Notify(summary string, body string) error { + users, err := ListUsers() + if err != nil { + return err + } + for _, user := range users { + // we don't care if these exit + RunUID(user.UID, []string{"/usr/bin/notify-send", "--app-name", "ublue-upd", summary, body}, nil) + } + return nil +} diff --git a/main.go b/main.go index af82a36..40c5ee0 100644 --- a/main.go +++ b/main.go @@ -1,7 +1,19 @@ package main -import "github.com/gerblesh/update-next/cmd" +import ( + "github.com/gerblesh/update-next/cmd" + "log" + "os/user" +) func main() { + log.SetFlags(0) + currentUser, err := user.Current() + if err != nil { + log.Fatalf("Error fetching current user: %v", err) + } + if currentUser.Uid != "0" { + log.Fatalf("ublue-upd needs to be invoked as root.") + } cmd.Execute() }