75 lines
1.9 KiB
Go
75 lines
1.9 KiB
Go
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")
|
|
}
|