diff --git a/pkg/fetch/alpm/alpm.go b/pkg/fetch/alpm/alpm.go index 0d2809c..dd4d7c8 100644 --- a/pkg/fetch/alpm/alpm.go +++ b/pkg/fetch/alpm/alpm.go @@ -19,6 +19,59 @@ type Handle struct { syncDBs []dyalpm.Database } +func (h *Handle) LocalPackages() (map[string]dyalpm.Package, error) { + start := time.Now() + log.Debug("LocalPackages: starting...") + + localPkgs := make(map[string]dyalpm.Package) + + err := h.localDB.PkgCache().ForEach(func(pkg dyalpm.Package) error { + localPkgs[pkg.Name()] = pkg + return nil + }) + if err != nil { + return nil, fmt.Errorf("failed to iterate local package cache: %w", err) + } + + log.Debug("LocalPackages: done (%.2fs)", time.Since(start).Seconds()) + return localPkgs, nil +} + +func (h *Handle) Release() error { + if h.handle != nil { + h.handle.Release() + } + return nil +} + +func (h *Handle) SyncPackages(pkgNames []string) (map[string]dyalpm.Package, error) { + start := time.Now() + log.Debug("SyncPackages: starting...") + + syncPkgs := make(map[string]dyalpm.Package) + pkgSet := make(map[string]bool) + for _, name := range pkgNames { + pkgSet[name] = true + } + + for _, db := range h.syncDBs { + err := db.PkgCache().ForEach(func(pkg dyalpm.Package) error { + if pkgSet[pkg.Name()] { + if _, exists := syncPkgs[pkg.Name()]; !exists { + syncPkgs[pkg.Name()] = pkg + } + } + return nil + }) + if err != nil { + return nil, fmt.Errorf("failed to iterate sync database %s: %w", db.Name(), err) + } + } + + log.Debug("SyncPackages: done (%.2fs)", time.Since(start).Seconds()) + return syncPkgs, nil +} + func New() (*Handle, error) { start := time.Now() log.Debug("alpm.New: starting...") @@ -56,12 +109,8 @@ func New() (*Handle, error) { }, nil } -func (h *Handle) Release() error { - if h.handle != nil { - h.handle.Release() - } - return nil -} +// ----------------------------------------- +// private func registerSyncDBs(handle dyalpm.Handle) ([]dyalpm.Database, error) { log.Debug("registerSyncDBs: starting...") @@ -89,49 +138,3 @@ func registerSyncDBs(handle dyalpm.Handle) ([]dyalpm.Database, error) { log.Debug("registerSyncDBs: done (%d dbs)", len(dbs)) return dbs, nil } - -func (h *Handle) LocalPackages() (map[string]dyalpm.Package, error) { - start := time.Now() - log.Debug("LocalPackages: starting...") - - localPkgs := make(map[string]dyalpm.Package) - - err := h.localDB.PkgCache().ForEach(func(pkg dyalpm.Package) error { - localPkgs[pkg.Name()] = pkg - return nil - }) - if err != nil { - return nil, fmt.Errorf("failed to iterate local package cache: %w", err) - } - - log.Debug("LocalPackages: done (%.2fs)", time.Since(start).Seconds()) - return localPkgs, nil -} - -func (h *Handle) SyncPackages(pkgNames []string) (map[string]dyalpm.Package, error) { - start := time.Now() - log.Debug("SyncPackages: starting...") - - syncPkgs := make(map[string]dyalpm.Package) - pkgSet := make(map[string]bool) - for _, name := range pkgNames { - pkgSet[name] = true - } - - for _, db := range h.syncDBs { - err := db.PkgCache().ForEach(func(pkg dyalpm.Package) error { - if pkgSet[pkg.Name()] { - if _, exists := syncPkgs[pkg.Name()]; !exists { - syncPkgs[pkg.Name()] = pkg - } - } - return nil - }) - if err != nil { - return nil, fmt.Errorf("failed to iterate sync database %s: %w", db.Name(), err) - } - } - - log.Debug("SyncPackages: done (%.2fs)", time.Since(start).Seconds()) - return syncPkgs, nil -} diff --git a/pkg/fetch/fetch.go b/pkg/fetch/fetch.go index bb4e6b8..1ea25ef 100644 --- a/pkg/fetch/fetch.go +++ b/pkg/fetch/fetch.go @@ -22,22 +22,16 @@ type Fetcher struct { aurClient *aur.Client } -func New() (*Fetcher, error) { - start := time.Now() - log.Debug("fetch.Fetcher New: starting...") - - alpmHandle, err := alpm.New() +func (f *Fetcher) BuildLocalPkgMap() (map[string]interface{}, error) { + localPkgs, err := f.alpmHandle.LocalPackages() if err != nil { return nil, err } - - aurClient := aur.New() - - log.Debug("fetch.Fetcher New: done (%.2fs)", time.Since(start).Seconds()) - return &Fetcher{ - alpmHandle: alpmHandle, - aurClient: aurClient, - }, nil + result := make(map[string]interface{}) + for k, v := range localPkgs { + result[k] = v + } + return result, nil } func (f *Fetcher) Close() error { @@ -48,18 +42,6 @@ func (f *Fetcher) GetAURPackage(name string) (aur.Package, bool) { return f.aurClient.Get(name) } -func (f *Fetcher) BuildLocalPkgMap() (map[string]interface{}, error) { - localPkgs, err := f.alpmHandle.LocalPackages() - if err != nil { - return nil, err - } - result := make(map[string]interface{}) - for k, v := range localPkgs { - result[k] = v - } - return result, nil -} - func (f *Fetcher) Resolve(packages []string) (map[string]*PackageInfo, error) { start := time.Now() log.Debug("fetch.Resolve: starting...") @@ -130,3 +112,21 @@ func (f *Fetcher) Resolve(packages []string) (map[string]*PackageInfo, error) { log.Debug("fetch.Resolve: done (%.2fs)", time.Since(start).Seconds()) return result, nil } + +func New() (*Fetcher, error) { + start := time.Now() + log.Debug("fetch.Fetcher New: starting...") + + alpmHandle, err := alpm.New() + if err != nil { + return nil, err + } + + aurClient := aur.New() + + log.Debug("fetch.Fetcher New: done (%.2fs)", time.Since(start).Seconds()) + return &Fetcher{ + alpmHandle: alpmHandle, + aurClient: aurClient, + }, nil +} diff --git a/pkg/log/log.go b/pkg/log/log.go index 5762011..f67133b 100644 --- a/pkg/log/log.go +++ b/pkg/log/log.go @@ -10,12 +10,21 @@ import ( "time" ) -// ----------------------------------------- - var logFile *os.File var Verbose bool -// ----------------------------------------- +func Close() error { + if logFile == nil { + return nil + } + return logFile.Close() +} + +func Command(name string, args ...string) *exec.Cmd { + cmdStr := name + " " + strings.Join(args, " ") + fmt.Fprintf(logFile, "[cmd] %s\n", cmdStr) + return exec.Command(name, args...) +} func Debug(format string, args ...interface{}) { if !Verbose { @@ -24,7 +33,9 @@ func Debug(format string, args ...interface{}) { fmt.Fprintf(os.Stderr, "[debug] "+format+"\n", args...) } -// ----------------------------------------- +func GetLogWriter() io.Writer { + return logFile +} func OpenLog() error { logPath := filepath.Join("/var/log", "declpac.log") @@ -37,28 +48,12 @@ func OpenLog() error { return nil } -func GetLogWriter() io.Writer { - return logFile -} - func Write(msg []byte) { logFile.Write(msg) } -func Command(name string, args ...string) *exec.Cmd { - cmdStr := name + " " + strings.Join(args, " ") - fmt.Fprintf(logFile, "[cmd] %s\n", cmdStr) - return exec.Command(name, args...) -} - -func Close() error { - if logFile == nil { - return nil - } - return logFile.Close() -} - // ----------------------------------------- +// private func writeTimestamp() { ts := time.Now().Format("2006-01-02 15:04:05") diff --git a/pkg/pacman/pacman.go b/pkg/pacman/pacman.go index a9a8586..72ade3f 100644 --- a/pkg/pacman/pacman.go +++ b/pkg/pacman/pacman.go @@ -117,6 +117,9 @@ func Sync(packages []string, noCheck bool, prune bool) (*output.Result, error) { }, nil } +// ----------------------------------------- +// private + func categorizePackages(f *fetch.Fetcher, packages []string) (pacmanPkgs, aurPkgs []string, err error) { start := time.Now() log.Debug("categorizePackages: starting...") @@ -143,24 +146,6 @@ func categorizePackages(f *fetch.Fetcher, packages []string) (pacmanPkgs, aurPkg return pacmanPkgs, aurPkgs, nil } -func markAllAsDeps() error { - start := time.Now() - log.Debug("markAllAsDeps: starting...") - - packages, err := read.List() - if err != nil || len(packages) == 0 { - return fmt.Errorf("failed to list packages: %w", err) - } - - if err := sync.MarkAs(packages, "deps", log.GetLogWriter()); err != nil { - log.Write([]byte(fmt.Sprintf("error: %v\n", err))) - return err - } - - log.Debug("markAllAsDeps: done (%.2fs)", time.Since(start).Seconds()) - return nil -} - func cleanupOrphans() (int, error) { start := time.Now() log.Debug("cleanupOrphans: starting...") @@ -180,3 +165,21 @@ func cleanupOrphans() (int, error) { log.Debug("cleanupOrphans: done (%.2fs)", time.Since(start).Seconds()) return removed, nil } + +func markAllAsDeps() error { + start := time.Now() + log.Debug("markAllAsDeps: starting...") + + packages, err := read.List() + if err != nil || len(packages) == 0 { + return fmt.Errorf("failed to list packages: %w", err) + } + + if err := sync.MarkAs(packages, "deps", log.GetLogWriter()); err != nil { + log.Write([]byte(fmt.Sprintf("error: %v\n", err))) + return err + } + + log.Debug("markAllAsDeps: done (%.2fs)", time.Since(start).Seconds()) + return nil +} diff --git a/pkg/pacman/read/read.go b/pkg/pacman/read/read.go index a50d2f1..2465a28 100644 --- a/pkg/pacman/read/read.go +++ b/pkg/pacman/read/read.go @@ -16,71 +16,6 @@ import ( var LockFile = "/var/lib/pacman/db.lock" -func List() ([]string, error) { - start := time.Now() - log.Debug("List: starting...") - - cmd := exec.Command("pacman", "-Qq") - output, err := cmd.Output() - if err != nil { - return nil, err - } - - list := strings.Split(strings.TrimSpace(string(output)), "\n") - if list[0] == "" { - list = nil - } - - log.Debug("List: done (%.2fs)", time.Since(start).Seconds()) - return list, nil -} - -func ExplicitList() ([]string, error) { - start := time.Now() - log.Debug("ExplicitList: starting...") - - cmd := exec.Command("pacman", "-Qqe") - output, err := cmd.Output() - if err != nil { - return nil, err - } - - list := strings.Split(strings.TrimSpace(string(output)), "\n") - if len(list) > 0 && list[0] == "" { - list = nil - } - - log.Debug("ExplicitList: done (%.2fs)", time.Since(start).Seconds()) - return list, nil -} - -func ListOrphans() ([]string, error) { - start := time.Now() - log.Debug("ListOrphans: starting...") - - cmd := exec.Command("pacman", "-Qdtq") - var stderr bytes.Buffer - cmd.Stderr = &stderr - - output, err := cmd.Output() - if err != nil { - // pacman -Qdtq exits 1 when there are no orphans, this isnt an error - var exitErr *exec.ExitError - if errors.As(err, &exitErr) && exitErr.ExitCode() == 1 && stderr.Len() == 0 { - return nil, nil - } - return nil, err - } - - orphans := strings.Split(strings.TrimSpace(string(output)), "\n") - if len(orphans) > 0 && orphans[0] == "" { - orphans = orphans[1:] - } - - log.Debug("ListOrphans: done (%.2fs)", time.Since(start).Seconds()) - return orphans, nil -} - func DBFreshness() (bool, error) { info, err := os.Stat(LockFile) if err != nil { @@ -148,3 +83,67 @@ func DryRun(packages []string) (*output.Result, error) { ToRemove: toRemove, }, nil } + +func ExplicitList() ([]string, error) { + start := time.Now() + log.Debug("ExplicitList: starting...") + + cmd := exec.Command("pacman", "-Qqe") + output, err := cmd.Output() + if err != nil { + return nil, err + } + + list := strings.Split(strings.TrimSpace(string(output)), "\n") + if len(list) > 0 && list[0] == "" { + list = nil + } + + log.Debug("ExplicitList: done (%.2fs)", time.Since(start).Seconds()) + return list, nil +} + +func List() ([]string, error) { + start := time.Now() + log.Debug("List: starting...") + + cmd := exec.Command("pacman", "-Qq") + output, err := cmd.Output() + if err != nil { + return nil, err + } + + list := strings.Split(strings.TrimSpace(string(output)), "\n") + if list[0] == "" { + list = nil + } + + log.Debug("List: done (%.2fs)", time.Since(start).Seconds()) + return list, nil +} + +func ListOrphans() ([]string, error) { + start := time.Now() + log.Debug("ListOrphans: starting...") + + cmd := exec.Command("pacman", "-Qdtq") + var stderr bytes.Buffer + cmd.Stderr = &stderr + + output, err := cmd.Output() + if err != nil { + var exitErr *exec.ExitError + if errors.As(err, &exitErr) && exitErr.ExitCode() == 1 && stderr.Len() == 0 { + return nil, nil + } + return nil, err + } + + orphans := strings.Split(strings.TrimSpace(string(output)), "\n") + if len(orphans) > 0 && orphans[0] == "" { + orphans = orphans[1:] + } + + log.Debug("ListOrphans: done (%.2fs)", time.Since(start).Seconds()) + return orphans, nil +} diff --git a/pkg/pacman/sync/sync.go b/pkg/pacman/sync/sync.go index 3ae2fc3..a535f80 100644 --- a/pkg/pacman/sync/sync.go +++ b/pkg/pacman/sync/sync.go @@ -15,43 +15,59 @@ type Result struct { Removed int } -func SyncPackages(packages []string, logWriter io.Writer) error { +func InstallAUR(pkgName string, packageBase string, logWriter io.Writer) error { start := time.Now() - log.Debug("SyncPackages: starting...") + log.Debug("InstallAUR: starting...") if logWriter == nil { logWriter = os.Stderr } - args := append([]string{"-S", "--needed", "--noconfirm"}, packages...) - cmd := log.Command("pacman", args...) - cmd.Stdout = logWriter - cmd.Stderr = logWriter - err := cmd.Run() - if err != nil { - return fmt.Errorf("pacman sync failed: %w", err) + sudoUser := os.Getenv("SUDO_USER") + if sudoUser == "" { + sudoUser = os.Getenv("USER") + if sudoUser == "" { + sudoUser = "root" + } } - log.Debug("SyncPackages: done (%.2fs)", time.Since(start).Seconds()) - return nil -} + tmpDir := "/tmp/declpac/" + pkgName + mkdirCmd := log.Command("su", "-", sudoUser, "-c", "rm -rf "+tmpDir+" && mkdir -p "+tmpDir) + if err := mkdirCmd.Run(); err != nil { + return fmt.Errorf("failed to create temp directory: %w", err) + } + defer os.RemoveAll(tmpDir) -func RefreshDB(logWriter io.Writer) error { - start := time.Now() - log.Debug("RefreshDB: starting...") + cloneURL := "https://aur.archlinux.org/" + packageBase + ".git" + cloneCmd := log.Command("su", "-", sudoUser, "-c", "git clone "+cloneURL+" "+tmpDir) + cloneCmd.Stdout = logWriter + cloneCmd.Stderr = logWriter + if err := cloneCmd.Run(); err != nil { + return fmt.Errorf("failed to clone AUR repo: %w", err) + } + log.Debug("InstallAUR: cloned (%.2fs)", time.Since(start).Seconds()) - if logWriter == nil { - logWriter = os.Stderr + makepkgCmd := log.Command("su", "-", sudoUser, "-c", "cd "+tmpDir+" && makepkg -s --noconfirm") + makepkgCmd.Stdout = logWriter + makepkgCmd.Stderr = logWriter + if err := makepkgCmd.Run(); err != nil { + return fmt.Errorf("makepkg failed to build AUR package: %w", err) } + log.Debug("InstallAUR: built (%.2fs)", time.Since(start).Seconds()) - cmd := log.Command("pacman", "-Syy") - cmd.Stdout = logWriter - cmd.Stderr = logWriter - if err := cmd.Run(); err != nil { - return fmt.Errorf("failed to refresh pacman database: %w", err) + pkgFile, err := findPKGFile(pkgName, tmpDir) + if err != nil { + return fmt.Errorf("failed to find built package: %w", err) } - log.Debug("RefreshDB: done (%.2fs)", time.Since(start).Seconds()) + installCmd := log.Command("pacman", "-U", "--noconfirm", pkgFile) + installCmd.Stdout = logWriter + installCmd.Stderr = logWriter + if err := installCmd.Run(); err != nil { + return fmt.Errorf("failed to install package: %w", err) + } + log.Debug("InstallAUR: done (%.2fs)", time.Since(start).Seconds()) + return nil } @@ -80,6 +96,25 @@ func MarkAs(packages []string, flag string, logWriter io.Writer) error { return nil } +func RefreshDB(logWriter io.Writer) error { + start := time.Now() + log.Debug("RefreshDB: starting...") + + if logWriter == nil { + logWriter = os.Stderr + } + + cmd := log.Command("pacman", "-Syy") + cmd.Stdout = logWriter + cmd.Stderr = logWriter + if err := cmd.Run(); err != nil { + return fmt.Errorf("failed to refresh pacman database: %w", err) + } + + log.Debug("RefreshDB: done (%.2fs)", time.Since(start).Seconds()) + return nil +} + func RemoveOrphans(orphans []string, logWriter io.Writer) (int, error) { start := time.Now() log.Debug("RemoveOrphans: starting...") @@ -110,62 +145,30 @@ func RemoveOrphans(orphans []string, logWriter io.Writer) (int, error) { return count, nil } -func InstallAUR(pkgName string, packageBase string, logWriter io.Writer) error { +func SyncPackages(packages []string, logWriter io.Writer) error { start := time.Now() - log.Debug("InstallAUR: starting...") + log.Debug("SyncPackages: starting...") if logWriter == nil { logWriter = os.Stderr } - sudoUser := os.Getenv("SUDO_USER") - if sudoUser == "" { - sudoUser = os.Getenv("USER") - if sudoUser == "" { - sudoUser = "root" - } - } - - tmpDir := "/tmp/declpac/" + pkgName - mkdirCmd := log.Command("su", "-", sudoUser, "-c", "rm -rf "+tmpDir+" && mkdir -p "+tmpDir) - if err := mkdirCmd.Run(); err != nil { - return fmt.Errorf("failed to create temp directory: %w", err) - } - defer os.RemoveAll(tmpDir) - - cloneURL := "https://aur.archlinux.org/" + packageBase + ".git" - cloneCmd := log.Command("su", "-", sudoUser, "-c", "git clone "+cloneURL+" "+tmpDir) - cloneCmd.Stdout = logWriter - cloneCmd.Stderr = logWriter - if err := cloneCmd.Run(); err != nil { - return fmt.Errorf("failed to clone AUR repo: %w", err) - } - log.Debug("InstallAUR: cloned (%.2fs)", time.Since(start).Seconds()) - - makepkgCmd := log.Command("su", "-", sudoUser, "-c", "cd "+tmpDir+" && makepkg -s --noconfirm") - makepkgCmd.Stdout = logWriter - makepkgCmd.Stderr = logWriter - if err := makepkgCmd.Run(); err != nil { - return fmt.Errorf("makepkg failed to build AUR package: %w", err) - } - log.Debug("InstallAUR: built (%.2fs)", time.Since(start).Seconds()) - - pkgFile, err := findPKGFile(pkgName, tmpDir) + args := append([]string{"-S", "--needed", "--noconfirm"}, packages...) + cmd := log.Command("pacman", args...) + cmd.Stdout = logWriter + cmd.Stderr = logWriter + err := cmd.Run() if err != nil { - return fmt.Errorf("failed to find built package: %w", err) - } - - installCmd := log.Command("pacman", "-U", "--noconfirm", pkgFile) - installCmd.Stdout = logWriter - installCmd.Stderr = logWriter - if err := installCmd.Run(); err != nil { - return fmt.Errorf("failed to install package: %w", err) + return fmt.Errorf("pacman sync failed: %w", err) } - log.Debug("InstallAUR: done (%.2fs)", time.Since(start).Seconds()) + log.Debug("SyncPackages: done (%.2fs)", time.Since(start).Seconds()) return nil } +// ----------------------------------------- +// private + func findPKGFile(pkgName string, dir string) (string, error) { entries, err := os.ReadDir(dir) if err != nil {