diff --git a/apk/capabilities.go b/apk/capabilities.go index ba67975..803f7de 100644 --- a/apk/capabilities.go +++ b/apk/capabilities.go @@ -11,8 +11,12 @@ var ( _ snack.VersionQuerier = (*Apk)(nil) _ snack.Cleaner = (*Apk)(nil) _ snack.FileOwner = (*Apk)(nil) + _ snack.DryRunner = (*Apk)(nil) ) +// SupportsDryRun reports that apk honors [snack.WithDryRun] via --simulate. +func (a *Apk) SupportsDryRun() bool { return true } + // LatestVersion returns the latest available version from configured repositories. func (a *Apk) LatestVersion(ctx context.Context, pkg string) (string, error) { return latestVersion(ctx, pkg) diff --git a/apt/apt.go b/apt/apt.go index a0aeae7..abc82f7 100644 --- a/apt/apt.go +++ b/apt/apt.go @@ -196,6 +196,9 @@ func (a *Apt) ParseArch(name string) (string, string) { return parseArch(name) } +// SupportsDryRun reports that apt honors [snack.WithDryRun] via --dry-run. +func (a *Apt) SupportsDryRun() bool { return true } + // Compile-time interface checks. var ( _ snack.Manager = (*Apt)(nil) @@ -206,4 +209,5 @@ var ( _ snack.RepoManager = (*Apt)(nil) _ snack.KeyManager = (*Apt)(nil) _ snack.NameNormalizer = (*Apt)(nil) + _ snack.DryRunner = (*Apt)(nil) ) diff --git a/capabilities.go b/capabilities.go index e9b3337..048a9bd 100644 --- a/capabilities.go +++ b/capabilities.go @@ -12,6 +12,7 @@ type Capabilities struct { KeyManagement bool Groups bool NameNormalize bool + DryRun bool } // GetCapabilities probes a Manager for all optional interface support. @@ -24,6 +25,7 @@ func GetCapabilities(m Manager) Capabilities { _, km := m.(KeyManager) _, g := m.(Grouper) _, nn := m.(NameNormalizer) + _, dr := m.(DryRunner) return Capabilities{ VersionQuery: vq, Hold: h, @@ -33,5 +35,6 @@ func GetCapabilities(m Manager) Capabilities { KeyManagement: km, Groups: g, NameNormalize: nn, + DryRun: dr, } } diff --git a/dnf/capabilities.go b/dnf/capabilities.go index 1b8dc38..5af06a2 100644 --- a/dnf/capabilities.go +++ b/dnf/capabilities.go @@ -17,8 +17,12 @@ var ( _ snack.Grouper = (*DNF)(nil) _ snack.GroupQuerier = (*DNF)(nil) _ snack.NameNormalizer = (*DNF)(nil) + _ snack.DryRunner = (*DNF)(nil) ) +// SupportsDryRun reports that dnf honors [snack.WithDryRun] via --setopt=tsflags=test. +func (d *DNF) SupportsDryRun() bool { return true } + // LatestVersion returns the latest available version from configured repositories. func (d *DNF) LatestVersion(ctx context.Context, pkg string) (string, error) { return latestVersion(ctx, pkg, d.v5) diff --git a/dpkg/dpkg.go b/dpkg/dpkg.go index 76fa30e..a5bdf5b 100644 --- a/dpkg/dpkg.go +++ b/dpkg/dpkg.go @@ -101,9 +101,13 @@ func (d *Dpkg) ParseArch(name string) (string, string) { return parseArch(name) } +// SupportsDryRun reports that dpkg honors [snack.WithDryRun] via --simulate. +func (d *Dpkg) SupportsDryRun() bool { return true } + // Compile-time interface checks. var ( _ snack.Manager = (*Dpkg)(nil) _ snack.FileOwner = (*Dpkg)(nil) _ snack.NameNormalizer = (*Dpkg)(nil) + _ snack.DryRunner = (*Dpkg)(nil) ) diff --git a/pacman/capabilities.go b/pacman/capabilities.go index d5400ad..bee23f4 100644 --- a/pacman/capabilities.go +++ b/pacman/capabilities.go @@ -13,12 +13,16 @@ var ( _ snack.FileOwner = (*Pacman)(nil) _ snack.Grouper = (*Pacman)(nil) _ snack.GroupQuerier = (*Pacman)(nil) + _ snack.DryRunner = (*Pacman)(nil) ) // NOTE: snack.Holder is not implemented for pacman. While pacman supports // IgnorePkg in /etc/pacman.conf, there is no clean CLI command to hold/unhold // packages. pacman-contrib provides some tooling but it's not standard. +// SupportsDryRun reports that pacman honors [snack.WithDryRun] via --print. +func (p *Pacman) SupportsDryRun() bool { return true } + // LatestVersion returns the latest available version from configured repositories. func (p *Pacman) LatestVersion(ctx context.Context, pkg string) (string, error) { return latestVersion(ctx, pkg) diff --git a/snack.go b/snack.go index a70e91d..05a90ef 100644 --- a/snack.go +++ b/snack.go @@ -207,3 +207,18 @@ type NameNormalizer interface { // Returns the name without arch and the arch string. ParseArch(name string) (string, string) } + +// DryRunner reports whether a Manager backend natively supports dry-run mode. +// Backends that implement this interface honor [WithDryRun] in Install, Remove, +// Purge, and Upgrade and pass the appropriate flag to the underlying CLI +// (e.g. apt-get --dry-run, apk --simulate, dnf --setopt=tsflags=test, +// pacman --print). +// +// Backends that do NOT implement this interface (snap, flatpak, rpm, ports) +// silently ignore [WithDryRun]. +// +// Supported by: apt, apk, dnf, pacman. +type DryRunner interface { + // SupportsDryRun reports whether this backend honors [WithDryRun]. + SupportsDryRun() bool +} diff --git a/snack_test.go b/snack_test.go index 895ca82..0c849a2 100644 --- a/snack_test.go +++ b/snack_test.go @@ -91,6 +91,7 @@ func TestGetCapabilities_NilSafe(t *testing.T) { assert.False(t, caps.KeyManagement) assert.False(t, caps.Groups) assert.False(t, caps.NameNormalize) + assert.False(t, caps.DryRun) } func TestErrors(t *testing.T) {