Skip to content

Commit

Permalink
feat: accent color support for the progress bar (#34)
Browse files Browse the repository at this point in the history
* feat: add env support + verbose logging to all drivers

* fix: invalid boolean comparsions w/ maps

* refactor: use inner configuration for environment changes

* feat: OSC escape sequence progress bar status support

* fix: ensure OSC is being printed on non-tty context

* feat: add accent color support to the progress bar

* fix: correctly disable colors and interactive progress if not in interactive progress mode

* feat: use accent color portal to get proper RGB color + translate it to ANSI

* docs: remove outdated comment

---------

Co-authored-by: Gareth Widlansky <[email protected]>
  • Loading branch information
tulilirockz and gerblesh authored Dec 13, 2024
1 parent 14f9233 commit 594a200
Show file tree
Hide file tree
Showing 10 changed files with 248 additions and 76 deletions.
23 changes: 15 additions & 8 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"path"
"path/filepath"

"github.com/jedib0t/go-pretty/v6/text"
"github.com/spf13/cobra"
appLogging "github.com/ublue-os/uupd/pkg/logging"
"golang.org/x/term"
Expand Down Expand Up @@ -107,6 +108,15 @@ func init() {
rootCmd.AddCommand(updateCheckCmd)
rootCmd.AddCommand(hardwareCheckCmd)
rootCmd.AddCommand(imageOutdatedCmd)
rootCmd.Flags().BoolP("hw-check", "c", false, "Run hardware check before running updates")
rootCmd.Flags().BoolP("dry-run", "n", false, "Do a dry run")
rootCmd.Flags().BoolP("verbose", "v", false, "Display command outputs after run")
rootCmd.Flags().Bool("ci", false, "Makes some modifications to behavior if is running in CI")

rootCmd.PersistentFlags().StringVar(&fLogFile, "log-file", "-", "File where user-facing logs will be written to")
rootCmd.PersistentFlags().StringVar(&fLogLevel, "log-level", "info", "Log level for user-facing logs")
rootCmd.PersistentFlags().BoolVar(&fNoLogging, "quiet", false, "Make logs quiet")

interactiveProgress := true
if fLogFile != "-" {
interactiveProgress = false
Expand All @@ -115,13 +125,10 @@ func init() {
if !isTerminal {
interactiveProgress = false
}
rootCmd.Flags().BoolP("no-progress", "p", interactiveProgress, "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")
rootCmd.Flags().BoolP("verbose", "v", false, "Display command outputs after run")
rootCmd.Flags().Bool("ci", false, "Makes some modifications to behavior if is running in CI")
if !text.ANSICodesSupported {
interactiveProgress = false
text.DisableColors()
}

rootCmd.PersistentFlags().StringVar(&fLogFile, "log-file", "-", "File where user-facing logs will be written to")
rootCmd.PersistentFlags().StringVar(&fLogLevel, "log-level", "info", "Log level for user-facing logs")
rootCmd.PersistentFlags().BoolVar(&fNoLogging, "quiet", false, "Make logs quiet")
rootCmd.Flags().BoolP("no-progress", "p", !interactiveProgress, "Do not show progress bars")
}
14 changes: 9 additions & 5 deletions cmd/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ func Update(cmd *cobra.Command, args []string) {
}

initConfiguration := drv.UpdaterInitConfiguration{}.New()
_, empty := os.LookupEnv("CI")
initConfiguration.Ci = !empty
_, exists := os.LookupEnv("CI")
initConfiguration.Ci = exists
initConfiguration.DryRun = dryRun
initConfiguration.Verbose = verboseRun

Expand Down Expand Up @@ -130,6 +130,7 @@ func Update(cmd *cobra.Command, args []string) {

if progressEnabled {
go pw.Render()
lib.ResetOscProgress()
}

// -1 because 0 index
Expand Down Expand Up @@ -188,12 +189,15 @@ func Update(cmd *cobra.Command, args []string) {
tracker.IncrementSection(err)
}

pw.Stop()
if progressEnabled {
pw.Stop()
lib.ResetOscProgress()
}
if verboseRun {
slog.Info("Verbose run requested")

for _, output := range outputs {
slog.Info("CommandOutput", slog.String("context", output.Context), slog.String("stdout", output.Stdout), slog.Any("stderr", output.Stderr))
slog.Info(output.Context, slog.String("stdout", output.Stdout), slog.Any("stderr", output.Stderr), slog.Any("cli", output.Cli))
}

return
Expand All @@ -210,7 +214,7 @@ func Update(cmd *cobra.Command, args []string) {
slog.Warn("Exited with failed updates.")

for _, output := range failures {
slog.Info("CommandOutput", slog.String("context", output.Context), slog.String("stdout", output.Stdout), slog.Any("stderr", output.Stderr))
slog.Info(output.Context, slog.String("stdout", output.Stdout), slog.Any("stderr", output.Stderr), slog.Any("cli", output.Cli))
}

return
Expand Down
80 changes: 46 additions & 34 deletions drv/brew.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@ import (
)

func (up BrewUpdater) GetBrewUID() (int, error) {
inf, err := os.Stat(up.Environment["HOMEBREW_PREFIX"])
inf, err := os.Stat(up.BrewPrefix)
if err != nil {
return -1, err
}

if !inf.IsDir() {
return -1, fmt.Errorf("Brew prefix: %v, is not a dir.", up.Environment["HOMEBREW_PREFIX"])
return -1, fmt.Errorf("Brew prefix: %v, is not a dir.", up.BrewPrefix)
}
stat, ok := inf.Sys().(*syscall.Stat_t)
if !ok {
return -1, fmt.Errorf("Unable to retriev UID info for %v", up.Environment["HOMEBREW_PREFIX"])
return -1, fmt.Errorf("Unable to retriev UID info for %v", up.BrewPrefix)
}
return int(stat.Uid), nil
}
Expand All @@ -42,59 +42,71 @@ func (up BrewUpdater) Update() (*[]CommandOutput, error) {
return &final_output, nil
}

out, err := lib.RunUID(up.BaseUser, []string{up.Environment["HOMEBREW_PATH"], "update"}, up.Environment)
cli := []string{up.BrewPath, "update"}
out, err := lib.RunUID(up.BaseUser, cli, up.Config.Environment)
tmpout := CommandOutput{}.New(out, err)
tmpout.Context = "Brew Update"
tmpout.Cli = cli
tmpout.Failure = err != nil
if err != nil {
tmpout.SetFailureContext("Brew update")
final_output = append(final_output, *tmpout)
return &final_output, err
}

out, err = lib.RunUID(up.BaseUser, []string{up.Environment["HOMEBREW_PATH"], "upgrade"}, up.Environment)
cli = []string{up.BrewPath, "upgrade"}
out, err = lib.RunUID(up.BaseUser, cli, up.Config.Environment)
tmpout = CommandOutput{}.New(out, err)
if err != nil {
tmpout.SetFailureContext("Brew upgrade")
}
tmpout.Context = "Brew Upgrade"
tmpout.Cli = cli
tmpout.Failure = err != nil
final_output = append(final_output, *tmpout)
return &final_output, err
}

type BrewUpdater struct {
Config DriverConfiguration
BaseUser int
Environment map[string]string
Config DriverConfiguration
BaseUser int
BrewRepo string
BrewPrefix string
BrewCellar string
BrewPath string
}

func (up BrewUpdater) New(config UpdaterInitConfiguration) (BrewUpdater, error) {
brewPrefix, empty := os.LookupEnv("HOMEBREW_PREFIX")
if empty || brewPrefix == "" {
brewPrefix = "/home/linuxbrew/.linuxbrew"
}
brewRepo, empty := os.LookupEnv("HOMEBREW_REPOSITORY")
if empty || brewRepo == "" {
brewRepo = fmt.Sprintf("%s/Homebrew", brewPrefix)
}
brewCellar, empty := os.LookupEnv("HOMEBREW_CELLAR")
if empty || brewCellar == "" {
brewCellar = fmt.Sprintf("%s/Cellar", brewPrefix)
}
brewPath, empty := os.LookupEnv("HOMEBREW_PATH")
if empty || brewPath == "" {
brewPath = fmt.Sprintf("%s/bin/brew", brewPrefix)
}

up.Environment = map[string]string{
"HOMEBREW_PREFIX": brewPrefix,
"HOMEBREW_REPOSITORY": brewRepo,
"HOMEBREW_CELLAR": brewCellar,
"HOMEBREW_PATH": brewPath,
}
up.Config = DriverConfiguration{
Title: "Brew",
Description: "CLI Apps",
Enabled: true,
MultiUser: false,
DryRun: config.DryRun,
Environment: config.Environment,
}

brewPrefix, exists := up.Config.Environment["HOMEBREW_PREFIX"]
if !exists || brewPrefix == "" {
up.BrewPrefix = "/home/linuxbrew/.linuxbrew"
} else {
up.BrewPrefix = brewPrefix
}
brewRepo, exists := up.Config.Environment["HOMEBREW_REPOSITORY"]
if !exists || brewRepo == "" {
up.BrewRepo = fmt.Sprintf("%s/Homebrew", up.BrewPrefix)
} else {
up.BrewRepo = brewRepo
}
brewCellar, exists := up.Config.Environment["HOMEBREW_CELLAR"]
if !exists || brewCellar == "" {
up.BrewCellar = fmt.Sprintf("%s/Cellar", up.BrewPrefix)
} else {
up.BrewCellar = brewCellar
}
brewPath, exists := up.Config.Environment["HOMEBREW_PATH"]
if !exists || brewPath == "" {
up.BrewPath = fmt.Sprintf("%s/bin/brew", up.BrewPrefix)
} else {
up.BrewPath = brewPath
}

if up.Config.DryRun {
Expand All @@ -105,7 +117,7 @@ func (up BrewUpdater) New(config UpdaterInitConfiguration) (BrewUpdater, error)
if err != nil {
return up, err
}

up.BaseUser = uid

return up, nil
}
35 changes: 22 additions & 13 deletions drv/distrobox.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
package drv

import (
"fmt"

"github.com/ublue-os/uupd/lib"
)

type DistroboxUpdater struct {
Config DriverConfiguration
Tracker *TrackerConfiguration
binaryPath string
users []lib.User
usersEnabled bool
}
Expand All @@ -24,19 +23,27 @@ func (up DistroboxUpdater) Steps() int {
return 0
}

func (up DistroboxUpdater) New(initconfig UpdaterInitConfiguration) (DistroboxUpdater, error) {
func (up DistroboxUpdater) New(config UpdaterInitConfiguration) (DistroboxUpdater, error) {
userdesc := "Distroboxes for User:"
up.Config = DriverConfiguration{
Title: "Distrobox",
Description: "Rootful Distroboxes",
UserDescription: &userdesc,
Enabled: true,
MultiUser: true,
DryRun: initconfig.DryRun,
DryRun: config.DryRun,
Environment: config.Environment,
}
up.usersEnabled = false
up.Tracker = nil

binaryPath, exists := up.Config.Environment["UUPD_DISTROBOX_BINARY"]
if !exists || binaryPath == "" {
up.binaryPath = "/usr/bin/distrobox"
} else {
up.binaryPath = binaryPath
}

return up, nil
}

Expand Down Expand Up @@ -64,24 +71,26 @@ func (up *DistroboxUpdater) Update() (*[]CommandOutput, error) {
return &finalOutput, nil
}

// TODO: add env support for Flatpak and Distrobox updaters
lib.ChangeTrackerMessageFancy(*up.Tracker.Writer, up.Tracker.Tracker, up.Tracker.Progress, lib.TrackerMessage{Title: up.Config.Title, Description: up.Config.Description})
out, err := lib.RunUID(0, []string{"/usr/bin/distrobox", "upgrade", "-a"}, nil)
cli := []string{up.binaryPath, "upgrade", "-a"}
out, err := lib.RunUID(0, cli, nil)
tmpout := CommandOutput{}.New(out, err)
if err != nil {
tmpout.SetFailureContext("System Distroboxes")
}
tmpout.Context = up.Config.Description
tmpout.Cli = cli
tmpout.Failure = err != nil
finalOutput = append(finalOutput, *tmpout)

err = nil
for _, user := range up.users {
up.Tracker.Tracker.IncrementSection(err)
context := *up.Config.UserDescription + " " + user.Name
lib.ChangeTrackerMessageFancy(*up.Tracker.Writer, up.Tracker.Tracker, up.Tracker.Progress, lib.TrackerMessage{Title: up.Config.Title, Description: *up.Config.UserDescription + " " + user.Name})
out, err := lib.RunUID(user.UID, []string{"/usr/bin/distrobox", "upgrade", "-a"}, nil)
cli := []string{up.binaryPath, "upgrade", "-a"}
out, err := lib.RunUID(user.UID, cli, nil)
tmpout = CommandOutput{}.New(out, err)
if err != nil {
tmpout.SetFailureContext(fmt.Sprintf("Distroboxes for User: %s", user.Name))
}
tmpout.Context = context
tmpout.Cli = cli
tmpout.Failure = err != nil
finalOutput = append(finalOutput, *tmpout)
}
return &finalOutput, nil
Expand Down
35 changes: 23 additions & 12 deletions drv/flatpak.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package drv

import (
"fmt"
"os/exec"

"github.com/ublue-os/uupd/lib"
Expand All @@ -10,6 +9,7 @@ import (
type FlatpakUpdater struct {
Config DriverConfiguration
Tracker *TrackerConfiguration
binaryPath string
users []lib.User
usersEnabled bool
}
Expand All @@ -25,19 +25,27 @@ func (up FlatpakUpdater) Steps() int {
return 0
}

func (up FlatpakUpdater) New(initconfig UpdaterInitConfiguration) (FlatpakUpdater, error) {
func (up FlatpakUpdater) New(config UpdaterInitConfiguration) (FlatpakUpdater, error) {
userdesc := "Apps for User:"
up.Config = DriverConfiguration{
Title: "Flatpak",
Description: "System Apps",
UserDescription: &userdesc,
Enabled: true,
MultiUser: true,
DryRun: initconfig.DryRun,
DryRun: config.DryRun,
Environment: config.Environment,
}
up.usersEnabled = false
up.Tracker = nil

binaryPath, exists := up.Config.Environment["UUPD_FLATPAK_BINARY"]
if !exists || binaryPath == "" {
up.binaryPath = "/usr/bin/flatpak"
} else {
up.binaryPath = binaryPath
}

return up, nil
}

Expand Down Expand Up @@ -66,23 +74,26 @@ func (up FlatpakUpdater) Update() (*[]CommandOutput, error) {
}

lib.ChangeTrackerMessageFancy(*up.Tracker.Writer, up.Tracker.Tracker, up.Tracker.Progress, lib.TrackerMessage{Title: up.Config.Title, Description: up.Config.Description})
flatpakCmd := exec.Command("/usr/bin/flatpak", "update", "-y")
cli := []string{up.binaryPath, "update", "-y"}
flatpakCmd := exec.Command(cli[0], cli[1:]...)
out, err := flatpakCmd.CombinedOutput()
tmpout := CommandOutput{}.New(out, err)
if err != nil {
tmpout.SetFailureContext("Flatpak System Apps")
}
tmpout.Context = up.Config.Description
tmpout.Cli = cli
tmpout.Failure = err != nil
finalOutput = append(finalOutput, *tmpout)

err = nil
for _, user := range up.users {
up.Tracker.Tracker.IncrementSection(err)
lib.ChangeTrackerMessageFancy(*up.Tracker.Writer, up.Tracker.Tracker, up.Tracker.Progress, lib.TrackerMessage{Title: up.Config.Title, Description: *up.Config.UserDescription + " " + user.Name})
out, err := lib.RunUID(user.UID, []string{"/usr/bin/flatpak", "update", "-y"}, nil)
context := *up.Config.UserDescription + " " + user.Name
lib.ChangeTrackerMessageFancy(*up.Tracker.Writer, up.Tracker.Tracker, up.Tracker.Progress, lib.TrackerMessage{Title: up.Config.Title, Description: context})
cli := []string{up.binaryPath, "update", "-y"}
out, err := lib.RunUID(user.UID, cli, nil)
tmpout = CommandOutput{}.New(out, err)
if err != nil {
tmpout.SetFailureContext(fmt.Sprintf("Flatpak User: %s", user.Name))
}
tmpout.Context = context
tmpout.Cli = cli
tmpout.Failure = err != nil
finalOutput = append(finalOutput, *tmpout)
}
return &finalOutput, nil
Expand Down
Loading

0 comments on commit 594a200

Please sign in to comment.