From 62ff4e782bc057d277a7c41fa35a665e2aeca045 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Mon, 6 Jul 2026 23:56:32 +0300 Subject: initial package management --- internal/resource/dir/dir.go | 2 +- internal/resource/dir/dir_test.go | 10 ++++---- internal/resource/file/file.go | 2 +- internal/resource/file/file_test.go | 2 +- internal/resource/link/link.go | 2 +- internal/resource/link/link_test.go | 2 +- internal/resource/pkg/dnf.go | 34 +++++++++++++++++++++++++++ internal/resource/pkg/pkg.go | 47 +++++++++++++++++++++++++++++++++++++ 8 files changed, 91 insertions(+), 10 deletions(-) create mode 100644 internal/resource/pkg/dnf.go create mode 100644 internal/resource/pkg/pkg.go (limited to 'internal/resource') diff --git a/internal/resource/dir/dir.go b/internal/resource/dir/dir.go index 94220b7..83c017c 100644 --- a/internal/resource/dir/dir.go +++ b/internal/resource/dir/dir.go @@ -200,6 +200,6 @@ func Present(path string, opts ...opt.Option) resource.Resource { } func Absent(path string, opts ...opt.Option) resource.Resource { - opts = append(opts, opt.IsAbsent()) + opts = append(opts, opt.IsAbsent) return Present(path, opts...) } diff --git a/internal/resource/dir/dir_test.go b/internal/resource/dir/dir_test.go index c7ec92e..21af8e1 100644 --- a/internal/resource/dir/dir_test.go +++ b/internal/resource/dir/dir_test.go @@ -78,7 +78,7 @@ func TestPresentAbsentNonEmptyDirWithoutPruneFails(t *testing.T) { t.Fatal(err) } - Present(path, IsAbsent()) + Present(path, IsAbsent) if err := resource.Apply(); err == nil { t.Error("expected Apply to fail when removing non-empty directory without prune") } @@ -95,7 +95,7 @@ func TestPresentAbsentPruneDirectoryRecursive(t *testing.T) { t.Fatal(err) } - Present(path, IsAbsent(), WithPrune()) + Present(path, IsAbsent, WithPrune) if err := resource.Apply(); err != nil { t.Fatalf("Apply failed: %v", err) } @@ -116,7 +116,7 @@ func TestAbsentPruneDirectoryRecursive(t *testing.T) { t.Fatal(err) } - Absent(path, WithPrune()) + Absent(path, WithPrune) if err := resource.Apply(); err != nil { t.Fatalf("Apply failed: %v", err) } @@ -168,7 +168,7 @@ func TestPresentDirectoryWithSource(t *testing.T) { t.Fatal(err) } - Present(dst, WithSource(src), WithPrune()) + Present(dst, WithSource(src), WithPrune) if err := resource.Apply(); err != nil { t.Fatalf("Apply failed: %v", err) } @@ -284,7 +284,7 @@ func TestSourceCopyWithPruneKeepsTemplatedFile(t *testing.T) { // Now apply with prune resource.ResetRepository() - Present(dst, WithSource(src), WithPrune()) + Present(dst, WithSource(src), WithPrune) if err := resource.Apply(); err != nil { t.Fatalf("Apply failed: %v", err) } diff --git a/internal/resource/file/file.go b/internal/resource/file/file.go index 9d8a0d9..88b0bca 100644 --- a/internal/resource/file/file.go +++ b/internal/resource/file/file.go @@ -231,6 +231,6 @@ func Present(path string, opts ...opt.Option) resource.Resource { } func Absent(path string, opts ...opt.Option) resource.Resource { - opts = append(opts, opt.IsAbsent()) + opts = append(opts, opt.IsAbsent) return Present(path, opts...) } diff --git a/internal/resource/file/file_test.go b/internal/resource/file/file_test.go index b158657..3ae183c 100644 --- a/internal/resource/file/file_test.go +++ b/internal/resource/file/file_test.go @@ -194,7 +194,7 @@ func TestPresentAbsent(t *testing.T) { t.Fatal(err) } - Present(path, IsAbsent()) + Present(path, IsAbsent) if err := resource.Apply(); err != nil { t.Fatalf("Apply failed: %v", err) } diff --git a/internal/resource/link/link.go b/internal/resource/link/link.go index a5c5122..b40ce65 100644 --- a/internal/resource/link/link.go +++ b/internal/resource/link/link.go @@ -95,7 +95,7 @@ func Present(path string, opts ...opt.Option) resource.Resource { } func Absent(path string, opts ...opt.Option) resource.Resource { - opts = append(opts, opt.IsAbsent()) + opts = append(opts, opt.IsAbsent) return Present(path, opts...) } diff --git a/internal/resource/link/link_test.go b/internal/resource/link/link_test.go index 862d5f7..170fbdd 100644 --- a/internal/resource/link/link_test.go +++ b/internal/resource/link/link_test.go @@ -183,7 +183,7 @@ func TestPresentAbsentSymlink(t *testing.T) { t.Fatal(err) } - Present(path, IsAbsent()) + Present(path, IsAbsent) if err := resource.Apply(); err != nil { t.Fatalf("Apply failed: %v", err) } diff --git a/internal/resource/pkg/dnf.go b/internal/resource/pkg/dnf.go new file mode 100644 index 0000000..ad03da2 --- /dev/null +++ b/internal/resource/pkg/dnf.go @@ -0,0 +1,34 @@ +package pkg + +import ( + "fmt" + "log" + + "codeberg.org/snonux/gonf/internal/exec" +) + +func applyDNF(name string, ensure Ensure) 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) + } + + stdout, stderr, exitCode, err := exec.Run("dnf", args...) + if err != nil { + return fmt.Errorf("failed to execute dnf: %w", err) + } + + if exitCode != 0 { + return fmt.Errorf("dnf failed with exit code %d: %s\n%s", exitCode, stdout, stderr) + } + + return nil +} diff --git a/internal/resource/pkg/pkg.go b/internal/resource/pkg/pkg.go new file mode 100644 index 0000000..72f098c --- /dev/null +++ b/internal/resource/pkg/pkg.go @@ -0,0 +1,47 @@ +package pkg + +import ( + "errors" + "os" +) + +type Ensure int + +const ( + PkgPresent Ensure = iota + PkgAbsent + PkgLatest +) + +type applyFunc func(name string, ensure Ensure) error + +func Present(name string, ensure Ensure) error { + bin, err := detect() + if err != nil { + return err + } + + var applyFunc applyFunc + + switch bin { + case "dnf": + applyFunc = applyDNF + } + + return applyFunc(name, ensure) +} + +func detect() (string, error) { + switch { + case exists("/etc/fedora-release"): + fallthrough + case exists("/etc/rocky-release"): + return "dnf", nil + } + return "", errors.New("unable to detect package manager!") +} + +func exists(path string) bool { + _, err := os.Stat(path) + return err == nil +} -- cgit v1.2.3