feat(platform): add PackageManager interface and Pacman implementation
This commit is contained in:
74
internal/platform/pacman.go
Normal file
74
internal/platform/pacman.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package platform
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ExecRunner runs commands via os/exec. This is the production Runner.
|
||||
type ExecRunner struct{}
|
||||
|
||||
func (ExecRunner) Run(ctx context.Context, name string, args ...string) error {
|
||||
cmd := exec.CommandContext(ctx, name, args...)
|
||||
cmd.Stdout = nil
|
||||
cmd.Stderr = nil
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
func (ExecRunner) Output(ctx context.Context, name string, args ...string) ([]byte, error) {
|
||||
cmd := exec.CommandContext(ctx, name, args...)
|
||||
var stderr bytes.Buffer
|
||||
cmd.Stderr = &stderr
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
return out, fmt.Errorf("%s %s: %w: %s", name, strings.Join(args, " "), err, stderr.String())
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Pacman implements PackageManager via `pacman` on Arch.
|
||||
type Pacman struct {
|
||||
Runner Runner
|
||||
}
|
||||
|
||||
func NewPacman(r Runner) *Pacman {
|
||||
if r == nil {
|
||||
r = ExecRunner{}
|
||||
}
|
||||
return &Pacman{Runner: r}
|
||||
}
|
||||
|
||||
func (Pacman) Name() string { return "pacman" }
|
||||
|
||||
// IsInstalled returns true when `pacman -Q <pkg>` succeeds.
|
||||
func (p Pacman) IsInstalled(ctx context.Context, pkg string) (bool, error) {
|
||||
if pkg == "" {
|
||||
return false, errors.New("empty package name")
|
||||
}
|
||||
_, err := p.Runner.Output(ctx, "pacman", "-Q", pkg)
|
||||
if err == nil {
|
||||
return true, nil
|
||||
}
|
||||
// pacman -Q exits 1 when not installed; we treat any error as "not installed"
|
||||
// because false-positives on transient errors here are harmless (Install
|
||||
// is idempotent via --needed).
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Install runs `sudo pacman -S --needed --noconfirm <pkgs...>`.
|
||||
func (p Pacman) Install(ctx context.Context, pkgs []string) error {
|
||||
if len(pkgs) == 0 {
|
||||
return nil
|
||||
}
|
||||
args := append([]string{"pacman", "-S", "--needed", "--noconfirm"}, pkgs...)
|
||||
return p.Runner.Run(ctx, "sudo", args...)
|
||||
}
|
||||
|
||||
// Update runs `sudo pacman -Sy`.
|
||||
func (p Pacman) Update(ctx context.Context) error {
|
||||
return p.Runner.Run(ctx, "sudo", "pacman", "-Sy")
|
||||
}
|
||||
Reference in New Issue
Block a user