mirror of
https://github.com/gogrlx/snack.git
synced 2026-04-02 05:08:42 -07:00
Merge pull request #38 from gogrlx/cd/aur-implementation
feat(aur): implement native AUR client
This commit is contained in:
117
aur/aur.go
117
aur/aur.go
@@ -1,2 +1,117 @@
|
||||
// Package aur provides Go bindings for AUR (Arch User Repository) package building.
|
||||
// Package aur provides a native Go client for the Arch User Repository.
|
||||
//
|
||||
// Unlike other snack backends that wrap CLI tools, aur uses the AUR RPC API
|
||||
// directly for queries and git+makepkg for building. Packages are built in
|
||||
// a temporary directory and installed via pacman -U.
|
||||
//
|
||||
// Requirements: git, makepkg, pacman (all present on any Arch Linux system).
|
||||
package aur
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogrlx/snack"
|
||||
)
|
||||
|
||||
// AUR wraps the Arch User Repository using its RPC API and makepkg.
|
||||
type AUR struct {
|
||||
snack.Locker
|
||||
|
||||
// BuildDir is the base directory for cloning and building packages.
|
||||
// If empty, a temporary directory is created per build.
|
||||
BuildDir string
|
||||
|
||||
// MakepkgFlags are extra flags passed to makepkg (e.g. "--skippgpcheck").
|
||||
MakepkgFlags []string
|
||||
}
|
||||
|
||||
// New returns a new AUR manager with default settings.
|
||||
func New() *AUR {
|
||||
return &AUR{}
|
||||
}
|
||||
|
||||
// Option configures an AUR manager.
|
||||
type AUROption func(*AUR)
|
||||
|
||||
// WithBuildDir sets a persistent build directory.
|
||||
func WithBuildDir(dir string) AUROption {
|
||||
return func(a *AUR) { a.BuildDir = dir }
|
||||
}
|
||||
|
||||
// WithMakepkgFlags sets extra flags for makepkg.
|
||||
func WithMakepkgFlags(flags ...string) AUROption {
|
||||
return func(a *AUR) { a.MakepkgFlags = flags }
|
||||
}
|
||||
|
||||
// NewWithOptions returns a new AUR manager with the given options.
|
||||
func NewWithOptions(opts ...AUROption) *AUR {
|
||||
a := New()
|
||||
for _, opt := range opts {
|
||||
opt(a)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Name returns "aur".
|
||||
func (a *AUR) Name() string { return "aur" }
|
||||
|
||||
// Available reports whether the AUR toolchain (git, makepkg, pacman) is present.
|
||||
func (a *AUR) Available() bool { return available() }
|
||||
|
||||
// Install clones, builds, and installs AUR packages.
|
||||
func (a *AUR) Install(ctx context.Context, pkgs []snack.Target, opts ...snack.Option) (snack.InstallResult, error) {
|
||||
a.Lock()
|
||||
defer a.Unlock()
|
||||
return a.install(ctx, pkgs, opts...)
|
||||
}
|
||||
|
||||
// Remove removes packages via pacman (AUR packages are regular pacman packages once installed).
|
||||
func (a *AUR) Remove(ctx context.Context, pkgs []snack.Target, opts ...snack.Option) (snack.RemoveResult, error) {
|
||||
a.Lock()
|
||||
defer a.Unlock()
|
||||
return remove(ctx, pkgs, opts...)
|
||||
}
|
||||
|
||||
// Purge removes packages including config files via pacman.
|
||||
func (a *AUR) Purge(ctx context.Context, pkgs []snack.Target, opts ...snack.Option) error {
|
||||
a.Lock()
|
||||
defer a.Unlock()
|
||||
return purge(ctx, pkgs, opts...)
|
||||
}
|
||||
|
||||
// Upgrade rebuilds and reinstalls all foreign (AUR) packages.
|
||||
func (a *AUR) Upgrade(ctx context.Context, opts ...snack.Option) error {
|
||||
a.Lock()
|
||||
defer a.Unlock()
|
||||
return a.upgradeAll(ctx, opts...)
|
||||
}
|
||||
|
||||
// Update is a no-op for AUR (there is no local package index to refresh).
|
||||
func (a *AUR) Update(_ context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// List returns all installed foreign (non-repo) packages, which are typically AUR packages.
|
||||
func (a *AUR) List(ctx context.Context) ([]snack.Package, error) {
|
||||
return list(ctx)
|
||||
}
|
||||
|
||||
// Search queries the AUR RPC API for packages matching the query.
|
||||
func (a *AUR) Search(ctx context.Context, query string) ([]snack.Package, error) {
|
||||
return rpcSearch(ctx, query)
|
||||
}
|
||||
|
||||
// Info returns details about a specific AUR package from the RPC API.
|
||||
func (a *AUR) Info(ctx context.Context, pkg string) (*snack.Package, error) {
|
||||
return rpcInfo(ctx, pkg)
|
||||
}
|
||||
|
||||
// IsInstalled reports whether a package is currently installed.
|
||||
func (a *AUR) IsInstalled(ctx context.Context, pkg string) (bool, error) {
|
||||
return isInstalled(ctx, pkg)
|
||||
}
|
||||
|
||||
// Version returns the installed version of a package.
|
||||
func (a *AUR) Version(ctx context.Context, pkg string) (string, error) {
|
||||
return version(ctx, pkg)
|
||||
}
|
||||
|
||||
423
aur/aur_linux.go
Normal file
423
aur/aur_linux.go
Normal file
@@ -0,0 +1,423 @@
|
||||
//go:build linux
|
||||
|
||||
package aur
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
git "github.com/go-git/go-git/v5"
|
||||
"github.com/go-git/go-git/v5/plumbing/transport"
|
||||
"github.com/gogrlx/snack"
|
||||
)
|
||||
|
||||
const aurGitBase = "https://aur.archlinux.org"
|
||||
|
||||
func available() bool {
|
||||
for _, tool := range []string{"makepkg", "pacman"} {
|
||||
if _, err := exec.LookPath(tool); err != nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// runPacman executes a pacman command and returns stdout.
|
||||
func runPacman(ctx context.Context, args []string, sudo bool) (string, error) {
|
||||
cmd := "pacman"
|
||||
if sudo {
|
||||
args = append([]string{cmd}, args...)
|
||||
cmd = "sudo"
|
||||
}
|
||||
c := exec.CommandContext(ctx, cmd, args...)
|
||||
var stdout, stderr bytes.Buffer
|
||||
c.Stdout = &stdout
|
||||
c.Stderr = &stderr
|
||||
err := c.Run()
|
||||
if err != nil {
|
||||
se := stderr.String()
|
||||
if strings.Contains(se, "permission denied") || strings.Contains(se, "requires root") {
|
||||
return "", fmt.Errorf("aur: %w", snack.ErrPermissionDenied)
|
||||
}
|
||||
return "", fmt.Errorf("aur: %s: %w", strings.TrimSpace(se), err)
|
||||
}
|
||||
return stdout.String(), nil
|
||||
}
|
||||
|
||||
// install clones PKGBUILDs from the AUR, builds with makepkg, and installs with pacman.
|
||||
func (a *AUR) install(ctx context.Context, pkgs []snack.Target, opts ...snack.Option) (snack.InstallResult, error) {
|
||||
o := snack.ApplyOptions(opts...)
|
||||
|
||||
var installed []snack.Package
|
||||
var unchanged []string
|
||||
|
||||
for _, t := range pkgs {
|
||||
if !o.Reinstall && !o.DryRun {
|
||||
ok, err := isInstalled(ctx, t.Name)
|
||||
if err != nil {
|
||||
return snack.InstallResult{}, err
|
||||
}
|
||||
if ok && t.Version == "" {
|
||||
unchanged = append(unchanged, t.Name)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
pkgFile, err := a.buildPackage(ctx, t)
|
||||
if err != nil {
|
||||
return snack.InstallResult{}, fmt.Errorf("aur install %s: %w", t.Name, err)
|
||||
}
|
||||
|
||||
if o.DryRun {
|
||||
installed = append(installed, snack.Package{Name: t.Name, Repository: "aur"})
|
||||
continue
|
||||
}
|
||||
|
||||
args := []string{"-U", "--noconfirm", pkgFile}
|
||||
if _, err := runPacman(ctx, args, o.Sudo); err != nil {
|
||||
return snack.InstallResult{}, fmt.Errorf("aur install %s: %w", t.Name, err)
|
||||
}
|
||||
|
||||
v, _ := version(ctx, t.Name)
|
||||
installed = append(installed, snack.Package{
|
||||
Name: t.Name,
|
||||
Version: v,
|
||||
Repository: "aur",
|
||||
Installed: true,
|
||||
})
|
||||
}
|
||||
|
||||
return snack.InstallResult{Installed: installed, Unchanged: unchanged}, nil
|
||||
}
|
||||
|
||||
// buildPackage clones the AUR git repo for a package and runs makepkg.
|
||||
// Returns the path to the built .pkg.tar.zst file.
|
||||
func (a *AUR) buildPackage(ctx context.Context, t snack.Target) (string, error) {
|
||||
// Determine build directory
|
||||
buildDir := a.BuildDir
|
||||
if buildDir == "" {
|
||||
tmp, err := os.MkdirTemp("", "snack-aur-*")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("creating temp dir: %w", err)
|
||||
}
|
||||
buildDir = tmp
|
||||
}
|
||||
|
||||
pkgDir := filepath.Join(buildDir, t.Name)
|
||||
|
||||
// Clone or update the PKGBUILD repo
|
||||
if err := cloneOrPull(ctx, t.Name, pkgDir); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Run makepkg
|
||||
args := []string{"-s", "-f", "--noconfirm"}
|
||||
args = append(args, a.MakepkgFlags...)
|
||||
c := exec.CommandContext(ctx, "makepkg", args...)
|
||||
c.Dir = pkgDir
|
||||
var stderr bytes.Buffer
|
||||
c.Stderr = &stderr
|
||||
c.Stdout = &stderr // makepkg output goes to stderr anyway
|
||||
if err := c.Run(); err != nil {
|
||||
return "", fmt.Errorf("makepkg %s: %s: %w", t.Name, strings.TrimSpace(stderr.String()), err)
|
||||
}
|
||||
|
||||
// Find the built package file
|
||||
matches, err := filepath.Glob(filepath.Join(pkgDir, "*.pkg.tar*"))
|
||||
if err != nil || len(matches) == 0 {
|
||||
return "", fmt.Errorf("makepkg %s: no package file produced", t.Name)
|
||||
}
|
||||
return matches[len(matches)-1], nil
|
||||
}
|
||||
|
||||
// cloneOrPull clones the AUR git repo if it doesn't exist, or pulls if it does.
|
||||
func cloneOrPull(ctx context.Context, pkg, dir string) error {
|
||||
repoURL := aurGitBase + "/" + pkg + ".git"
|
||||
|
||||
if _, err := os.Stat(filepath.Join(dir, ".git")); err == nil {
|
||||
// Repo exists, pull latest
|
||||
r, err := git.PlainOpen(dir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("aur open %s: %w", pkg, err)
|
||||
}
|
||||
w, err := r.Worktree()
|
||||
if err != nil {
|
||||
return fmt.Errorf("aur worktree %s: %w", pkg, err)
|
||||
}
|
||||
if err := w.Pull(&git.PullOptions{}); err != nil && err != git.NoErrAlreadyUpToDate {
|
||||
return fmt.Errorf("aur pull %s: %w", pkg, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Clone fresh (depth 1)
|
||||
_, err := git.PlainCloneContext(ctx, dir, false, &git.CloneOptions{
|
||||
URL: repoURL,
|
||||
Depth: 1,
|
||||
})
|
||||
if err != nil {
|
||||
if err == transport.ErrRepositoryNotFound {
|
||||
return fmt.Errorf("aur clone %s: %w", pkg, snack.ErrNotFound)
|
||||
}
|
||||
return fmt.Errorf("aur clone %s: %w", pkg, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func remove(ctx context.Context, pkgs []snack.Target, opts ...snack.Option) (snack.RemoveResult, error) {
|
||||
o := snack.ApplyOptions(opts...)
|
||||
|
||||
var toRemove []snack.Target
|
||||
var unchanged []string
|
||||
for _, t := range pkgs {
|
||||
ok, err := isInstalled(ctx, t.Name)
|
||||
if err != nil {
|
||||
return snack.RemoveResult{}, err
|
||||
}
|
||||
if !ok {
|
||||
unchanged = append(unchanged, t.Name)
|
||||
} else {
|
||||
toRemove = append(toRemove, t)
|
||||
}
|
||||
}
|
||||
|
||||
if len(toRemove) > 0 {
|
||||
args := append([]string{"-R", "--noconfirm"}, snack.TargetNames(toRemove)...)
|
||||
if _, err := runPacman(ctx, args, o.Sudo); err != nil {
|
||||
return snack.RemoveResult{}, err
|
||||
}
|
||||
}
|
||||
|
||||
var removed []snack.Package
|
||||
for _, t := range toRemove {
|
||||
removed = append(removed, snack.Package{Name: t.Name})
|
||||
}
|
||||
return snack.RemoveResult{Removed: removed, Unchanged: unchanged}, nil
|
||||
}
|
||||
|
||||
func purge(ctx context.Context, pkgs []snack.Target, opts ...snack.Option) error {
|
||||
o := snack.ApplyOptions(opts...)
|
||||
args := append([]string{"-Rns", "--noconfirm"}, snack.TargetNames(pkgs)...)
|
||||
_, err := runPacman(ctx, args, o.Sudo)
|
||||
return err
|
||||
}
|
||||
|
||||
// upgradeAll rebuilds all installed foreign packages that have newer versions in the AUR.
|
||||
func (a *AUR) upgradeAll(ctx context.Context, opts ...snack.Option) error {
|
||||
upgrades, err := listUpgrades(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(upgrades) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
targets := make([]snack.Target, len(upgrades))
|
||||
for i, p := range upgrades {
|
||||
targets[i] = snack.Target{Name: p.Name}
|
||||
}
|
||||
|
||||
// Force reinstall since we're upgrading
|
||||
allOpts := append([]snack.Option{snack.WithReinstall()}, opts...)
|
||||
_, err = a.install(ctx, targets, allOpts...)
|
||||
return err
|
||||
}
|
||||
|
||||
func list(ctx context.Context) ([]snack.Package, error) {
|
||||
// pacman -Qm lists foreign (non-repo) packages, which are typically AUR
|
||||
out, err := runPacman(ctx, []string{"-Qm"}, false)
|
||||
if err != nil {
|
||||
// exit status 1 means no foreign packages
|
||||
if strings.Contains(err.Error(), "exit status 1") {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, fmt.Errorf("aur list: %w", err)
|
||||
}
|
||||
return parsePackageList(out), nil
|
||||
}
|
||||
|
||||
func isInstalled(ctx context.Context, pkg string) (bool, error) {
|
||||
c := exec.CommandContext(ctx, "pacman", "-Q", pkg)
|
||||
err := c.Run()
|
||||
if err != nil {
|
||||
if exitErr, ok := err.(*exec.ExitError); ok && exitErr.ExitCode() == 1 {
|
||||
return false, nil
|
||||
}
|
||||
return false, fmt.Errorf("aur isInstalled: %w", err)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func version(ctx context.Context, pkg string) (string, error) {
|
||||
out, err := runPacman(ctx, []string{"-Q", pkg}, false)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "exit status 1") {
|
||||
return "", fmt.Errorf("aur version %s: %w", pkg, snack.ErrNotInstalled)
|
||||
}
|
||||
return "", fmt.Errorf("aur version: %w", err)
|
||||
}
|
||||
parts := strings.Fields(strings.TrimSpace(out))
|
||||
if len(parts) < 2 {
|
||||
return "", fmt.Errorf("aur version %s: unexpected output %q", pkg, out)
|
||||
}
|
||||
return parts[1], nil
|
||||
}
|
||||
|
||||
func latestVersion(ctx context.Context, pkg string) (string, error) {
|
||||
p, err := rpcInfo(ctx, pkg)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return p.Version, nil
|
||||
}
|
||||
|
||||
func listUpgrades(ctx context.Context) ([]snack.Package, error) {
|
||||
// Get all installed foreign packages
|
||||
installed, err := list(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(installed) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Batch-query the AUR for all of them
|
||||
names := make([]string, len(installed))
|
||||
for i, p := range installed {
|
||||
names[i] = p.Name
|
||||
}
|
||||
aurInfo, err := rpcInfoMulti(ctx, names)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Compare versions
|
||||
var upgrades []snack.Package
|
||||
for _, inst := range installed {
|
||||
aurPkg, ok := aurInfo[inst.Name]
|
||||
if !ok {
|
||||
continue // not in AUR (maybe from a custom repo)
|
||||
}
|
||||
cmp, err := versionCmp(ctx, inst.Version, aurPkg.Version)
|
||||
if err != nil {
|
||||
continue // skip packages where vercmp fails
|
||||
}
|
||||
if cmp < 0 {
|
||||
upgrades = append(upgrades, snack.Package{
|
||||
Name: inst.Name,
|
||||
Version: aurPkg.Version,
|
||||
Repository: "aur",
|
||||
Installed: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
return upgrades, nil
|
||||
}
|
||||
|
||||
func upgradeAvailable(ctx context.Context, pkg string) (bool, error) {
|
||||
inst, err := version(ctx, pkg)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
latest, err := latestVersion(ctx, pkg)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
cmp, err := versionCmp(ctx, inst, latest)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return cmp < 0, nil
|
||||
}
|
||||
|
||||
func versionCmp(ctx context.Context, ver1, ver2 string) (int, error) {
|
||||
c := exec.CommandContext(ctx, "vercmp", ver1, ver2)
|
||||
out, err := c.Output()
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("vercmp: %w", err)
|
||||
}
|
||||
n, err := strconv.Atoi(strings.TrimSpace(string(out)))
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("vercmp: unexpected output %q: %w", string(out), err)
|
||||
}
|
||||
switch {
|
||||
case n < 0:
|
||||
return -1, nil
|
||||
case n > 0:
|
||||
return 1, nil
|
||||
default:
|
||||
return 0, nil
|
||||
}
|
||||
}
|
||||
|
||||
func autoremove(ctx context.Context, opts ...snack.Option) error {
|
||||
o := snack.ApplyOptions(opts...)
|
||||
|
||||
// Get orphans
|
||||
orphans, err := runPacman(ctx, []string{"-Qdtq"}, false)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "exit status 1") {
|
||||
return nil // no orphans
|
||||
}
|
||||
return fmt.Errorf("aur autoremove: %w", err)
|
||||
}
|
||||
orphans = strings.TrimSpace(orphans)
|
||||
if orphans == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
pkgs := strings.Fields(orphans)
|
||||
args := append([]string{"-Rns", "--noconfirm"}, pkgs...)
|
||||
_, err = runPacman(ctx, args, o.Sudo)
|
||||
return err
|
||||
}
|
||||
|
||||
// cleanBuildDir removes all subdirectories in the build directory.
|
||||
func (a *AUR) cleanBuildDir() error {
|
||||
if a.BuildDir == "" {
|
||||
return nil // temp dirs are cleaned automatically
|
||||
}
|
||||
entries, err := os.ReadDir(a.BuildDir)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("aur clean: %w", err)
|
||||
}
|
||||
for _, e := range entries {
|
||||
if e.IsDir() {
|
||||
if err := os.RemoveAll(filepath.Join(a.BuildDir, e.Name())); err != nil {
|
||||
return fmt.Errorf("aur clean %s: %w", e.Name(), err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// parsePackageList parses "name version" lines from pacman -Q output.
|
||||
func parsePackageList(output string) []snack.Package {
|
||||
var pkgs []snack.Package
|
||||
for _, line := range strings.Split(output, "\n") {
|
||||
line = strings.TrimSpace(line)
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
parts := strings.Fields(line)
|
||||
if len(parts) < 2 {
|
||||
continue
|
||||
}
|
||||
pkgs = append(pkgs, snack.Package{
|
||||
Name: parts[0],
|
||||
Version: parts[1],
|
||||
Repository: "aur",
|
||||
Installed: true,
|
||||
})
|
||||
}
|
||||
return pkgs
|
||||
}
|
||||
63
aur/aur_other.go
Normal file
63
aur/aur_other.go
Normal file
@@ -0,0 +1,63 @@
|
||||
//go:build !linux
|
||||
|
||||
package aur
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogrlx/snack"
|
||||
)
|
||||
|
||||
func available() bool { return false }
|
||||
|
||||
func (a *AUR) install(_ context.Context, _ []snack.Target, _ ...snack.Option) (snack.InstallResult, error) {
|
||||
return snack.InstallResult{}, snack.ErrUnsupportedPlatform
|
||||
}
|
||||
|
||||
func remove(_ context.Context, _ []snack.Target, _ ...snack.Option) (snack.RemoveResult, error) {
|
||||
return snack.RemoveResult{}, snack.ErrUnsupportedPlatform
|
||||
}
|
||||
|
||||
func purge(_ context.Context, _ []snack.Target, _ ...snack.Option) error {
|
||||
return snack.ErrUnsupportedPlatform
|
||||
}
|
||||
|
||||
func (a *AUR) upgradeAll(_ context.Context, _ ...snack.Option) error {
|
||||
return snack.ErrUnsupportedPlatform
|
||||
}
|
||||
|
||||
func list(_ context.Context) ([]snack.Package, error) {
|
||||
return nil, snack.ErrUnsupportedPlatform
|
||||
}
|
||||
|
||||
func isInstalled(_ context.Context, _ string) (bool, error) {
|
||||
return false, snack.ErrUnsupportedPlatform
|
||||
}
|
||||
|
||||
func version(_ context.Context, _ string) (string, error) {
|
||||
return "", snack.ErrUnsupportedPlatform
|
||||
}
|
||||
|
||||
func latestVersion(_ context.Context, _ string) (string, error) {
|
||||
return "", snack.ErrUnsupportedPlatform
|
||||
}
|
||||
|
||||
func listUpgrades(_ context.Context) ([]snack.Package, error) {
|
||||
return nil, snack.ErrUnsupportedPlatform
|
||||
}
|
||||
|
||||
func upgradeAvailable(_ context.Context, _ string) (bool, error) {
|
||||
return false, snack.ErrUnsupportedPlatform
|
||||
}
|
||||
|
||||
func versionCmp(_ context.Context, _, _ string) (int, error) {
|
||||
return 0, snack.ErrUnsupportedPlatform
|
||||
}
|
||||
|
||||
func autoremove(_ context.Context, _ ...snack.Option) error {
|
||||
return snack.ErrUnsupportedPlatform
|
||||
}
|
||||
|
||||
func (a *AUR) cleanBuildDir() error {
|
||||
return snack.ErrUnsupportedPlatform
|
||||
}
|
||||
65
aur/aur_test.go
Normal file
65
aur/aur_test.go
Normal file
@@ -0,0 +1,65 @@
|
||||
package aur
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestParsePackageList(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
expect int
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
input: "",
|
||||
expect: 0,
|
||||
},
|
||||
{
|
||||
name: "single package",
|
||||
input: "yay 12.5.7-1\n",
|
||||
expect: 1,
|
||||
},
|
||||
{
|
||||
name: "multiple packages",
|
||||
input: "yay 12.5.7-1\nparu 2.0.4-1\naur-helper 1.0-1\n",
|
||||
expect: 3,
|
||||
},
|
||||
{
|
||||
name: "trailing whitespace",
|
||||
input: "yay 12.5.7-1 \n paru 2.0.4-1\n\n",
|
||||
expect: 2,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
pkgs := parsePackageList(tt.input)
|
||||
assert.Len(t, pkgs, tt.expect)
|
||||
for _, p := range pkgs {
|
||||
assert.NotEmpty(t, p.Name)
|
||||
assert.NotEmpty(t, p.Version)
|
||||
assert.Equal(t, "aur", p.Repository)
|
||||
assert.True(t, p.Installed)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
a := New()
|
||||
assert.Equal(t, "aur", a.Name())
|
||||
assert.Empty(t, a.BuildDir)
|
||||
assert.Nil(t, a.MakepkgFlags)
|
||||
}
|
||||
|
||||
func TestNewWithOptions(t *testing.T) {
|
||||
a := NewWithOptions(
|
||||
WithBuildDir("/tmp/aur-builds"),
|
||||
WithMakepkgFlags("--skippgpcheck", "--nocheck"),
|
||||
)
|
||||
assert.Equal(t, "/tmp/aur-builds", a.BuildDir)
|
||||
assert.Equal(t, []string{"--skippgpcheck", "--nocheck"}, a.MakepkgFlags)
|
||||
}
|
||||
54
aur/capabilities.go
Normal file
54
aur/capabilities.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package aur
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogrlx/snack"
|
||||
)
|
||||
|
||||
// Compile-time interface checks.
|
||||
var (
|
||||
_ snack.Manager = (*AUR)(nil)
|
||||
_ snack.VersionQuerier = (*AUR)(nil)
|
||||
_ snack.Cleaner = (*AUR)(nil)
|
||||
_ snack.PackageUpgrader = (*AUR)(nil)
|
||||
)
|
||||
|
||||
// LatestVersion returns the latest version available in the AUR.
|
||||
func (a *AUR) LatestVersion(ctx context.Context, pkg string) (string, error) {
|
||||
return latestVersion(ctx, pkg)
|
||||
}
|
||||
|
||||
// ListUpgrades returns installed foreign packages that have newer versions in the AUR.
|
||||
func (a *AUR) ListUpgrades(ctx context.Context) ([]snack.Package, error) {
|
||||
return listUpgrades(ctx)
|
||||
}
|
||||
|
||||
// UpgradeAvailable reports whether a newer version is available in the AUR.
|
||||
func (a *AUR) UpgradeAvailable(ctx context.Context, pkg string) (bool, error) {
|
||||
return upgradeAvailable(ctx, pkg)
|
||||
}
|
||||
|
||||
// VersionCmp compares two version strings using pacman's vercmp.
|
||||
func (a *AUR) VersionCmp(ctx context.Context, ver1, ver2 string) (int, error) {
|
||||
return versionCmp(ctx, ver1, ver2)
|
||||
}
|
||||
|
||||
// Autoremove removes orphaned packages via pacman.
|
||||
func (a *AUR) Autoremove(ctx context.Context, opts ...snack.Option) error {
|
||||
a.Lock()
|
||||
defer a.Unlock()
|
||||
return autoremove(ctx, opts...)
|
||||
}
|
||||
|
||||
// Clean removes cached build artifacts from the build directory.
|
||||
func (a *AUR) Clean(_ context.Context) error {
|
||||
return a.cleanBuildDir()
|
||||
}
|
||||
|
||||
// UpgradePackages rebuilds and reinstalls specific AUR packages.
|
||||
func (a *AUR) UpgradePackages(ctx context.Context, pkgs []snack.Target, opts ...snack.Option) (snack.InstallResult, error) {
|
||||
a.Lock()
|
||||
defer a.Unlock()
|
||||
return a.install(ctx, pkgs, opts...)
|
||||
}
|
||||
139
aur/rpc.go
Normal file
139
aur/rpc.go
Normal file
@@ -0,0 +1,139 @@
|
||||
package aur
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/gogrlx/snack"
|
||||
)
|
||||
|
||||
const rpcBaseURL = "https://aur.archlinux.org/rpc/v5"
|
||||
|
||||
// rpcResponse is the top-level AUR RPC response.
|
||||
type rpcResponse struct {
|
||||
ResultCount int `json:"resultcount"`
|
||||
Results []rpcResult `json:"results"`
|
||||
Type string `json:"type"`
|
||||
Error string `json:"error,omitempty"`
|
||||
Version int `json:"version"`
|
||||
}
|
||||
|
||||
// rpcResult is a single package from the AUR RPC API.
|
||||
type rpcResult struct {
|
||||
Name string `json:"Name"`
|
||||
Version string `json:"Version"`
|
||||
Description string `json:"Description"`
|
||||
URL string `json:"URL"`
|
||||
URLPath string `json:"URLPath"`
|
||||
PackageBase string `json:"PackageBase"`
|
||||
PackageBaseID int `json:"PackageBaseID"`
|
||||
NumVotes int `json:"NumVotes"`
|
||||
Popularity float64 `json:"Popularity"`
|
||||
OutOfDate *int64 `json:"OutOfDate"`
|
||||
Maintainer string `json:"Maintainer"`
|
||||
FirstSubmitted int64 `json:"FirstSubmitted"`
|
||||
LastModified int64 `json:"LastModified"`
|
||||
Depends []string `json:"Depends"`
|
||||
MakeDepends []string `json:"MakeDepends"`
|
||||
OptDepends []string `json:"OptDepends"`
|
||||
License []string `json:"License"`
|
||||
Keywords []string `json:"Keywords"`
|
||||
}
|
||||
|
||||
func (r *rpcResult) toPackage() snack.Package {
|
||||
return snack.Package{
|
||||
Name: r.Name,
|
||||
Version: r.Version,
|
||||
Description: r.Description,
|
||||
Repository: "aur",
|
||||
}
|
||||
}
|
||||
|
||||
// rpcGet performs an AUR RPC API request.
|
||||
func rpcGet(ctx context.Context, endpoint string) (*rpcResponse, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("aur rpc: %w", err)
|
||||
}
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("aur rpc: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("aur rpc: HTTP %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("aur rpc: reading response: %w", err)
|
||||
}
|
||||
|
||||
var result rpcResponse
|
||||
if err := json.Unmarshal(body, &result); err != nil {
|
||||
return nil, fmt.Errorf("aur rpc: parsing response: %w", err)
|
||||
}
|
||||
|
||||
if result.Error != "" {
|
||||
return nil, fmt.Errorf("aur rpc: %s", result.Error)
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// rpcSearch queries the AUR for packages matching the query string.
|
||||
func rpcSearch(ctx context.Context, query string) ([]snack.Package, error) {
|
||||
endpoint := rpcBaseURL + "/search/" + url.PathEscape(query)
|
||||
resp, err := rpcGet(ctx, endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pkgs := make([]snack.Package, 0, len(resp.Results))
|
||||
for _, r := range resp.Results {
|
||||
pkgs = append(pkgs, r.toPackage())
|
||||
}
|
||||
return pkgs, nil
|
||||
}
|
||||
|
||||
// rpcInfo returns info about a specific AUR package.
|
||||
func rpcInfo(ctx context.Context, pkg string) (*snack.Package, error) {
|
||||
endpoint := rpcBaseURL + "/info?arg[]=" + url.QueryEscape(pkg)
|
||||
resp, err := rpcGet(ctx, endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp.ResultCount == 0 {
|
||||
return nil, fmt.Errorf("aur info %s: %w", pkg, snack.ErrNotFound)
|
||||
}
|
||||
p := resp.Results[0].toPackage()
|
||||
return &p, nil
|
||||
}
|
||||
|
||||
// rpcInfoMulti returns info about multiple AUR packages in a single request.
|
||||
func rpcInfoMulti(ctx context.Context, pkgs []string) (map[string]rpcResult, error) {
|
||||
if len(pkgs) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
params := make([]string, len(pkgs))
|
||||
for i, p := range pkgs {
|
||||
params[i] = "arg[]=" + url.QueryEscape(p)
|
||||
}
|
||||
endpoint := rpcBaseURL + "/info?" + strings.Join(params, "&")
|
||||
resp, err := rpcGet(ctx, endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := make(map[string]rpcResult, len(resp.Results))
|
||||
for _, r := range resp.Results {
|
||||
result[r.Name] = r
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
184
aur/rpc_test.go
Normal file
184
aur/rpc_test.go
Normal file
@@ -0,0 +1,184 @@
|
||||
package aur
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestRPCSearch(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
resp := rpcResponse{
|
||||
ResultCount: 2,
|
||||
Results: []rpcResult{
|
||||
{Name: "yay", Version: "12.5.7-1", Description: "AUR helper"},
|
||||
{Name: "yay-bin", Version: "12.5.7-1", Description: "AUR helper (binary)"},
|
||||
},
|
||||
Type: "search",
|
||||
Version: 5,
|
||||
}
|
||||
json.NewEncoder(w).Encode(resp)
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
// Override the base URL for testing — we need to test the parsing
|
||||
// Since we can't easily override the const, test the JSON parsing directly
|
||||
var resp rpcResponse
|
||||
httpResp, err := http.Get(srv.URL)
|
||||
require.NoError(t, err)
|
||||
defer httpResp.Body.Close()
|
||||
require.NoError(t, json.NewDecoder(httpResp.Body).Decode(&resp))
|
||||
|
||||
assert.Equal(t, 2, resp.ResultCount)
|
||||
assert.Equal(t, "yay", resp.Results[0].Name)
|
||||
assert.Equal(t, "12.5.7-1", resp.Results[0].Version)
|
||||
|
||||
pkg := resp.Results[0].toPackage()
|
||||
assert.Equal(t, "yay", pkg.Name)
|
||||
assert.Equal(t, "12.5.7-1", pkg.Version)
|
||||
assert.Equal(t, "aur", pkg.Repository)
|
||||
}
|
||||
|
||||
func TestRPCInfo(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
resp := rpcResponse{
|
||||
ResultCount: 1,
|
||||
Results: []rpcResult{
|
||||
{
|
||||
Name: "yay",
|
||||
Version: "12.5.7-1",
|
||||
Description: "Yet another yogurt",
|
||||
Depends: []string{"pacman>6.1", "git"},
|
||||
MakeDepends: []string{"go>=1.24"},
|
||||
},
|
||||
},
|
||||
Type: "multiinfo",
|
||||
Version: 5,
|
||||
}
|
||||
json.NewEncoder(w).Encode(resp)
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
var resp rpcResponse
|
||||
httpResp, err := http.Get(srv.URL)
|
||||
require.NoError(t, err)
|
||||
defer httpResp.Body.Close()
|
||||
require.NoError(t, json.NewDecoder(httpResp.Body).Decode(&resp))
|
||||
|
||||
assert.Equal(t, 1, resp.ResultCount)
|
||||
r := resp.Results[0]
|
||||
assert.Equal(t, "yay", r.Name)
|
||||
assert.Equal(t, []string{"pacman>6.1", "git"}, r.Depends)
|
||||
assert.Equal(t, []string{"go>=1.24"}, r.MakeDepends)
|
||||
}
|
||||
|
||||
func TestRPCError(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
resp := rpcResponse{
|
||||
Error: "Incorrect request type specified.",
|
||||
Type: "error",
|
||||
Version: 5,
|
||||
}
|
||||
json.NewEncoder(w).Encode(resp)
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
var resp rpcResponse
|
||||
httpResp, err := http.Get(srv.URL)
|
||||
require.NoError(t, err)
|
||||
defer httpResp.Body.Close()
|
||||
require.NoError(t, json.NewDecoder(httpResp.Body).Decode(&resp))
|
||||
|
||||
assert.Equal(t, "Incorrect request type specified.", resp.Error)
|
||||
}
|
||||
|
||||
func TestRPCInfoMulti(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
resp := rpcResponse{
|
||||
ResultCount: 2,
|
||||
Results: []rpcResult{
|
||||
{Name: "yay", Version: "12.5.7-1"},
|
||||
{Name: "paru", Version: "2.0.4-1"},
|
||||
},
|
||||
Type: "multiinfo",
|
||||
Version: 5,
|
||||
}
|
||||
json.NewEncoder(w).Encode(resp)
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
var resp rpcResponse
|
||||
httpResp, err := http.Get(srv.URL)
|
||||
require.NoError(t, err)
|
||||
defer httpResp.Body.Close()
|
||||
require.NoError(t, json.NewDecoder(httpResp.Body).Decode(&resp))
|
||||
|
||||
assert.Equal(t, 2, resp.ResultCount)
|
||||
|
||||
// Simulate rpcInfoMulti result building
|
||||
result := make(map[string]rpcResult, len(resp.Results))
|
||||
for _, r := range resp.Results {
|
||||
result[r.Name] = r
|
||||
}
|
||||
assert.Equal(t, "12.5.7-1", result["yay"].Version)
|
||||
assert.Equal(t, "2.0.4-1", result["paru"].Version)
|
||||
}
|
||||
|
||||
func TestToPackage(t *testing.T) {
|
||||
r := rpcResult{
|
||||
Name: "paru",
|
||||
Version: "2.0.4-1",
|
||||
Description: "Feature packed AUR helper",
|
||||
URL: "https://github.com/Morganamilo/paru",
|
||||
}
|
||||
pkg := r.toPackage()
|
||||
assert.Equal(t, "paru", pkg.Name)
|
||||
assert.Equal(t, "2.0.4-1", pkg.Version)
|
||||
assert.Equal(t, "Feature packed AUR helper", pkg.Description)
|
||||
assert.Equal(t, "aur", pkg.Repository)
|
||||
assert.False(t, pkg.Installed) // AUR search results aren't installed
|
||||
}
|
||||
|
||||
func TestRPCSearchLive(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping live AUR API test")
|
||||
}
|
||||
pkgs, err := rpcSearch(context.Background(), "yay")
|
||||
require.NoError(t, err)
|
||||
assert.NotEmpty(t, pkgs)
|
||||
|
||||
found := false
|
||||
for _, p := range pkgs {
|
||||
if p.Name == "yay" {
|
||||
found = true
|
||||
assert.NotEmpty(t, p.Version)
|
||||
assert.Equal(t, "aur", p.Repository)
|
||||
break
|
||||
}
|
||||
}
|
||||
assert.True(t, found, "expected to find 'yay' in AUR search results")
|
||||
}
|
||||
|
||||
func TestRPCInfoLive(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping live AUR API test")
|
||||
}
|
||||
pkg, err := rpcInfo(context.Background(), "yay")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "yay", pkg.Name)
|
||||
assert.NotEmpty(t, pkg.Version)
|
||||
}
|
||||
|
||||
func TestRPCInfoNotFound(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping live AUR API test")
|
||||
}
|
||||
_, err := rpcInfo(context.Background(), "this-package-definitely-does-not-exist-12345")
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "not found")
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"github.com/gogrlx/snack"
|
||||
"github.com/gogrlx/snack/apk"
|
||||
"github.com/gogrlx/snack/apt"
|
||||
"github.com/gogrlx/snack/aur"
|
||||
"github.com/gogrlx/snack/dnf"
|
||||
"github.com/gogrlx/snack/flatpak"
|
||||
"github.com/gogrlx/snack/pacman"
|
||||
@@ -26,6 +27,7 @@ func candidates() []managerFactory {
|
||||
}
|
||||
|
||||
// allManagers returns all known manager factories (for ByName).
|
||||
// Includes supplemental managers like AUR that aren't primary candidates.
|
||||
func allManagers() []managerFactory {
|
||||
return candidates()
|
||||
return append(candidates(), func() snack.Manager { return aur.New() })
|
||||
}
|
||||
|
||||
16
go.mod
16
go.mod
@@ -4,6 +4,7 @@ go 1.26.0
|
||||
|
||||
require (
|
||||
github.com/charmbracelet/fang v0.4.4
|
||||
github.com/go-git/go-git/v5 v5.17.0
|
||||
github.com/spf13/cobra v1.10.2
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/testcontainers/testcontainers-go v0.40.0
|
||||
@@ -14,6 +15,7 @@ require (
|
||||
dario.cat/mergo v1.0.2 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/ProtonMail/go-crypto v1.1.6 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/charmbracelet/colorprofile v0.4.2 // indirect
|
||||
@@ -25,23 +27,31 @@ require (
|
||||
github.com/charmbracelet/x/windows v0.2.2 // indirect
|
||||
github.com/clipperhouse/displaywidth v0.11.0 // indirect
|
||||
github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
|
||||
github.com/cloudflare/circl v1.6.1 // indirect
|
||||
github.com/containerd/errdefs v1.0.0 // indirect
|
||||
github.com/containerd/errdefs/pkg v0.3.0 // indirect
|
||||
github.com/containerd/log v0.1.0 // indirect
|
||||
github.com/containerd/platforms v0.2.1 // indirect
|
||||
github.com/cpuguy83/dockercfg v0.3.2 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/distribution/reference v0.6.0 // indirect
|
||||
github.com/docker/docker v28.5.1+incompatible // indirect
|
||||
github.com/docker/go-connections v0.6.0 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/ebitengine/purego v0.8.4 // indirect
|
||||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.8.0 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||
@@ -62,15 +72,19 @@ require (
|
||||
github.com/muesli/roff v0.1.0 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.1 // indirect
|
||||
github.com/pjbgf/sha1cd v0.3.2 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
|
||||
github.com/shirou/gopsutil/v4 v4.25.6 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/skeema/knownhosts v1.3.1 // indirect
|
||||
github.com/spf13/pflag v1.0.10 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.12 // indirect
|
||||
github.com/tklauser/numcpus v0.6.1 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
@@ -82,9 +96,11 @@ require (
|
||||
go.opentelemetry.io/otel/trace v1.40.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.9.0 // indirect
|
||||
golang.org/x/crypto v0.45.0 // indirect
|
||||
golang.org/x/net v0.47.0 // indirect
|
||||
golang.org/x/sync v0.19.0 // indirect
|
||||
golang.org/x/sys v0.41.0 // indirect
|
||||
golang.org/x/text v0.34.0 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
66
go.sum
66
go.sum
@@ -6,8 +6,15 @@ github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8af
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw=
|
||||
github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||
github.com/aymanbagabas/go-udiff v0.4.0 h1:TKnLPh7IbnizJIBKFWa9mKayRUBQ9Kh1BPCk6w2PnYM=
|
||||
github.com/aymanbagabas/go-udiff v0.4.0/go.mod h1:0L9PGwj20lrtmEMeyw4WKJ/TMyDtvAoK9bf2u/mNo3w=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||
@@ -36,6 +43,8 @@ github.com/clipperhouse/displaywidth v0.11.0 h1:lBc6kY44VFw+TDx4I8opi/EtL9m20WSE
|
||||
github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0=
|
||||
github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk=
|
||||
github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
|
||||
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
|
||||
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
|
||||
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
|
||||
github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
|
||||
github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=
|
||||
@@ -49,6 +58,8 @@ github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHf
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
|
||||
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s=
|
||||
github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -62,8 +73,22 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4
|
||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw=
|
||||
github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||
github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
|
||||
github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
|
||||
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
||||
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
|
||||
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
|
||||
github.com/go-git/go-billy/v5 v5.8.0 h1:I8hjc3LbBlXTtVuFNJuwYuMiHvQJDq1AT6u4DwDzZG0=
|
||||
github.com/go-git/go-billy/v5 v5.8.0/go.mod h1:RpvI/rw4Vr5QA+Z60c6d6LXH0rYJo0uD5SqfmrrheCY=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
|
||||
github.com/go-git/go-git/v5 v5.17.0 h1:AbyI4xf+7DsjINHMu35quAh4wJygKBKBuXVjV/pxesM=
|
||||
github.com/go-git/go-git/v5 v5.17.0/go.mod h1:f82C4YiLx+Lhi8eHxltLeGC5uBTXSFa6PC5WW9o4SjI=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
@@ -71,6 +96,8 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
@@ -80,10 +107,17 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnV
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
|
||||
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag=
|
||||
@@ -122,10 +156,14 @@ github.com/muesli/mango-pflag v0.2.0 h1:QViokgKDZQCzKhYe1zH8D+UlPJzBSGoP9yx0hBG0
|
||||
github.com/muesli/mango-pflag v0.2.0/go.mod h1:X9LT1p/pbGA1wjvEbtwnixujKErkP0jVmrxwrw3fL0Y=
|
||||
github.com/muesli/roff v0.1.0 h1:YD0lalCotmYuF5HhZliKWlIx7IEhiXeSfq7hNjFqGF8=
|
||||
github.com/muesli/roff v0.1.0/go.mod h1:pjAHQM9hdUUwm/krAfrLGgJkXJ+YuhtsfZ42kieB2Ig=
|
||||
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
|
||||
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
|
||||
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
|
||||
github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4=
|
||||
github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
@@ -137,10 +175,15 @@ github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUc
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||
github.com/shirou/gopsutil/v4 v4.25.6 h1:kLysI2JsKorfaFPcYmcJqbzROzsBWEOAtw6A7dIfqXs=
|
||||
github.com/shirou/gopsutil/v4 v4.25.6/go.mod h1:PfybzyydfZcN+JMMjkF6Zb8Mq1A/VcogFFg7hj50W9c=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8=
|
||||
github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY=
|
||||
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
|
||||
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
|
||||
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
@@ -149,6 +192,8 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
@@ -158,6 +203,8 @@ github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFA
|
||||
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
|
||||
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
|
||||
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
|
||||
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
|
||||
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||
@@ -181,28 +228,38 @@ go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTq
|
||||
go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A=
|
||||
go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
||||
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
|
||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
|
||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
|
||||
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
|
||||
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
|
||||
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
|
||||
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44=
|
||||
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 h1:BIRfGDEjiHRrk0QKZe3Xv2ieMhtgRGeLcZQ0mIVn4EY=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5/go.mod h1:j3QtIyytwqGr1JUDtYXwtMXWPKsEa5LtzIFN1Wn5WvE=
|
||||
@@ -213,8 +270,13 @@ google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
|
||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
Reference in New Issue
Block a user