|
|
|
@ -12,6 +12,7 @@ import ( |
|
|
|
"strings" |
|
|
|
"strings" |
|
|
|
"time" |
|
|
|
"time" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
"github.com/Jguer/dyalpm" |
|
|
|
"github.com/Riyyi/declpac/pkg/output" |
|
|
|
"github.com/Riyyi/declpac/pkg/output" |
|
|
|
) |
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
@ -23,89 +24,253 @@ var ( |
|
|
|
|
|
|
|
|
|
|
|
type Pac struct { |
|
|
|
type Pac struct { |
|
|
|
aurCache map[string]AURPackage |
|
|
|
aurCache map[string]AURPackage |
|
|
|
|
|
|
|
handle dyalpm.Handle |
|
|
|
|
|
|
|
localDB dyalpm.Database |
|
|
|
|
|
|
|
syncDBs []dyalpm.Database |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func New() (*Pac, error) { |
|
|
|
func New() (*Pac, error) { |
|
|
|
return &Pac{aurCache: make(map[string]AURPackage)}, nil |
|
|
|
start := time.Now() |
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] New: starting...\n") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
handle, err := dyalpm.Initialize(Root, "/var/lib/pacman") |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return nil, fmt.Errorf("failed to initialize alpm: %w", err) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
localDB, err := handle.LocalDB() |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
handle.Release() |
|
|
|
|
|
|
|
return nil, fmt.Errorf("failed to get local database: %w", err) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
syncDBs, err := handle.SyncDBs() |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
handle.Release() |
|
|
|
|
|
|
|
return nil, fmt.Errorf("failed to get sync databases: %w", err) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] New: done (%.2fs)\n", time.Since(start).Seconds()) |
|
|
|
|
|
|
|
return &Pac{ |
|
|
|
|
|
|
|
aurCache: make(map[string]AURPackage), |
|
|
|
|
|
|
|
handle: handle, |
|
|
|
|
|
|
|
localDB: localDB, |
|
|
|
|
|
|
|
syncDBs: syncDBs, |
|
|
|
|
|
|
|
}, nil |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (p *Pac) Close() error { |
|
|
|
func (p *Pac) Close() error { |
|
|
|
|
|
|
|
if p.handle != nil { |
|
|
|
|
|
|
|
p.handle.Release() |
|
|
|
|
|
|
|
} |
|
|
|
return nil |
|
|
|
return nil |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
type PackageInfo struct { |
|
|
|
type PackageInfo struct { |
|
|
|
Name string |
|
|
|
Name string |
|
|
|
InAUR bool |
|
|
|
InAUR bool |
|
|
|
Exists bool |
|
|
|
Exists bool |
|
|
|
AURInfo *AURPackage |
|
|
|
Installed bool |
|
|
|
|
|
|
|
AURInfo *AURPackage |
|
|
|
|
|
|
|
syncPkg dyalpm.Package |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
type AURResponse struct { |
|
|
|
func (p *Pac) buildLocalPkgMap() (map[string]dyalpm.Package, error) { |
|
|
|
Results []AURPackage `json:"results"` |
|
|
|
start := time.Now() |
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] buildLocalPkgMap: starting...\n") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
localPkgs := make(map[string]dyalpm.Package) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
err := p.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) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] buildLocalPkgMap: done (%.2fs)\n", time.Since(start).Seconds()) |
|
|
|
|
|
|
|
return localPkgs, nil |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
type AURPackage struct { |
|
|
|
func (p *Pac) checkSyncDBs(pkgNames []string) (map[string]dyalpm.Package, error) { |
|
|
|
Name string `json:"Name"` |
|
|
|
start := time.Now() |
|
|
|
PackageBase string `json:"PackageBase"` |
|
|
|
fmt.Fprintf(os.Stderr, "[debug] checkSyncDBs: starting...\n") |
|
|
|
Version string `json:"Version"` |
|
|
|
|
|
|
|
URL string `json:"URL"` |
|
|
|
syncPkgs := make(map[string]dyalpm.Package) |
|
|
|
|
|
|
|
pkgSet := make(map[string]bool) |
|
|
|
|
|
|
|
for _, name := range pkgNames { |
|
|
|
|
|
|
|
pkgSet[name] = true |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for _, db := range p.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) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] checkSyncDBs: done (%.2fs)\n", time.Since(start).Seconds()) |
|
|
|
|
|
|
|
return syncPkgs, nil |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (p *Pac) ValidatePackage(name string) (*PackageInfo, error) { |
|
|
|
func (p *Pac) resolvePackages(packages []string) (map[string]*PackageInfo, error) { |
|
|
|
cmd := exec.Command("pacman", "-Qip", name) |
|
|
|
start := time.Now() |
|
|
|
if err := cmd.Run(); err == nil { |
|
|
|
fmt.Fprintf(os.Stderr, "[debug] resolvePackages: starting...\n") |
|
|
|
return &PackageInfo{Name: name, Exists: true, InAUR: false}, nil |
|
|
|
|
|
|
|
|
|
|
|
result := make(map[string]*PackageInfo) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
localPkgs, err := p.buildLocalPkgMap() |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return nil, err |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] resolvePackages: local pkgs built (%.2fs)\n", time.Since(start).Seconds()) |
|
|
|
|
|
|
|
|
|
|
|
cmd = exec.Command("pacman", "-Sip", name) |
|
|
|
var notInLocal []string |
|
|
|
if err := cmd.Run(); err == nil { |
|
|
|
for _, pkg := range packages { |
|
|
|
return &PackageInfo{Name: name, Exists: true, InAUR: false}, nil |
|
|
|
if localPkg, ok := localPkgs[pkg]; ok { |
|
|
|
|
|
|
|
result[pkg] = &PackageInfo{ |
|
|
|
|
|
|
|
Name: pkg, |
|
|
|
|
|
|
|
Exists: true, |
|
|
|
|
|
|
|
InAUR: false, |
|
|
|
|
|
|
|
Installed: true, |
|
|
|
|
|
|
|
syncPkg: localPkg, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
notInLocal = append(notInLocal, pkg) |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
p.ensureAURCache([]string{name}) |
|
|
|
if len(notInLocal) > 0 { |
|
|
|
if aurInfo, ok := p.aurCache[name]; ok { |
|
|
|
syncPkgs, err := p.checkSyncDBs(notInLocal) |
|
|
|
return &PackageInfo{Name: name, Exists: true, InAUR: true, AURInfo: &aurInfo}, nil |
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return nil, err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] resolvePackages: sync db checked (%.2fs)\n", time.Since(start).Seconds()) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var notInSync []string |
|
|
|
|
|
|
|
for _, pkg := range notInLocal { |
|
|
|
|
|
|
|
if syncPkg, ok := syncPkgs[pkg]; ok { |
|
|
|
|
|
|
|
result[pkg] = &PackageInfo{ |
|
|
|
|
|
|
|
Name: pkg, |
|
|
|
|
|
|
|
Exists: true, |
|
|
|
|
|
|
|
InAUR: false, |
|
|
|
|
|
|
|
Installed: false, |
|
|
|
|
|
|
|
syncPkg: syncPkg, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
notInSync = append(notInSync, pkg) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if len(notInSync) > 0 { |
|
|
|
|
|
|
|
p.ensureAURCache(notInSync) |
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] resolvePackages: AUR cache ensured (%.2fs)\n", time.Since(start).Seconds()) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var unfound []string |
|
|
|
|
|
|
|
for _, pkg := range notInSync { |
|
|
|
|
|
|
|
if aurInfo, ok := p.aurCache[pkg]; ok { |
|
|
|
|
|
|
|
result[pkg] = &PackageInfo{ |
|
|
|
|
|
|
|
Name: pkg, |
|
|
|
|
|
|
|
Exists: true, |
|
|
|
|
|
|
|
InAUR: true, |
|
|
|
|
|
|
|
Installed: false, |
|
|
|
|
|
|
|
AURInfo: &aurInfo, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
unfound = append(unfound, pkg) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if len(unfound) > 0 { |
|
|
|
|
|
|
|
return nil, fmt.Errorf("package(s) not found: %s", strings.Join(unfound, ", ")) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return &PackageInfo{Name: name, Exists: false, InAUR: false}, nil |
|
|
|
fmt.Fprintf(os.Stderr, "[debug] resolvePackages: done (%.2fs)\n", time.Since(start).Seconds()) |
|
|
|
|
|
|
|
return result, nil |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
type AURResponse struct { |
|
|
|
|
|
|
|
Results []AURPackage `json:"results"` |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
type AURPackage struct { |
|
|
|
|
|
|
|
Name string `json:"Name"` |
|
|
|
|
|
|
|
PackageBase string `json:"PackageBase"` |
|
|
|
|
|
|
|
Version string `json:"Version"` |
|
|
|
|
|
|
|
URL string `json:"URL"` |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (p *Pac) IsDBFresh() (bool, error) { |
|
|
|
func (p *Pac) IsDBFresh() (bool, error) { |
|
|
|
|
|
|
|
start := time.Now() |
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] IsDBFresh: starting...\n") |
|
|
|
|
|
|
|
|
|
|
|
info, err := os.Stat(LockFile) |
|
|
|
info, err := os.Stat(LockFile) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
return false, nil |
|
|
|
return false, nil |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
age := time.Since(info.ModTime()) |
|
|
|
age := time.Since(info.ModTime()) |
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] IsDBFresh: done (%.2fs)\n", time.Since(start).Seconds()) |
|
|
|
return age < 24*time.Hour, nil |
|
|
|
return age < 24*time.Hour, nil |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (p *Pac) SyncDB() error { |
|
|
|
func (p *Pac) SyncDB() error { |
|
|
|
|
|
|
|
start := time.Now() |
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] SyncDB: starting...\n") |
|
|
|
|
|
|
|
|
|
|
|
cmd := exec.Command("pacman", "-Syy") |
|
|
|
cmd := exec.Command("pacman", "-Syy") |
|
|
|
cmd.Stdout = os.Stdout |
|
|
|
cmd.Stdout = os.Stdout |
|
|
|
cmd.Stderr = os.Stderr |
|
|
|
cmd.Stderr = os.Stderr |
|
|
|
return cmd.Run() |
|
|
|
err := cmd.Run() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] SyncDB: done (%.2fs)\n", time.Since(start).Seconds()) |
|
|
|
|
|
|
|
return err |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (p *Pac) GetInstalledPackages() ([]string, error) { |
|
|
|
func (p *Pac) MarkAllAsDeps() error { |
|
|
|
cmd := exec.Command("pacman", "-Qq") |
|
|
|
start := time.Now() |
|
|
|
output, err := cmd.Output() |
|
|
|
fmt.Fprintf(os.Stderr, "[debug] MarkAllAsDeps: starting...\n") |
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return nil, err |
|
|
|
cmd := exec.Command("pacman", "-D", "--asdeps") |
|
|
|
} |
|
|
|
cmd.Stdout = os.Stdout |
|
|
|
|
|
|
|
cmd.Stderr = os.Stderr |
|
|
|
|
|
|
|
err := cmd.Run() |
|
|
|
|
|
|
|
|
|
|
|
packages := strings.Split(strings.TrimSpace(string(output)), "\n") |
|
|
|
fmt.Fprintf(os.Stderr, "[debug] MarkAllAsDeps: done (%.2fs)\n", time.Since(start).Seconds()) |
|
|
|
return packages, nil |
|
|
|
return err |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (p *Pac) MarkExplicit(pkgName string) error { |
|
|
|
func (p *Pac) MarkAsExplicit(packages []string) error { |
|
|
|
cmd := exec.Command("pacman", "-D", "--explicit", pkgName) |
|
|
|
if len(packages) == 0 { |
|
|
|
|
|
|
|
return nil |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
start := time.Now() |
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] MarkAsExplicit: starting...\n") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
args := append([]string{"-D", "--asexplicit"}, packages...) |
|
|
|
|
|
|
|
cmd := exec.Command("pacman", args...) |
|
|
|
cmd.Stdout = os.Stdout |
|
|
|
cmd.Stdout = os.Stdout |
|
|
|
cmd.Stderr = os.Stderr |
|
|
|
cmd.Stderr = os.Stderr |
|
|
|
return cmd.Run() |
|
|
|
err := cmd.Run() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] MarkAsExplicit: done (%.2fs)\n", time.Since(start).Seconds()) |
|
|
|
|
|
|
|
return err |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func Sync(packages []string) (*output.Result, error) { |
|
|
|
func Sync(packages []string) (*output.Result, error) { |
|
|
|
|
|
|
|
start := time.Now() |
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] Sync: starting...\n") |
|
|
|
|
|
|
|
|
|
|
|
before, err := getInstalledCount() |
|
|
|
before, err := getInstalledCount() |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
return nil, err |
|
|
|
@ -116,34 +281,52 @@ func Sync(packages []string) (*output.Result, error) { |
|
|
|
return nil, err |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
} |
|
|
|
defer p.Close() |
|
|
|
defer p.Close() |
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] Sync: initialized pacman (%.2fs)\n", time.Since(start).Seconds()) |
|
|
|
|
|
|
|
|
|
|
|
fresh, err := p.IsDBFresh() |
|
|
|
fresh, err := p.IsDBFresh() |
|
|
|
if err != nil || !fresh { |
|
|
|
if err != nil || !fresh { |
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] Sync: syncing database...\n") |
|
|
|
if err := p.SyncDB(); err != nil { |
|
|
|
if err := p.SyncDB(); err != nil { |
|
|
|
return nil, fmt.Errorf("failed to sync database: %w", err) |
|
|
|
return nil, fmt.Errorf("failed to sync database: %w", err) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] Sync: database synced (%.2fs)\n", time.Since(start).Seconds()) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pacmanPkgs, aurPkgs := p.categorizePackages(packages) |
|
|
|
fmt.Fprintf(os.Stderr, "[debug] Sync: categorizing packages...\n") |
|
|
|
|
|
|
|
pacmanPkgs, aurPkgs, err := p.categorizePackages(packages) |
|
|
|
for _, pkg := range packages { |
|
|
|
if err != nil { |
|
|
|
if err := p.MarkExplicit(pkg); err != nil { |
|
|
|
return nil, err |
|
|
|
fmt.Fprintf(os.Stderr, "warning: could not mark %s as explicit: %v\n", pkg, err) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] Sync: packages categorized (%.2fs)\n", time.Since(start).Seconds()) |
|
|
|
|
|
|
|
|
|
|
|
if len(pacmanPkgs) > 0 { |
|
|
|
if len(pacmanPkgs) > 0 { |
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] Sync: syncing %d pacman packages...\n", len(pacmanPkgs)) |
|
|
|
_, err = p.SyncPackages(pacmanPkgs) |
|
|
|
_, err = p.SyncPackages(pacmanPkgs) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] Sync: pacman packages synced (%.2fs)\n", time.Since(start).Seconds()) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
for _, pkg := range aurPkgs { |
|
|
|
for _, pkg := range aurPkgs { |
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] Sync: installing AUR package %s...\n", pkg) |
|
|
|
if err := p.InstallAUR(pkg); err != nil { |
|
|
|
if err := p.InstallAUR(pkg); err != nil { |
|
|
|
return nil, err |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] Sync: AUR package %s installed (%.2fs)\n", pkg, time.Since(start).Seconds()) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] Sync: marking all as deps...\n") |
|
|
|
|
|
|
|
if err := p.MarkAllAsDeps(); err != nil { |
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "warning: could not mark all as deps: %v\n", err) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] Sync: all marked as deps (%.2fs)\n", time.Since(start).Seconds()) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] Sync: marking state packages as explicit...\n") |
|
|
|
|
|
|
|
if err := p.MarkAsExplicit(packages); err != nil { |
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "warning: could not mark state packages as explicit: %v\n", err) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] Sync: state packages marked as explicit (%.2fs)\n", time.Since(start).Seconds()) |
|
|
|
|
|
|
|
|
|
|
|
removed, err := p.CleanupOrphans() |
|
|
|
removed, err := p.CleanupOrphans() |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
@ -153,39 +336,43 @@ func Sync(packages []string) (*output.Result, error) { |
|
|
|
after, _ := getInstalledCount() |
|
|
|
after, _ := getInstalledCount() |
|
|
|
installedCount := max(after-before, 0) |
|
|
|
installedCount := max(after-before, 0) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] Sync: done (%.2fs)\n", time.Since(start).Seconds()) |
|
|
|
return &output.Result{ |
|
|
|
return &output.Result{ |
|
|
|
Installed: installedCount, |
|
|
|
Installed: installedCount, |
|
|
|
Removed: removed, |
|
|
|
Removed: removed, |
|
|
|
}, nil |
|
|
|
}, nil |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (p *Pac) categorizePackages(packages []string) (pacmanPkgs, aurPkgs []string) { |
|
|
|
func (p *Pac) categorizePackages(packages []string) (pacmanPkgs, aurPkgs []string, err error) { |
|
|
|
var notInPacman []string |
|
|
|
start := time.Now() |
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] categorizePackages: starting...\n") |
|
|
|
|
|
|
|
|
|
|
|
for _, pkg := range packages { |
|
|
|
resolved, err := p.resolvePackages(packages) |
|
|
|
info, err := p.ValidatePackage(pkg) |
|
|
|
if err != nil { |
|
|
|
if err != nil || !info.Exists { |
|
|
|
return nil, nil, err |
|
|
|
notInPacman = append(notInPacman, pkg) |
|
|
|
|
|
|
|
} else if !info.InAUR { |
|
|
|
|
|
|
|
pacmanPkgs = append(pacmanPkgs, pkg) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if len(notInPacman) > 0 { |
|
|
|
for _, pkg := range packages { |
|
|
|
p.ensureAURCache(notInPacman) |
|
|
|
info := resolved[pkg] |
|
|
|
for _, pkg := range notInPacman { |
|
|
|
if info == nil || !info.Exists { |
|
|
|
if _, ok := p.aurCache[pkg]; ok { |
|
|
|
fmt.Fprintf(os.Stderr, "error: package not found: %s\n", pkg) |
|
|
|
aurPkgs = append(aurPkgs, pkg) |
|
|
|
continue |
|
|
|
} else { |
|
|
|
} |
|
|
|
fmt.Fprintf(os.Stderr, "error: package not found: %s\n", pkg) |
|
|
|
if info.InAUR { |
|
|
|
} |
|
|
|
aurPkgs = append(aurPkgs, pkg) |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
pacmanPkgs = append(pacmanPkgs, pkg) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return pacmanPkgs, aurPkgs |
|
|
|
fmt.Fprintf(os.Stderr, "[debug] categorizePackages: done (%.2fs)\n", time.Since(start).Seconds()) |
|
|
|
|
|
|
|
return pacmanPkgs, aurPkgs, nil |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (p *Pac) ensureAURCache(packages []string) { |
|
|
|
func (p *Pac) ensureAURCache(packages []string) { |
|
|
|
|
|
|
|
start := time.Now() |
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] ensureAURCache: starting...\n") |
|
|
|
|
|
|
|
|
|
|
|
if len(packages) == 0 { |
|
|
|
if len(packages) == 0 { |
|
|
|
return |
|
|
|
return |
|
|
|
} |
|
|
|
} |
|
|
|
@ -198,13 +385,18 @@ func (p *Pac) ensureAURCache(packages []string) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if len(uncached) == 0 { |
|
|
|
if len(uncached) == 0 { |
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] ensureAURCache: done (%.2fs)\n", time.Since(start).Seconds()) |
|
|
|
return |
|
|
|
return |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
p.fetchAURInfo(uncached) |
|
|
|
p.fetchAURInfo(uncached) |
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] ensureAURCache: done (%.2fs)\n", time.Since(start).Seconds()) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (p *Pac) fetchAURInfo(packages []string) map[string]AURPackage { |
|
|
|
func (p *Pac) fetchAURInfo(packages []string) map[string]AURPackage { |
|
|
|
|
|
|
|
start := time.Now() |
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] fetchAURInfo: starting...\n") |
|
|
|
|
|
|
|
|
|
|
|
result := make(map[string]AURPackage) |
|
|
|
result := make(map[string]AURPackage) |
|
|
|
|
|
|
|
|
|
|
|
if len(packages) == 0 { |
|
|
|
if len(packages) == 0 { |
|
|
|
@ -237,10 +429,14 @@ func (p *Pac) fetchAURInfo(packages []string) map[string]AURPackage { |
|
|
|
result[r.Name] = r |
|
|
|
result[r.Name] = r |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] fetchAURInfo: done (%.2fs)\n", time.Since(start).Seconds()) |
|
|
|
return result |
|
|
|
return result |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (p *Pac) InstallAUR(pkgName string) error { |
|
|
|
func (p *Pac) InstallAUR(pkgName string) error { |
|
|
|
|
|
|
|
start := time.Now() |
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] InstallAUR: starting...\n") |
|
|
|
|
|
|
|
|
|
|
|
aurInfo, ok := p.aurCache[pkgName] |
|
|
|
aurInfo, ok := p.aurCache[pkgName] |
|
|
|
if !ok { |
|
|
|
if !ok { |
|
|
|
return fmt.Errorf("AUR package not found in cache: %s", pkgName) |
|
|
|
return fmt.Errorf("AUR package not found in cache: %s", pkgName) |
|
|
|
@ -259,6 +455,7 @@ func (p *Pac) InstallAUR(pkgName string) error { |
|
|
|
if err := cloneCmd.Run(); err != nil { |
|
|
|
if err := cloneCmd.Run(); err != nil { |
|
|
|
return fmt.Errorf("failed to clone AUR repo: %w", err) |
|
|
|
return fmt.Errorf("failed to clone AUR repo: %w", err) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] InstallAUR: cloned (%.2fs)\n", time.Since(start).Seconds()) |
|
|
|
|
|
|
|
|
|
|
|
makepkgCmd := exec.Command("makepkg", "-si", "--noconfirm") |
|
|
|
makepkgCmd := exec.Command("makepkg", "-si", "--noconfirm") |
|
|
|
makepkgCmd.Stdout = os.Stdout |
|
|
|
makepkgCmd.Stdout = os.Stdout |
|
|
|
@ -267,11 +464,16 @@ func (p *Pac) InstallAUR(pkgName string) error { |
|
|
|
if err := makepkgCmd.Run(); err != nil { |
|
|
|
if err := makepkgCmd.Run(); err != nil { |
|
|
|
return fmt.Errorf("makepkg failed to build AUR package: %w", err) |
|
|
|
return fmt.Errorf("makepkg failed to build AUR package: %w", err) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] InstallAUR: built (%.2fs)\n", time.Since(start).Seconds()) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] InstallAUR: done (%.2fs)\n", time.Since(start).Seconds()) |
|
|
|
return nil |
|
|
|
return nil |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func getInstalledCount() (int, error) { |
|
|
|
func getInstalledCount() (int, error) { |
|
|
|
|
|
|
|
start := time.Now() |
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] getInstalledCount: starting...\n") |
|
|
|
|
|
|
|
|
|
|
|
cmd := exec.Command("pacman", "-Qq") |
|
|
|
cmd := exec.Command("pacman", "-Qq") |
|
|
|
output, err := cmd.Output() |
|
|
|
output, err := cmd.Output() |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
@ -281,10 +483,15 @@ func getInstalledCount() (int, error) { |
|
|
|
if strings.TrimSpace(string(output)) == "" { |
|
|
|
if strings.TrimSpace(string(output)) == "" { |
|
|
|
count = 0 |
|
|
|
count = 0 |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] getInstalledCount: done (%.2fs)\n", time.Since(start).Seconds()) |
|
|
|
return count, nil |
|
|
|
return count, nil |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (p *Pac) SyncPackages(packages []string) (int, error) { |
|
|
|
func (p *Pac) SyncPackages(packages []string) (int, error) { |
|
|
|
|
|
|
|
start := time.Now() |
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] SyncPackages: starting...\n") |
|
|
|
|
|
|
|
|
|
|
|
args := append([]string{"-Syu"}, packages...) |
|
|
|
args := append([]string{"-Syu"}, packages...) |
|
|
|
cmd := exec.Command("pacman", args...) |
|
|
|
cmd := exec.Command("pacman", args...) |
|
|
|
output, err := cmd.CombinedOutput() |
|
|
|
output, err := cmd.CombinedOutput() |
|
|
|
@ -294,10 +501,15 @@ func (p *Pac) SyncPackages(packages []string) (int, error) { |
|
|
|
|
|
|
|
|
|
|
|
re := regexp.MustCompile(`upgrading (\S+)`) |
|
|
|
re := regexp.MustCompile(`upgrading (\S+)`) |
|
|
|
matches := re.FindAllStringSubmatch(string(output), -1) |
|
|
|
matches := re.FindAllStringSubmatch(string(output), -1) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] SyncPackages: done (%.2fs)\n", time.Since(start).Seconds()) |
|
|
|
return len(matches), nil |
|
|
|
return len(matches), nil |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (p *Pac) CleanupOrphans() (int, error) { |
|
|
|
func (p *Pac) CleanupOrphans() (int, error) { |
|
|
|
|
|
|
|
start := time.Now() |
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] CleanupOrphans: starting...\n") |
|
|
|
|
|
|
|
|
|
|
|
listCmd := exec.Command("pacman", "-Qdtq") |
|
|
|
listCmd := exec.Command("pacman", "-Qdtq") |
|
|
|
orphans, err := listCmd.Output() |
|
|
|
orphans, err := listCmd.Output() |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
@ -306,6 +518,7 @@ func (p *Pac) CleanupOrphans() (int, error) { |
|
|
|
|
|
|
|
|
|
|
|
orphanList := strings.TrimSpace(string(orphans)) |
|
|
|
orphanList := strings.TrimSpace(string(orphans)) |
|
|
|
if orphanList == "" { |
|
|
|
if orphanList == "" { |
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] CleanupOrphans: done (%.2fs)\n", time.Since(start).Seconds()) |
|
|
|
return 0, nil |
|
|
|
return 0, nil |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@ -316,33 +529,41 @@ func (p *Pac) CleanupOrphans() (int, error) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
count := strings.Count(orphanList, "\n") + 1 |
|
|
|
count := strings.Count(orphanList, "\n") + 1 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] CleanupOrphans: done (%.2fs)\n", time.Since(start).Seconds()) |
|
|
|
return count, nil |
|
|
|
return count, nil |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func DryRun(packages []string) (*output.Result, error) { |
|
|
|
func DryRun(packages []string) (*output.Result, error) { |
|
|
|
|
|
|
|
start := time.Now() |
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] DryRun: starting...\n") |
|
|
|
|
|
|
|
|
|
|
|
p, err := New() |
|
|
|
p, err := New() |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
} |
|
|
|
defer p.Close() |
|
|
|
defer p.Close() |
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] DryRun: initialized pacman (%.2fs)\n", time.Since(start).Seconds()) |
|
|
|
|
|
|
|
|
|
|
|
current, err := p.GetInstalledPackages() |
|
|
|
resolved, err := p.resolvePackages(packages) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
} |
|
|
|
currentSet := make(map[string]bool) |
|
|
|
fmt.Fprintf(os.Stderr, "[debug] DryRun: packages resolved (%.2fs)\n", time.Since(start).Seconds()) |
|
|
|
for _, pkg := range current { |
|
|
|
|
|
|
|
currentSet[pkg] = true |
|
|
|
localPkgs, err := p.buildLocalPkgMap() |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return nil, err |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
var toInstall []string |
|
|
|
var toInstall []string |
|
|
|
var aurPkgs []string |
|
|
|
var aurPkgs []string |
|
|
|
for _, pkg := range packages { |
|
|
|
for _, pkg := range packages { |
|
|
|
if !currentSet[pkg] { |
|
|
|
info := resolved[pkg] |
|
|
|
info, err := p.ValidatePackage(pkg) |
|
|
|
if info == nil || !info.Exists { |
|
|
|
if err != nil || !info.Exists { |
|
|
|
return nil, fmt.Errorf("package not found: %s", pkg) |
|
|
|
return nil, fmt.Errorf("package not found: %s", pkg) |
|
|
|
} |
|
|
|
} |
|
|
|
if _, installed := localPkgs[pkg]; !installed { |
|
|
|
if info.InAUR { |
|
|
|
if info.InAUR { |
|
|
|
aurPkgs = append(aurPkgs, pkg) |
|
|
|
aurPkgs = append(aurPkgs, pkg) |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
@ -350,12 +571,15 @@ func DryRun(packages []string) (*output.Result, error) { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] DryRun: packages categorized (%.2fs)\n", time.Since(start).Seconds()) |
|
|
|
|
|
|
|
|
|
|
|
orphans, err := p.listOrphans() |
|
|
|
orphans, err := p.listOrphans() |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] DryRun: orphans listed (%.2fs)\n", time.Since(start).Seconds()) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] DryRun: done (%.2fs)\n", time.Since(start).Seconds()) |
|
|
|
return &output.Result{ |
|
|
|
return &output.Result{ |
|
|
|
Installed: len(toInstall) + len(aurPkgs), |
|
|
|
Installed: len(toInstall) + len(aurPkgs), |
|
|
|
Removed: len(orphans), |
|
|
|
Removed: len(orphans), |
|
|
|
@ -365,6 +589,9 @@ func DryRun(packages []string) (*output.Result, error) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (p *Pac) listOrphans() ([]string, error) { |
|
|
|
func (p *Pac) listOrphans() ([]string, error) { |
|
|
|
|
|
|
|
start := time.Now() |
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] listOrphans: starting...\n") |
|
|
|
|
|
|
|
|
|
|
|
cmd := exec.Command("pacman", "-Qdtq") |
|
|
|
cmd := exec.Command("pacman", "-Qdtq") |
|
|
|
orphans, err := cmd.Output() |
|
|
|
orphans, err := cmd.Output() |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
@ -373,8 +600,10 @@ func (p *Pac) listOrphans() ([]string, error) { |
|
|
|
|
|
|
|
|
|
|
|
list := strings.TrimSpace(string(orphans)) |
|
|
|
list := strings.TrimSpace(string(orphans)) |
|
|
|
if list == "" { |
|
|
|
if list == "" { |
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] listOrphans: done (%.2fs)\n", time.Since(start).Seconds()) |
|
|
|
return nil, nil |
|
|
|
return nil, nil |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "[debug] listOrphans: done (%.2fs)\n", time.Since(start).Seconds()) |
|
|
|
return strings.Split(list, "\n"), nil |
|
|
|
return strings.Split(list, "\n"), nil |
|
|
|
} |
|
|
|
} |
|
|
|
|