summaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-07-07 00:17:27 +0300
committerPaul Buetow <paul@buetow.org>2026-07-07 00:17:27 +0300
commitab6e1341dc5eb06b6573571f8c17e19d0c393418 (patch)
treef9aa00bf0cf3ab302ecb2d152f7e781793112ba5 /internal
parent62ff4e782bc057d277a7c41fa35a665e2aeca045 (diff)
can manage dnf packages
Diffstat (limited to 'internal')
-rw-r--r--internal/resource/pkg/dnf.go20
-rw-r--r--internal/resource/pkg/dnf_test.go52
-rw-r--r--internal/resource/pkg/pkg.go54
3 files changed, 99 insertions, 27 deletions
diff --git a/internal/resource/pkg/dnf.go b/internal/resource/pkg/dnf.go
index ad03da2..e970b10 100644
--- a/internal/resource/pkg/dnf.go
+++ b/internal/resource/pkg/dnf.go
@@ -2,23 +2,21 @@ package pkg
import (
"fmt"
- "log"
"codeberg.org/snonux/gonf/internal/exec"
)
-func applyDNF(name string, ensure Ensure) error {
+func applyDNF(p *Package) error {
var args []string
- switch ensure {
- case PkgPresent:
- args = []string{"install", "-y", name}
- case PkgAbsent:
- args = []string{"remove", "-y", name}
- case PkgLatest:
- args = []string{"install", "-y", name}
- default:
- log.Fatalf("unsupported ensure state: %v", ensure)
+ if p.absent {
+ args = []string{"remove", "-y", p.name}
+ } else if p.latest {
+ // update ensures the package is installed and updated to the latest version.
+ args = []string{"update", "-y", p.name}
+ } else {
+ // install ensures the package is installed, but does not update it if already present.
+ args = []string{"install", "-y", p.name}
}
stdout, stderr, exitCode, err := exec.Run("dnf", args...)
diff --git a/internal/resource/pkg/dnf_test.go b/internal/resource/pkg/dnf_test.go
new file mode 100644
index 0000000..e9778dd
--- /dev/null
+++ b/internal/resource/pkg/dnf_test.go
@@ -0,0 +1,52 @@
+package pkg
+
+import (
+ "os"
+ "testing"
+)
+
+func TestApplyDNF(t *testing.T) {
+ // Only run this test if explicitly enabled via environment variable.
+ if os.Getenv("GONF_RUN_DNF_TESTS") != "1" {
+ t.Skip("Skipping DNF test: GONF_RUN_DNF_TESTS=1 not set")
+ }
+
+ // Skip if not running as root, as dnf requires superuser privileges.
+ if os.Getuid() != 0 {
+ t.Skip("Skipping DNF test: root privileges required")
+ }
+
+ p := &Package{
+ name: "tig",
+ }
+
+ t.Run("Present", func(t *testing.T) {
+ p.absent = false
+ p.latest = false
+ if err := applyDNF(p); err != nil {
+ t.Errorf("applyDNF Present failed: %v", err)
+ }
+ })
+
+ t.Run("Latest", func(t *testing.T) {
+ p.absent = false
+ p.latest = true
+ if err := applyDNF(p); err != nil {
+ t.Errorf("applyDNF Latest failed: %v", err)
+ }
+ })
+
+ t.Run("Absent", func(t *testing.T) {
+ p.absent = true
+ p.latest = false
+ if err := applyDNF(p); err != nil {
+ t.Errorf("applyDNF Absent failed: %v", err)
+ }
+
+ // // Restore the package so we don't leave the system in a changed state
+ // p.absent = false
+ // if err := applyDNF(p); err != nil {
+ // t.Errorf("failed to restore package tig after Absent test: %v", err)
+ // }
+ })
+}
diff --git a/internal/resource/pkg/pkg.go b/internal/resource/pkg/pkg.go
index 72f098c..b010c7a 100644
--- a/internal/resource/pkg/pkg.go
+++ b/internal/resource/pkg/pkg.go
@@ -3,38 +3,60 @@ package pkg
import (
"errors"
"os"
-)
-
-type Ensure int
-const (
- PkgPresent Ensure = iota
- PkgAbsent
- PkgLatest
+ opt "codeberg.org/snonux/gonf/api/options"
+ "codeberg.org/snonux/gonf/internal/resource"
)
-type applyFunc func(name string, ensure Ensure) error
+type Package struct {
+ name string
+ absent bool
+ latest bool
+}
+
+func (p *Package) SetAbsent() { p.absent = true }
+func (p *Package) SetLatest() { p.latest = true }
-func Present(name string, ensure Ensure) error {
- bin, err := detect()
+func (p *Package) apply() error {
+ pkgMan, err := detectPackageManager()
if err != nil {
return err
}
- var applyFunc applyFunc
-
- switch bin {
+ switch pkgMan {
case "dnf":
- applyFunc = applyDNF
+ return applyDNF(p)
}
- return applyFunc(name, ensure)
+ return errors.New("unsupported package manager")
}
-func detect() (string, error) {
+func Present(name string, opts ...opt.Option) resource.Resource {
+ p := &Package{
+ name: name,
+ }
+
+ for _, o := range opts {
+ o(p)
+ }
+
+ return resource.Register("Package", p.name,
+ resource.ApplierFunc(func() error { return p.apply() }))
+}
+
+func Absent(name string, opts ...opt.Option) resource.Resource {
+ opts = append(opts, opt.IsAbsent)
+ return Present(name, opts...)
+}
+
+func detectPackageManager() (string, error) {
switch {
case exists("/etc/fedora-release"):
fallthrough
+ case exists("/etc/centos-release"):
+ fallthrough
+ case exists("/etc/redhat-release"):
+ fallthrough
case exists("/etc/rocky-release"):
return "dnf", nil
}