256 lines
5.6 KiB
Go
256 lines
5.6 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"flag"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
"lazy-update-manager/internal/config"
|
|
"lazy-update-manager/internal/gui"
|
|
"lazy-update-manager/internal/notify"
|
|
"lazy-update-manager/internal/state"
|
|
"lazy-update-manager/internal/updater"
|
|
)
|
|
|
|
func main() {
|
|
os.Exit(run(os.Args[1:]))
|
|
}
|
|
|
|
func run(args []string) int {
|
|
if len(args) == 0 {
|
|
args = []string{"status"}
|
|
}
|
|
|
|
switch args[0] {
|
|
case "status":
|
|
return status()
|
|
case "check":
|
|
return check(args[1:])
|
|
case "notify":
|
|
return notifyUpdates(args[1:])
|
|
case "gui":
|
|
return runGUI(args[1:])
|
|
case "update":
|
|
return update()
|
|
case "help", "-h", "--help":
|
|
usage()
|
|
return 0
|
|
default:
|
|
fmt.Fprintf(os.Stderr, "unknown command: %s\n\n", args[0])
|
|
usage()
|
|
return 2
|
|
}
|
|
}
|
|
|
|
func status() int {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
|
|
defer cancel()
|
|
|
|
cfg, err := config.Load(defaultConfigPath())
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, err)
|
|
return 1
|
|
}
|
|
|
|
result, err := updater.CheckWithOptions(ctx, updater.Options{CheckAUR: cfg.CheckAUR})
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, err)
|
|
return 1
|
|
}
|
|
|
|
printResult(result)
|
|
return 0
|
|
}
|
|
|
|
func check(args []string) int {
|
|
fs := flag.NewFlagSet("check", flag.ContinueOnError)
|
|
fs.SetOutput(os.Stderr)
|
|
quiet := fs.Bool("quiet", false, "only print the number of available updates")
|
|
if err := fs.Parse(args); err != nil {
|
|
return 2
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
|
|
defer cancel()
|
|
|
|
cfg, err := config.Load(defaultConfigPath())
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, err)
|
|
return 1
|
|
}
|
|
|
|
result, err := updater.CheckWithOptions(ctx, updater.Options{CheckAUR: cfg.CheckAUR})
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, err)
|
|
return 1
|
|
}
|
|
|
|
if *quiet {
|
|
fmt.Println(result.Total())
|
|
return 0
|
|
}
|
|
|
|
printResult(result)
|
|
return 0
|
|
}
|
|
|
|
func notifyUpdates(args []string) int {
|
|
fs := flag.NewFlagSet("notify", flag.ContinueOnError)
|
|
fs.SetOutput(os.Stderr)
|
|
force := fs.Bool("force", false, "send a notification even if the weekly reminder was already shown")
|
|
if err := fs.Parse(args); err != nil {
|
|
return 2
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
|
|
defer cancel()
|
|
|
|
cfg, err := config.Load(defaultConfigPath())
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, err)
|
|
return 1
|
|
}
|
|
|
|
result, err := updater.CheckWithOptions(ctx, updater.Options{CheckAUR: cfg.CheckAUR})
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, err)
|
|
return 1
|
|
}
|
|
if result.Total() == 0 {
|
|
return 0
|
|
}
|
|
|
|
store, err := state.Load(defaultStatePath())
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, err)
|
|
return 1
|
|
}
|
|
|
|
now := time.Now()
|
|
reminderInterval := time.Duration(cfg.ReminderIntervalHours) * time.Hour
|
|
if !*force && now.Sub(store.LastReminder) < reminderInterval {
|
|
return 0
|
|
}
|
|
|
|
message := result.Summary()
|
|
if err := notify.Send("LazyUpdateManager", message); err != nil {
|
|
fmt.Fprintln(os.Stderr, err)
|
|
return 1
|
|
}
|
|
|
|
store.LastReminder = now
|
|
store.LastUpdateCount = result.Total()
|
|
if err := state.Save(defaultStatePath(), store); err != nil {
|
|
fmt.Fprintln(os.Stderr, err)
|
|
return 1
|
|
}
|
|
|
|
return 0
|
|
}
|
|
|
|
func runGUI(args []string) int {
|
|
fs := flag.NewFlagSet("gui", flag.ContinueOnError)
|
|
fs.SetOutput(os.Stderr)
|
|
noOpen := fs.Bool("no-open", false, "start the GUI server without opening a browser")
|
|
if err := fs.Parse(args); err != nil {
|
|
return 2
|
|
}
|
|
|
|
if err := gui.Run(defaultConfigPath(), !*noOpen); err != nil {
|
|
fmt.Fprintln(os.Stderr, err)
|
|
return 1
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func update() int {
|
|
cfg, err := config.Load(defaultConfigPath())
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, err)
|
|
return 1
|
|
}
|
|
|
|
helper := updater.AURHelper()
|
|
if helper != "" && cfg.CheckAUR {
|
|
return runInteractive(helper, "-Syu")
|
|
}
|
|
return runInteractive("sudo", "pacman", "-Syu")
|
|
}
|
|
|
|
func runInteractive(name string, args ...string) int {
|
|
cmd := exec.Command(name, args...)
|
|
cmd.Stdin = os.Stdin
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
if err := cmd.Run(); err != nil {
|
|
var exitErr *exec.ExitError
|
|
if errors.As(err, &exitErr) {
|
|
return exitErr.ExitCode()
|
|
}
|
|
fmt.Fprintln(os.Stderr, err)
|
|
return 1
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func printResult(result updater.Result) {
|
|
fmt.Println(result.Summary())
|
|
for _, item := range result.Packages {
|
|
fmt.Printf("%-8s %s", item.Source, item.Name)
|
|
if item.Current != "" || item.Available != "" {
|
|
fmt.Printf(" %s -> %s", item.Current, item.Available)
|
|
}
|
|
fmt.Println()
|
|
}
|
|
}
|
|
|
|
func defaultStatePath() string {
|
|
stateHome := os.Getenv("XDG_STATE_HOME")
|
|
if stateHome == "" {
|
|
home, err := os.UserHomeDir()
|
|
if err != nil {
|
|
return filepath.Join(".", "lazy-update-manager.json")
|
|
}
|
|
stateHome = filepath.Join(home, ".local", "state")
|
|
}
|
|
return filepath.Join(stateHome, "lazy-update-manager", "state.json")
|
|
}
|
|
|
|
func defaultConfigPath() string {
|
|
configHome := os.Getenv("XDG_CONFIG_HOME")
|
|
if configHome == "" {
|
|
home, err := os.UserHomeDir()
|
|
if err != nil {
|
|
return filepath.Join(".", "lazy-update-manager.json")
|
|
}
|
|
configHome = filepath.Join(home, ".config")
|
|
}
|
|
return filepath.Join(configHome, "lazy-update-manager", "config.json")
|
|
}
|
|
|
|
func usage() {
|
|
fmt.Println(strings.TrimSpace(`
|
|
LazyUpdateManager - Update helper for Arch / Hyprland
|
|
|
|
Usage:
|
|
lazy-update-manager status
|
|
lazy-update-manager check [-quiet]
|
|
lazy-update-manager notify [-force]
|
|
lazy-update-manager gui [-no-open]
|
|
lazy-update-manager update
|
|
|
|
Commands:
|
|
status Show available pacman and AUR updates
|
|
check Check updates, useful for scripts and status bars
|
|
notify Send a weekly desktop notification when updates exist
|
|
gui Start the graphical web interface
|
|
update Run paru/yay -Syu when available, otherwise sudo pacman -Syu
|
|
`))
|
|
}
|