feat: add Target type for per-package version/repo/source constraints

Breaking change to Manager interface: Install/Remove/Purge now accept
[]Target instead of []string. Target supports Version pinning, FromRepo
constraints, and Source paths — matching SaltStack's pkgs list semantics.

Also adds WithRefresh, WithFromRepo, WithReinstall options.
This commit is contained in:
2026-02-25 20:32:16 +00:00
parent 48315f9bec
commit 6cbfc96e3d
2 changed files with 62 additions and 3 deletions

View File

@@ -7,16 +7,55 @@ package snack
import "context" import "context"
// Target represents a package to install, remove, or otherwise act on.
// At minimum, Name must be set. Version and other fields constrain the action.
//
// Modeled after SaltStack's pkgs list, which accepts both plain names
// and name:version mappings:
//
// pkgs:
// - nginx
// - redis: ">=7.0"
// - curl: "8.5.0-1"
type Target struct {
// Name is the package name (required).
Name string
// Version pins a specific version. If empty, the latest is used.
// Comparison operators are supported where the backend allows them
// (e.g. ">=1.2.3", "<2.0", "1.2.3-4").
Version string
// FromRepo constrains the install to a specific repository
// (e.g. "unstable", "community", "epel").
FromRepo string
// Source is a local file path or URL for package files
// (e.g. .deb, .rpm, .pkg.tar.zst). When set, Name is used only
// for display/logging.
Source string
}
// Targets is a convenience constructor for a slice of [Target] from
// plain package names (no version constraint).
func Targets(names ...string) []Target {
targets := make([]Target, len(names))
for i, name := range names {
targets[i] = Target{Name: name}
}
return targets
}
// Manager is the common interface implemented by all package manager wrappers. // Manager is the common interface implemented by all package manager wrappers.
type Manager interface { type Manager interface {
// Install one or more packages. // Install one or more packages.
Install(ctx context.Context, pkgs []string, opts ...Option) error Install(ctx context.Context, pkgs []Target, opts ...Option) error
// Remove one or more packages. // Remove one or more packages.
Remove(ctx context.Context, pkgs []string, opts ...Option) error Remove(ctx context.Context, pkgs []Target, opts ...Option) error
// Purge one or more packages (remove including config files). // Purge one or more packages (remove including config files).
Purge(ctx context.Context, pkgs []string, opts ...Option) error Purge(ctx context.Context, pkgs []Target, opts ...Option) error
// Upgrade all installed packages to their latest versions. // Upgrade all installed packages to their latest versions.
Upgrade(ctx context.Context, opts ...Option) error Upgrade(ctx context.Context, opts ...Option) error

View File

@@ -17,6 +17,9 @@ type Options struct {
DryRun bool DryRun bool
Root string // alternate root filesystem Root string // alternate root filesystem
Verbose bool Verbose bool
Refresh bool // refresh package index before operation
FromRepo string // constrain operation to a specific repository
Reinstall bool // reinstall already-installed packages
} }
// Option is a functional option for package manager operations. // Option is a functional option for package manager operations.
@@ -47,6 +50,23 @@ func WithVerbose() Option {
return func(o *Options) { o.Verbose = true } return func(o *Options) { o.Verbose = true }
} }
// WithRefresh refreshes the package database before the operation.
// Equivalent to pacman -Sy, apt-get update, apk update, etc.
func WithRefresh() Option {
return func(o *Options) { o.Refresh = true }
}
// WithFromRepo constrains the operation to a specific repository.
// e.g. "unstable" for apt, "community" for pacman.
func WithFromRepo(repo string) Option {
return func(o *Options) { o.FromRepo = repo }
}
// WithReinstall forces reinstallation of already-installed packages.
func WithReinstall() Option {
return func(o *Options) { o.Reinstall = true }
}
// ApplyOptions processes functional options into an Options struct. // ApplyOptions processes functional options into an Options struct.
func ApplyOptions(opts ...Option) Options { func ApplyOptions(opts ...Option) Options {
var o Options var o Options