diff --git a/README.md b/README.md index 2f27484..f09ef65 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Part of the [grlx](https://github.com/gogrlx/grlx) ecosystem. | Package | Manager | Platform | Status | |---------|---------|----------|--------| | `pacman` | pacman | Arch Linux | ✅ | -| `aur` | AUR (makepkg) | Arch Linux | ✅ | +| `aur` | AUR (RPC + makepkg) | Arch Linux | ✅ | | `apk` | apk-tools | Alpine Linux | ✅ | | `apt` | APT (apt-get/apt-cache) | Debian/Ubuntu | ✅ | | `dpkg` | dpkg | Debian/Ubuntu | ✅ | @@ -34,7 +34,7 @@ Part of the [grlx](https://github.com/gogrlx/grlx) ecosystem. |----------|:------------:|:----:|:-----:|:---------:|:--------:|:-------:|:------:|:--------:|:------:|:----------:| | apt | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | - | ✅ | ✅ | ✅ | | pacman | ✅ | - | ✅ | ✅ | - | - | ✅ | ✅ | ✅ | ✅ | -| aur | ✅ | - | ✅ | - | - | - | - | ✅ | - | ✅ | +| aur | ✅ | - | ✅ | - | - | - | - | - | - | ✅ | | apk | ✅ | - | ✅ | ✅ | - | - | - | ✅ | ✅ | ✅ | | dnf | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | flatpak | ✅ | - | ✅ | - | ✅ | - | - | ✅ | - | ✅ | diff --git a/aur/aur.go b/aur/aur.go index f32e0d3..ab2162a 100644 --- a/aur/aur.go +++ b/aur/aur.go @@ -1,5 +1,10 @@ -// Package aur provides Go bindings for AUR (Arch User Repository) package building. -// AUR packages are built from source using makepkg. +// 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 ( @@ -8,91 +13,105 @@ import ( "github.com/gogrlx/snack" ) -// AUR wraps makepkg and AUR helper tools for building packages from the AUR. +// 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. +// New returns a new AUR manager with default settings. func New() *AUR { return &AUR{} } -// Compile-time interface checks. -var ( - _ snack.Manager = (*AUR)(nil) - _ snack.VersionQuerier = (*AUR)(nil) - _ snack.Cleaner = (*AUR)(nil) - _ snack.PackageUpgrader = (*AUR)(nil) - _ snack.NameNormalizer = (*AUR)(nil) -) +// 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 makepkg is present on the system. +// Available reports whether the AUR toolchain (git, makepkg, pacman) is present. func (a *AUR) Available() bool { return available() } -// Install one or more packages from the AUR. +// 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 install(ctx, pkgs, opts...) + return a.install(ctx, pkgs, opts...) } -// Remove is not directly supported by AUR (use pacman). +// 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 is not directly supported by AUR (use pacman). +// 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 all AUR packages (requires re-building from source). +// 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 upgrade(ctx, opts...) + return a.upgradeAll(ctx, opts...) } -// Update is a no-op for AUR (packages are fetched on demand). -func (a *AUR) Update(ctx context.Context) error { - return update(ctx) +// 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 installed packages that came from the AUR. +// 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 for packages matching the query. +// Search queries the AUR RPC API for packages matching the query. func (a *AUR) Search(ctx context.Context, query string) ([]snack.Package, error) { - return search(ctx, query) + return rpcSearch(ctx, query) } -// Info returns details about a specific AUR package. +// 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 info(ctx, pkg) + return rpcInfo(ctx, pkg) } -// IsInstalled reports whether a package from the AUR is currently installed. +// 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 an AUR package. +// Version returns the installed version of a package. func (a *AUR) Version(ctx context.Context, pkg string) (string, error) { return version(ctx, pkg) } - -// NormalizeName returns the canonical form of an AUR package name. -func (a *AUR) NormalizeName(name string) string { - return normalizeName(name) -} - -// ParseArch extracts the architecture from a package name if present. -func (a *AUR) ParseArch(name string) (string, string) { - return parseArch(name) -} diff --git a/aur/aur_linux.go b/aur/aur_linux.go index 616f4c5..88624df 100644 --- a/aur/aur_linux.go +++ b/aur/aur_linux.go @@ -5,67 +5,59 @@ package aur import ( "bytes" "context" - "encoding/json" "fmt" - "io" - "net/http" "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 aurRPC = "https://aur.archlinux.org/rpc/v5" +const aurGitBase = "https://aur.archlinux.org" func available() bool { - _, err := exec.LookPath("makepkg") - return err == nil + for _, tool := range []string{"makepkg", "pacman"} { + if _, err := exec.LookPath(tool); err != nil { + return false + } + } + return true } -// aurSearchResponse is the JSON response from the AUR RPC API. -type aurSearchResponse struct { - ResultCount int `json:"resultcount"` - Results []struct { - Name string `json:"Name"` - Version string `json:"Version"` - Description string `json:"Description"` - URL string `json:"URL"` - OutOfDate *int64 `json:"OutOfDate"` - Maintainer string `json:"Maintainer"` - Popularity float64 `json:"Popularity"` - } `json:"results"` +// 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 } -func aurQuery(ctx context.Context, queryType, arg string) (*aurSearchResponse, error) { - url := fmt.Sprintf("%s/%s/%s", aurRPC, queryType, arg) - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) - if err != nil { - return nil, err - } - resp, err := http.DefaultClient.Do(req) - if err != nil { - return nil, fmt.Errorf("aur: %w", err) - } - defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) - if err != nil { - return nil, fmt.Errorf("aur: %w", err) - } - var result aurSearchResponse - if err := json.Unmarshal(body, &result); err != nil { - return nil, fmt.Errorf("aur: %w", err) - } - return &result, nil -} - -func install(ctx context.Context, pkgs []snack.Target, opts ...snack.Option) (snack.InstallResult, error) { +// 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 { - // Check if already installed if !o.Reinstall && !o.DryRun { ok, err := isInstalled(ctx, t.Name) if err != nil { @@ -77,8 +69,29 @@ func install(ctx context.Context, pkgs []snack.Target, opts ...snack.Option) (sn } } - if err := installPkg(ctx, t.Name, o); err != nil { - return snack.InstallResult{}, err + pkgFile, cleanupDir, err := a.buildPackage(ctx, t) + if err != nil { + return snack.InstallResult{}, fmt.Errorf("aur install %s: %w", t.Name, err) + } + + if o.DryRun { + if cleanupDir != "" { + os.RemoveAll(cleanupDir) + } + installed = append(installed, snack.Package{Name: t.Name, Repository: "aur"}) + continue + } + + args := []string{"-U", "--noconfirm", pkgFile} + installErr := func() error { + if cleanupDir != "" { + defer os.RemoveAll(cleanupDir) + } + _, err := runPacman(ctx, args, o.Sudo) + return err + }() + if installErr != nil { + return snack.InstallResult{}, fmt.Errorf("aur install %s: %w", t.Name, installErr) } v, _ := version(ctx, t.Name) @@ -89,92 +102,317 @@ func install(ctx context.Context, pkgs []snack.Target, opts ...snack.Option) (sn Installed: true, }) } + return snack.InstallResult{Installed: installed, Unchanged: unchanged}, nil } -func installPkg(ctx context.Context, pkg string, opts snack.Options) error { - tmpDir, err := os.MkdirTemp("", "aur-"+pkg) - if err != nil { - return fmt.Errorf("aur: create temp dir: %w", err) - } - defer os.RemoveAll(tmpDir) - - gitURL := fmt.Sprintf("https://aur.archlinux.org/%s.git", pkg) - cloneCmd := exec.CommandContext(ctx, "git", "clone", "--depth=1", gitURL, tmpDir) - var stderr bytes.Buffer - cloneCmd.Stderr = &stderr - if err := cloneCmd.Run(); err != nil { - return fmt.Errorf("aur: git clone %s: %s: %w", pkg, stderr.String(), err) - } - - makepkgArgs := []string{"-si", "--noconfirm"} - if opts.AssumeYes { - makepkgArgs = append(makepkgArgs, "--noconfirm") - } - - makeCmd := exec.CommandContext(ctx, "makepkg", makepkgArgs...) - makeCmd.Dir = tmpDir - makeCmd.Stderr = &stderr - makeCmd.Stdout = os.Stdout - - if err := makeCmd.Run(); err != nil { - se := stderr.String() - if strings.Contains(se, "permission denied") { - return fmt.Errorf("aur: %w", snack.ErrPermissionDenied) +// buildPackage clones the AUR git repo for a package and runs makepkg. +// Returns the path to the built .pkg.tar.zst file and an optional cleanup +// directory (non-empty only when a temp dir was created; caller must remove it). +func (a *AUR) buildPackage(ctx context.Context, t snack.Target) (pkgPath string, cleanupDir string, err 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) } - return fmt.Errorf("aur: makepkg %s: %s: %w", pkg, se, err) + buildDir = tmp + cleanupDir = tmp + } + + pkgDir := filepath.Join(buildDir, t.Name) + + // Clone or update the PKGBUILD repo + if err := cloneOrPull(ctx, t.Name, pkgDir); err != nil { + return "", cleanupDir, 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 "", cleanupDir, 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 "", cleanupDir, fmt.Errorf("makepkg %s: no package file produced", t.Name) + } + return matches[len(matches)-1], cleanupDir, 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(_ context.Context, _ []snack.Target, _ ...snack.Option) (snack.RemoveResult, error) { - return snack.RemoveResult{}, fmt.Errorf("aur: remove not supported, use pacman instead") +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(_ context.Context, _ []snack.Target, _ ...snack.Option) error { - return fmt.Errorf("aur: purge not supported, use pacman instead") +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 } -func upgrade(ctx context.Context, opts ...snack.Option) error { - aurPkgs, err := list(ctx) +// 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 } - for _, p := range aurPkgs { - result, err := aurQuery(ctx, "info", p.Name) + 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 + continue // skip packages where vercmp fails } - if result.ResultCount == 0 { - continue + if cmp < 0 { + upgrades = append(upgrades, snack.Package{ + Name: inst.Name, + Version: aurPkg.Version, + Repository: "aur", + Installed: true, + }) } - if result.Results[0].Version != p.Version { - if _, err := install(ctx, []snack.Target{{Name: p.Name}}, opts...); err != nil { - return err + } + 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 } -func update(_ context.Context) error { - return nil -} - -func list(ctx context.Context) ([]snack.Package, error) { - c := exec.CommandContext(ctx, "pacman", "-Qm") - var stdout bytes.Buffer - c.Stdout = &stdout - if err := c.Run(); err != nil { - // exit status 1 means no foreign packages - if exitErr, ok := err.(*exec.ExitError); ok && exitErr.ExitCode() == 1 { - return nil, nil - } - return nil, fmt.Errorf("aur list: %w", err) - } - return parsePackageList(stdout.String()), 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") { @@ -195,186 +433,3 @@ func parsePackageList(output string) []snack.Package { } return pkgs } - -func search(ctx context.Context, query string) ([]snack.Package, error) { - result, err := aurQuery(ctx, "search", query) - if err != nil { - return nil, err - } - var pkgs []snack.Package - for _, r := range result.Results { - pkgs = append(pkgs, snack.Package{ - Name: r.Name, - Version: r.Version, - Description: r.Description, - }) - } - return pkgs, nil -} - -func info(ctx context.Context, pkg string) (*snack.Package, error) { - result, err := aurQuery(ctx, "info", pkg) - if err != nil { - return nil, err - } - if result.ResultCount == 0 { - return nil, fmt.Errorf("aur info %s: %w", pkg, snack.ErrNotFound) - } - r := result.Results[0] - return &snack.Package{ - Name: r.Name, - Version: r.Version, - Description: r.Description, - }, 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) - } - aurPkgs, err := list(ctx) - if err != nil { - return false, err - } - for _, p := range aurPkgs { - if p.Name == pkg { - return true, nil - } - } - return false, nil -} - -func version(ctx context.Context, pkg string) (string, error) { - c := exec.CommandContext(ctx, "pacman", "-Q", pkg) - var stdout bytes.Buffer - c.Stdout = &stdout - if err := c.Run(); err != nil { - if exitErr, ok := err.(*exec.ExitError); ok && exitErr.ExitCode() == 1 { - return "", fmt.Errorf("aur version %s: %w", pkg, snack.ErrNotInstalled) - } - return "", fmt.Errorf("aur version: %w", err) - } - parts := strings.Fields(strings.TrimSpace(stdout.String())) - if len(parts) < 2 { - return "", fmt.Errorf("aur version %s: unexpected output", pkg) - } - return parts[1], nil -} - -func latestVersion(ctx context.Context, pkg string) (string, error) { - result, err := aurQuery(ctx, "info", pkg) - if err != nil { - return "", err - } - if result.ResultCount == 0 { - return "", fmt.Errorf("aur latestVersion %s: %w", pkg, snack.ErrNotFound) - } - return result.Results[0].Version, nil -} - -func listUpgrades(ctx context.Context) ([]snack.Package, error) { - aurPkgs, err := list(ctx) - if err != nil { - return nil, err - } - var upgrades []snack.Package - for _, p := range aurPkgs { - result, err := aurQuery(ctx, "info", p.Name) - if err != nil || result.ResultCount == 0 { - continue - } - if result.Results[0].Version != p.Version { - upgrades = append(upgrades, snack.Package{ - Name: p.Name, - Version: result.Results[0].Version, - Repository: "aur", - Installed: true, - }) - } - } - return upgrades, nil -} - -func upgradeAvailable(ctx context.Context, pkg string) (bool, error) { - installed, 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, installed, latest) - if err != nil { - return false, err - } - return cmp < 0, nil -} - -func versionCmp(_ context.Context, ver1, ver2 string) (int, error) { - c := exec.Command("vercmp", ver1, ver2) - var stdout bytes.Buffer - c.Stdout = &stdout - if err := c.Run(); err != nil { - return 0, fmt.Errorf("aur versionCmp: %w", err) - } - result := strings.TrimSpace(stdout.String()) - switch result { - case "-1": - return -1, nil - case "0": - return 0, nil - case "1": - return 1, nil - default: - return 0, fmt.Errorf("aur versionCmp: unexpected output %q", result) - } -} - -func autoremove(ctx context.Context, opts ...snack.Option) error { - o := snack.ApplyOptions(opts...) - - // Get orphans via pacman - c := exec.CommandContext(ctx, "pacman", "-Qdtq") - var stdout bytes.Buffer - c.Stdout = &stdout - if err := c.Run(); err != nil { - // exit status 1 means no orphans - if exitErr, ok := err.(*exec.ExitError); ok && exitErr.ExitCode() == 1 { - return nil - } - return fmt.Errorf("aur autoremove: %w", err) - } - - orphans := strings.TrimSpace(stdout.String()) - if orphans == "" { - return nil - } - - args := []string{"-Rns", "--noconfirm"} - args = append(args, strings.Fields(orphans)...) - cmd := "pacman" - if o.Sudo { - args = append([]string{cmd}, args...) - cmd = "sudo" - } - - removeCmd := exec.CommandContext(ctx, cmd, args...) - return removeCmd.Run() -} - -func clean(_ context.Context) error { - // AUR builds in temp dirs, nothing persistent to clean - return nil -} - -func upgradePackages(ctx context.Context, pkgs []snack.Target, opts ...snack.Option) (snack.InstallResult, error) { - // For AUR, upgrading is just reinstalling from source - allOpts := append([]snack.Option{snack.WithReinstall()}, opts...) - return install(ctx, pkgs, allOpts...) -} diff --git a/aur/aur_other.go b/aur/aur_other.go index 6b73203..420c43b 100644 --- a/aur/aur_other.go +++ b/aur/aur_other.go @@ -10,7 +10,7 @@ import ( func available() bool { return false } -func install(_ context.Context, _ []snack.Target, _ ...snack.Option) (snack.InstallResult, error) { +func (a *AUR) install(_ context.Context, _ []snack.Target, _ ...snack.Option) (snack.InstallResult, error) { return snack.InstallResult{}, snack.ErrUnsupportedPlatform } @@ -22,11 +22,7 @@ func purge(_ context.Context, _ []snack.Target, _ ...snack.Option) error { return snack.ErrUnsupportedPlatform } -func upgrade(_ context.Context, _ ...snack.Option) error { - return snack.ErrUnsupportedPlatform -} - -func update(_ context.Context) error { +func (a *AUR) upgradeAll(_ context.Context, _ ...snack.Option) error { return snack.ErrUnsupportedPlatform } @@ -34,14 +30,6 @@ func list(_ context.Context) ([]snack.Package, error) { return nil, snack.ErrUnsupportedPlatform } -func search(_ context.Context, _ string) ([]snack.Package, error) { - return nil, snack.ErrUnsupportedPlatform -} - -func info(_ context.Context, _ string) (*snack.Package, error) { - return nil, snack.ErrUnsupportedPlatform -} - func isInstalled(_ context.Context, _ string) (bool, error) { return false, snack.ErrUnsupportedPlatform } @@ -70,10 +58,6 @@ func autoremove(_ context.Context, _ ...snack.Option) error { return snack.ErrUnsupportedPlatform } -func clean(_ context.Context) error { +func (a *AUR) cleanBuildDir() error { return snack.ErrUnsupportedPlatform } - -func upgradePackages(_ context.Context, _ []snack.Target, _ ...snack.Option) (snack.InstallResult, error) { - return snack.InstallResult{}, snack.ErrUnsupportedPlatform -} diff --git a/aur/aur_test.go b/aur/aur_test.go index 6826a6d..2c2cd91 100644 --- a/aur/aur_test.go +++ b/aur/aur_test.go @@ -3,123 +3,63 @@ package aur import ( "testing" - "github.com/gogrlx/snack" + "github.com/stretchr/testify/assert" ) -// Compile-time interface checks -var ( - _ snack.Manager = (*AUR)(nil) - _ snack.VersionQuerier = (*AUR)(nil) - _ snack.Cleaner = (*AUR)(nil) - _ snack.PackageUpgrader = (*AUR)(nil) - _ snack.NameNormalizer = (*AUR)(nil) -) - -func TestNew(t *testing.T) { - a := New() - if a == nil { - t.Fatal("New() returned nil") - } -} - -func TestName(t *testing.T) { - a := New() - if a.Name() != "aur" { - t.Errorf("Name() = %q, want %q", a.Name(), "aur") - } -} - -func TestNormalizeName(t *testing.T) { +func TestParsePackageList(t *testing.T) { tests := []struct { - input string - want string + name string + input string + expect int }{ - {"yay", "yay"}, - {"paru", "paru"}, - {"google-chrome", "google-chrome"}, - {"visual-studio-code-bin", "visual-studio-code-bin"}, - {"", ""}, - } - - a := New() - for _, tt := range tests { - t.Run(tt.input, func(t *testing.T) { - got := a.NormalizeName(tt.input) - if got != tt.want { - t.Errorf("NormalizeName(%q) = %q, want %q", tt.input, got, tt.want) - } - }) - } -} - -func TestParseArch(t *testing.T) { - tests := []struct { - input string - wantName string - wantArch string - }{ - {"yay", "yay", ""}, - {"paru", "paru", ""}, - {"google-chrome", "google-chrome", ""}, - } - - a := New() - for _, tt := range tests { - t.Run(tt.input, func(t *testing.T) { - gotName, gotArch := a.ParseArch(tt.input) - if gotName != tt.wantName || gotArch != tt.wantArch { - t.Errorf("ParseArch(%q) = (%q, %q), want (%q, %q)", - tt.input, gotName, gotArch, tt.wantName, tt.wantArch) - } - }) - } -} - -func TestCapabilities(t *testing.T) { - caps := snack.GetCapabilities(New()) - - tests := []struct { - name string - got bool - want bool - }{ - {"VersionQuery", caps.VersionQuery, true}, - {"Clean", caps.Clean, true}, - {"PackageUpgrade", caps.PackageUpgrade, true}, - {"NameNormalize", caps.NameNormalize, true}, - // AUR does not support these - {"Hold", caps.Hold, false}, - {"FileOwnership", caps.FileOwnership, false}, - {"RepoManagement", caps.RepoManagement, false}, - {"KeyManagement", caps.KeyManagement, false}, - {"Groups", caps.Groups, false}, - {"DryRun", caps.DryRun, false}, + { + 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) { - if tt.got != tt.want { - t.Errorf("%s = %v, want %v", tt.name, tt.got, tt.want) + 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 TestInterfaceNonCompliance(t *testing.T) { - var m snack.Manager = New() - if _, ok := m.(snack.Holder); ok { - t.Error("AUR should not implement Holder") - } - if _, ok := m.(snack.FileOwner); ok { - t.Error("AUR should not implement FileOwner") - } - if _, ok := m.(snack.RepoManager); ok { - t.Error("AUR should not implement RepoManager") - } - if _, ok := m.(snack.KeyManager); ok { - t.Error("AUR should not implement KeyManager") - } - if _, ok := m.(snack.Grouper); ok { - t.Error("AUR should not implement Grouper") - } +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) } diff --git a/aur/capabilities.go b/aur/capabilities.go index b28ce9b..14c5ce1 100644 --- a/aur/capabilities.go +++ b/aur/capabilities.go @@ -6,22 +6,30 @@ import ( "github.com/gogrlx/snack" ) -// LatestVersion returns the latest version of an AUR package. +// 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 AUR packages that have newer versions available. +// 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. +// 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 vercmp. +// 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) } @@ -33,14 +41,14 @@ func (a *AUR) Autoremove(ctx context.Context, opts ...snack.Option) error { return autoremove(ctx, opts...) } -// Clean is a no-op for AUR (builds use temp directories). -func (a *AUR) Clean(ctx context.Context) error { - return clean(ctx) +// 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 upgradePackages(ctx, pkgs, opts...) + return a.install(ctx, pkgs, opts...) } diff --git a/aur/normalize.go b/aur/normalize.go deleted file mode 100644 index 3ae3dd1..0000000 --- a/aur/normalize.go +++ /dev/null @@ -1,15 +0,0 @@ -package aur - -// normalizeName returns the canonical form of an AUR package name. -// AUR package names are simple identifiers without architecture or version -// suffixes, so this is essentially a pass-through. -func normalizeName(name string) string { - return name -} - -// parseArch extracts the architecture from a package name if present. -// AUR package names do not include architecture suffixes, -// so this returns the name unchanged with an empty string. -func parseArch(name string) (string, string) { - return name, "" -} diff --git a/aur/rpc.go b/aur/rpc.go index e276c26..29a4c12 100644 --- a/aur/rpc.go +++ b/aur/rpc.go @@ -7,6 +7,7 @@ import ( "io" "net/http" "net/url" + "strings" "github.com/gogrlx/snack" ) @@ -115,3 +116,24 @@ func rpcInfo(ctx context.Context, pkg string) (*snack.Package, error) { 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 +} diff --git a/flatpak/flatpak_linux.go b/flatpak/flatpak_linux.go index 20f1f3e..ec9c92c 100644 --- a/flatpak/flatpak_linux.go +++ b/flatpak/flatpak_linux.go @@ -198,49 +198,6 @@ func removeRepo(ctx context.Context, id string) error { return err } -func latestVersion(ctx context.Context, pkg string) (string, error) { - out, err := run(ctx, []string{"remote-info", "flathub", pkg}) - if err != nil { - if strings.Contains(err.Error(), "exit status 1") { - return "", fmt.Errorf("flatpak latestVersion %s: %w", pkg, snack.ErrNotFound) - } - return "", fmt.Errorf("flatpak latestVersion: %w", err) - } - p := parseInfo(out) - if p == nil || p.Version == "" { - return "", fmt.Errorf("flatpak latestVersion %s: %w", pkg, snack.ErrNotFound) - } - return p.Version, nil -} - -func listUpgrades(ctx context.Context) ([]snack.Package, error) { - out, err := run(ctx, []string{"remote-ls", "--updates", "--columns=name,application,version,origin"}) - if err != nil { - if strings.Contains(err.Error(), "No updates") { - return nil, nil - } - return nil, fmt.Errorf("flatpak listUpgrades: %w", err) - } - return parseList(out), nil -} - -func upgradeAvailable(ctx context.Context, pkg string) (bool, error) { - upgrades, err := listUpgrades(ctx) - if err != nil { - return false, err - } - for _, u := range upgrades { - if u.Name == pkg || u.Description == pkg { - return true, nil - } - } - return false, nil -} - -func versionCmp(_ context.Context, ver1, ver2 string) (int, error) { - return semverCmp(ver1, ver2), nil -} - func upgradePackages(ctx context.Context, pkgs []snack.Target, opts ...snack.Option) (snack.InstallResult, error) { o := snack.ApplyOptions(opts...) var toUpgrade []snack.Target diff --git a/go.mod b/go.mod index 18cb8ea..5f39df2 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.26.1 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,10 +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/exp v0.0.0-20240719175910-8a7402abbf56 // indirect + golang.org/x/net v0.47.0 // indirect golang.org/x/sync v0.20.0 // indirect golang.org/x/sys v0.42.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 ) diff --git a/go.sum b/go.sum index 8be88b8..bdbc599 100644 --- a/go.sum +++ b/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-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.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= 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.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +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= diff --git a/ports/ports_other.go b/ports/ports_other.go index 8693473..5242806 100644 --- a/ports/ports_other.go +++ b/ports/ports_other.go @@ -49,39 +49,3 @@ func isInstalled(_ context.Context, _ string) (bool, error) { func version(_ context.Context, _ string) (string, error) { return "", snack.ErrUnsupportedPlatform } - -func autoremove(_ context.Context, _ ...snack.Option) error { - return snack.ErrUnsupportedPlatform -} - -func clean(_ context.Context) error { - return snack.ErrUnsupportedPlatform -} - -func fileList(_ context.Context, _ string) ([]string, error) { - return nil, snack.ErrUnsupportedPlatform -} - -func owner(_ 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 upgradePackages(_ context.Context, _ []snack.Target, _ ...snack.Option) (snack.InstallResult, error) { - return snack.InstallResult{}, snack.ErrUnsupportedPlatform -}