You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
179 lines
3.6 KiB
179 lines
3.6 KiB
package pacman |
|
|
|
import ( |
|
"fmt" |
|
"os" |
|
"os/exec" |
|
"regexp" |
|
"strings" |
|
"time" |
|
|
|
"github.com/Riyyi/declpac/pkg/output" |
|
) |
|
|
|
var ( |
|
Root = "/" |
|
LockFile = "/var/lib/pacman/db.lock" |
|
) |
|
|
|
type Pac struct{} |
|
|
|
func New() (*Pac, error) { |
|
return &Pac{}, nil |
|
} |
|
|
|
func (p *Pac) Close() error { |
|
return nil |
|
} |
|
|
|
type PackageInfo struct { |
|
Name string |
|
InAUR bool |
|
Exists bool |
|
} |
|
|
|
func (p *Pac) ValidatePackage(name string) (*PackageInfo, error) { |
|
cmd := exec.Command("pacman", "-Qip", name) |
|
if err := cmd.Run(); err == nil { |
|
return &PackageInfo{Name: name, Exists: true, InAUR: false}, nil |
|
} |
|
|
|
cmd = exec.Command("pacman", "-Sip", name) |
|
if err := cmd.Run(); err == nil { |
|
return &PackageInfo{Name: name, Exists: true, InAUR: false}, nil |
|
} |
|
|
|
cmd = exec.Command("aur", "search", name) |
|
if out, err := cmd.Output(); err == nil && len(out) > 0 { |
|
return &PackageInfo{Name: name, Exists: true, InAUR: true}, nil |
|
} |
|
|
|
return &PackageInfo{Name: name, Exists: false, InAUR: false}, nil |
|
} |
|
|
|
func (p *Pac) IsDBFresh() (bool, error) { |
|
info, err := os.Stat(LockFile) |
|
if err != nil { |
|
return false, nil |
|
} |
|
|
|
age := time.Since(info.ModTime()) |
|
return age < 24*time.Hour, nil |
|
} |
|
|
|
func (p *Pac) SyncDB() error { |
|
cmd := exec.Command("pacman", "-Syy") |
|
cmd.Stdout = os.Stdout |
|
cmd.Stderr = os.Stderr |
|
return cmd.Run() |
|
} |
|
|
|
func (p *Pac) GetInstalledPackages() ([]string, error) { |
|
cmd := exec.Command("pacman", "-Qq") |
|
output, err := cmd.Output() |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
packages := strings.Split(strings.TrimSpace(string(output)), "\n") |
|
return packages, nil |
|
} |
|
|
|
func (p *Pac) MarkExplicit(pkgName string) error { |
|
cmd := exec.Command("pacman", "-D", "--explicit", pkgName) |
|
cmd.Stdout = os.Stdout |
|
cmd.Stderr = os.Stderr |
|
return cmd.Run() |
|
} |
|
|
|
func Sync(packages []string) (*output.Result, error) { |
|
before, err := getInstalledCount() |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
p, err := New() |
|
if err != nil { |
|
return nil, err |
|
} |
|
defer p.Close() |
|
|
|
fresh, err := p.IsDBFresh() |
|
if err != nil || !fresh { |
|
if err := p.SyncDB(); err != nil { |
|
return nil, fmt.Errorf("failed to sync database: %w", err) |
|
} |
|
} |
|
|
|
for _, pkg := range packages { |
|
if err := p.MarkExplicit(pkg); err != nil { |
|
fmt.Fprintf(os.Stderr, "warning: could not mark %s as explicit: %v\n", pkg, err) |
|
} |
|
} |
|
|
|
_, err = p.SyncPackages(packages) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
removed, err := p.CleanupOrphans() |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
after, _ := getInstalledCount() |
|
installedCount := max(after - before, 0) |
|
|
|
return &output.Result{ |
|
Installed: installedCount, |
|
Removed: removed, |
|
}, nil |
|
} |
|
|
|
func getInstalledCount() (int, error) { |
|
cmd := exec.Command("pacman", "-Qq") |
|
output, err := cmd.Output() |
|
if err != nil { |
|
return 0, nil |
|
} |
|
count := strings.Count(string(output), "\n") + 1 |
|
if strings.TrimSpace(string(output)) == "" { |
|
count = 0 |
|
} |
|
return count, nil |
|
} |
|
|
|
func (p *Pac) SyncPackages(packages []string) (int, error) { |
|
args := append([]string{"-Syu"}, packages...) |
|
cmd := exec.Command("pacman", args...) |
|
output, err := cmd.CombinedOutput() |
|
if err != nil { |
|
return 0, fmt.Errorf("pacman sync failed: %s", output) |
|
} |
|
|
|
re := regexp.MustCompile(`upgrading (\S+)`) |
|
matches := re.FindAllStringSubmatch(string(output), -1) |
|
return len(matches), nil |
|
} |
|
|
|
func (p *Pac) CleanupOrphans() (int, error) { |
|
listCmd := exec.Command("pacman", "-Qdtq") |
|
orphans, err := listCmd.Output() |
|
if err != nil { |
|
return 0, nil |
|
} |
|
|
|
orphanList := strings.TrimSpace(string(orphans)) |
|
if orphanList == "" { |
|
return 0, nil |
|
} |
|
|
|
removeCmd := exec.Command("pacman", "-Rns") |
|
output, err := removeCmd.CombinedOutput() |
|
if err != nil { |
|
return 0, fmt.Errorf("%s: %s", err, output) |
|
} |
|
|
|
count := strings.Count(orphanList, "\n") + 1 |
|
return count, nil |
|
}
|
|
|