mirror of
https://github.com/gogrlx/snack.git
synced 2026-04-02 05:08:42 -07:00
feat: add Homebrew provider, implement NameNormalizer across all managers
- Add brew package for Homebrew support on macOS and Linux - Implement NameNormalizer interface (NormalizeName, ParseArch) for all providers - Add darwin platform detection with Homebrew as default - Consolidate capabilities by removing separate *_linux.go/*_other.go files - Update tests for new capability expectations - Add comprehensive tests for AUR and brew providers - Update README with capability matrix and modern Target API usage 💘 Generated with Crush Assisted-by: AWS Claude Opus 4.5 via Crush <crush@charm.land>
This commit is contained in:
@@ -10,6 +10,7 @@ import (
|
||||
var (
|
||||
_ snack.VersionQuerier = (*Snap)(nil)
|
||||
_ snack.Cleaner = (*Snap)(nil)
|
||||
_ snack.NameNormalizer = (*Snap)(nil)
|
||||
)
|
||||
|
||||
// LatestVersion returns the latest stable version of a snap.
|
||||
@@ -32,13 +33,12 @@ func (s *Snap) VersionCmp(ctx context.Context, ver1, ver2 string) (int, error) {
|
||||
return versionCmp(ctx, ver1, ver2)
|
||||
}
|
||||
|
||||
// Autoremove is a no-op for snap. Snaps are self-contained and do not
|
||||
// have orphan dependencies.
|
||||
func (s *Snap) Autoremove(ctx context.Context, opts ...snack.Option) error {
|
||||
return autoremove(ctx, opts...)
|
||||
// Autoremove is a no-op for snap (snap doesn't have orphan packages).
|
||||
func (s *Snap) Autoremove(_ context.Context, _ ...snack.Option) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Clean removes old disabled snap revisions to free disk space.
|
||||
// Clean removes old snap revisions to free up space.
|
||||
func (s *Snap) Clean(ctx context.Context) error {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
||||
15
snap/normalize.go
Normal file
15
snap/normalize.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package snap
|
||||
|
||||
// normalizeName returns the canonical form of a snap name.
|
||||
// Snap 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 snap name if present.
|
||||
// Snap 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, ""
|
||||
}
|
||||
10
snap/snap.go
10
snap/snap.go
@@ -81,6 +81,16 @@ func (s *Snap) Version(ctx context.Context, pkg string) (string, error) {
|
||||
return version(ctx, pkg)
|
||||
}
|
||||
|
||||
// NormalizeName returns the canonical form of a snap name.
|
||||
func (s *Snap) NormalizeName(name string) string {
|
||||
return normalizeName(name)
|
||||
}
|
||||
|
||||
// ParseArch extracts the architecture from a snap name if present.
|
||||
func (s *Snap) ParseArch(name string) (string, string) {
|
||||
return parseArch(name)
|
||||
}
|
||||
|
||||
// Verify interface compliance at compile time.
|
||||
var _ snack.Manager = (*Snap)(nil)
|
||||
var _ snack.PackageUpgrader = (*Snap)(nil)
|
||||
|
||||
@@ -232,43 +232,21 @@ func versionCmp(_ context.Context, ver1, ver2 string) (int, error) {
|
||||
return semverCmp(ver1, ver2), nil
|
||||
}
|
||||
|
||||
// autoremove is a no-op for snap. Snaps are self-contained and do not
|
||||
// have orphan dependencies.
|
||||
func autoremove(_ context.Context, _ ...snack.Option) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// clean removes old disabled snap revisions to free disk space.
|
||||
// It runs `snap list --all` to find disabled revisions, then removes
|
||||
// each one with `snap remove --revision=<rev> <name>`.
|
||||
func clean(ctx context.Context) error {
|
||||
// Remove disabled snap revisions to free up space
|
||||
out, err := run(ctx, []string{"list", "--all"})
|
||||
if err != nil {
|
||||
return fmt.Errorf("snap clean: %w", err)
|
||||
return err
|
||||
}
|
||||
// Parse output for disabled revisions
|
||||
// Header: Name Version Rev Tracking Publisher Notes
|
||||
// Disabled snaps have "disabled" in the Notes column
|
||||
lines := strings.Split(out, "\n")
|
||||
for i, line := range lines {
|
||||
if i == 0 { // skip header
|
||||
continue
|
||||
}
|
||||
line = strings.TrimSpace(line)
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
for _, line := range strings.Split(out, "\n") {
|
||||
if !strings.Contains(line, "disabled") {
|
||||
continue
|
||||
}
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) < 3 {
|
||||
continue
|
||||
}
|
||||
name := fields[0]
|
||||
rev := fields[2]
|
||||
if _, err := run(ctx, []string{"remove", "--revision=" + rev, name}); err != nil {
|
||||
return fmt.Errorf("snap clean %s rev %s: %w", name, rev, err)
|
||||
if len(fields) >= 3 {
|
||||
name := fields[0]
|
||||
rev := fields[2]
|
||||
_, _ = run(ctx, []string{"remove", name, "--revision=" + rev})
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -293,14 +271,9 @@ func upgradePackages(ctx context.Context, pkgs []snack.Target, opts ...snack.Opt
|
||||
toUpgrade = append(toUpgrade, t)
|
||||
}
|
||||
}
|
||||
if len(toUpgrade) > 0 {
|
||||
for _, t := range toUpgrade {
|
||||
cmd := exec.CommandContext(ctx, "snap", "refresh", t.Name)
|
||||
var stderr bytes.Buffer
|
||||
cmd.Stderr = &stderr
|
||||
if err := cmd.Run(); err != nil {
|
||||
return snack.InstallResult{}, fmt.Errorf("snap refresh %s: %w: %s", t.Name, err, stderr.String())
|
||||
}
|
||||
for _, t := range toUpgrade {
|
||||
if _, err := run(ctx, []string{"refresh", t.Name}); err != nil {
|
||||
return snack.InstallResult{}, fmt.Errorf("snap refresh %s: %w", t.Name, err)
|
||||
}
|
||||
}
|
||||
var upgraded []snack.Package
|
||||
|
||||
@@ -66,10 +66,6 @@ func versionCmp(_ context.Context, _, _ string) (int, error) {
|
||||
return 0, snack.ErrUnsupportedPlatform
|
||||
}
|
||||
|
||||
func autoremove(_ context.Context, _ ...snack.Option) error {
|
||||
return snack.ErrUnsupportedPlatform
|
||||
}
|
||||
|
||||
func clean(_ context.Context) error {
|
||||
return snack.ErrUnsupportedPlatform
|
||||
}
|
||||
|
||||
@@ -441,8 +441,8 @@ func TestCapabilities(t *testing.T) {
|
||||
if caps.Groups {
|
||||
t.Error("expected Groups=false")
|
||||
}
|
||||
if caps.NameNormalize {
|
||||
t.Error("expected NameNormalize=false")
|
||||
if !caps.NameNormalize {
|
||||
t.Error("expected NameNormalize=true")
|
||||
}
|
||||
if !caps.PackageUpgrade {
|
||||
t.Error("expected PackageUpgrade=true")
|
||||
|
||||
Reference in New Issue
Block a user