mirror of
https://github.com/gogrlx/snack.git
synced 2026-04-02 05:08:42 -07:00
feat: implement detect package and snack CLI
- detect: auto-detection with Default(), All(), ByName()
- Platform-specific probing via build tags (linux, freebsd, openbsd)
- Linux probe order: apt, dnf, pacman, apk, flatpak, snap
- cmd/snack: full CLI with cobra + charmbracelet/fang
- Subcommands: install, remove, purge, upgrade, update, list, search,
info, which, hold, unhold, clean, detect, version
- Global flags: --manager, --sudo, --yes, --dry-run
- Capability type-assertions for FileOwner, Holder, Cleaner
- detect: basic compilation tests
This commit is contained in:
@@ -7,35 +7,45 @@ import (
|
||||
"github.com/gogrlx/snack"
|
||||
)
|
||||
|
||||
// probeOrder defines the order in which package managers are probed.
|
||||
// The first available manager wins.
|
||||
var probeOrder = []struct {
|
||||
name string
|
||||
bin string
|
||||
}{
|
||||
{"pacman", "pacman"},
|
||||
{"apk", "apk"},
|
||||
{"apt", "apt-get"},
|
||||
{"dnf", "dnf"},
|
||||
{"rpm", "rpm"},
|
||||
{"flatpak", "flatpak"},
|
||||
{"snap", "snap"},
|
||||
{"pkg", "pkg"},
|
||||
}
|
||||
|
||||
// Default returns the first available package manager on the system.
|
||||
// Returns ErrManagerNotFound if no supported manager is detected.
|
||||
func Default() (snack.Manager, error) {
|
||||
// TODO: implement — probe for each manager in order, return first match
|
||||
for _, fn := range candidates() {
|
||||
m := fn()
|
||||
if m.Available() {
|
||||
return m, nil
|
||||
}
|
||||
}
|
||||
return nil, snack.ErrManagerNotFound
|
||||
}
|
||||
|
||||
// All returns all available package managers on the system.
|
||||
func All() []snack.Manager {
|
||||
// TODO: implement
|
||||
return nil
|
||||
var out []snack.Manager
|
||||
for _, fn := range candidates() {
|
||||
m := fn()
|
||||
if m.Available() {
|
||||
out = append(out, m)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// ByName returns a specific manager by name, regardless of availability.
|
||||
// Returns ErrManagerNotFound if the name is not recognized.
|
||||
func ByName(name string) (snack.Manager, error) {
|
||||
for _, fn := range allManagers() {
|
||||
m := fn()
|
||||
if m.Name() == name {
|
||||
return m, nil
|
||||
}
|
||||
}
|
||||
return nil, snack.ErrManagerNotFound
|
||||
}
|
||||
|
||||
// managerFactory is a function that returns a new Manager instance.
|
||||
type managerFactory func() snack.Manager
|
||||
|
||||
// HasBinary reports whether a binary is available in PATH.
|
||||
func HasBinary(name string) bool {
|
||||
_, err := exec.LookPath(name)
|
||||
|
||||
18
detect/detect_freebsd.go
Normal file
18
detect/detect_freebsd.go
Normal file
@@ -0,0 +1,18 @@
|
||||
//go:build freebsd
|
||||
|
||||
package detect
|
||||
|
||||
import (
|
||||
"github.com/gogrlx/snack"
|
||||
"github.com/gogrlx/snack/pkg"
|
||||
)
|
||||
|
||||
func candidates() []managerFactory {
|
||||
return []managerFactory{
|
||||
func() snack.Manager { return pkg.New() },
|
||||
}
|
||||
}
|
||||
|
||||
func allManagers() []managerFactory {
|
||||
return candidates()
|
||||
}
|
||||
31
detect/detect_linux.go
Normal file
31
detect/detect_linux.go
Normal file
@@ -0,0 +1,31 @@
|
||||
//go:build linux
|
||||
|
||||
package detect
|
||||
|
||||
import (
|
||||
"github.com/gogrlx/snack"
|
||||
"github.com/gogrlx/snack/apk"
|
||||
"github.com/gogrlx/snack/apt"
|
||||
"github.com/gogrlx/snack/dnf"
|
||||
"github.com/gogrlx/snack/flatpak"
|
||||
"github.com/gogrlx/snack/pacman"
|
||||
"github.com/gogrlx/snack/snap"
|
||||
)
|
||||
|
||||
// candidates returns manager factories in probe order for Linux.
|
||||
// The first available manager wins for Default().
|
||||
func candidates() []managerFactory {
|
||||
return []managerFactory{
|
||||
func() snack.Manager { return apt.New() },
|
||||
func() snack.Manager { return dnf.New() },
|
||||
func() snack.Manager { return pacman.New() },
|
||||
func() snack.Manager { return apk.New() },
|
||||
func() snack.Manager { return flatpak.New() },
|
||||
func() snack.Manager { return snap.New() },
|
||||
}
|
||||
}
|
||||
|
||||
// allManagers returns all known manager factories (for ByName).
|
||||
func allManagers() []managerFactory {
|
||||
return candidates()
|
||||
}
|
||||
13
detect/detect_openbsd.go
Normal file
13
detect/detect_openbsd.go
Normal file
@@ -0,0 +1,13 @@
|
||||
//go:build openbsd
|
||||
|
||||
package detect
|
||||
|
||||
// candidates returns manager factories in probe order for OpenBSD.
|
||||
// TODO: wire up ports.New() once the ports package is implemented.
|
||||
func candidates() []managerFactory {
|
||||
return nil
|
||||
}
|
||||
|
||||
func allManagers() []managerFactory {
|
||||
return nil
|
||||
}
|
||||
22
detect/detect_test.go
Normal file
22
detect/detect_test.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package detect
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestByNameUnknown(t *testing.T) {
|
||||
_, err := ByName("nonexistent")
|
||||
if err == nil {
|
||||
t.Fatal("expected error for unknown manager")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAllReturnsSlice(t *testing.T) {
|
||||
// Just verify it doesn't panic; actual availability depends on system.
|
||||
_ = All()
|
||||
}
|
||||
|
||||
func TestDefaultDoesNotPanic(t *testing.T) {
|
||||
// May return error if no managers available; that's fine.
|
||||
_, _ = Default()
|
||||
}
|
||||
Reference in New Issue
Block a user