From b2cbd4f64089998cc1d27bc64ea944d4ea9db300 Mon Sep 17 00:00:00 2001 From: MDietermann Date: Tue, 14 Apr 2026 22:06:57 +0200 Subject: [PATCH] feat(shellconfig): implement PlanForce --- internal/modules/shellconfig/shellconfig.go | 18 ++++++++- .../modules/shellconfig/shellconfig_test.go | 38 +++++++++++++++++++ 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/internal/modules/shellconfig/shellconfig.go b/internal/modules/shellconfig/shellconfig.go index a84ca88..3c7fa5e 100644 --- a/internal/modules/shellconfig/shellconfig.go +++ b/internal/modules/shellconfig/shellconfig.go @@ -3,7 +3,6 @@ package shellconfig import ( "context" - "errors" "fmt" "os" "path/filepath" @@ -91,5 +90,20 @@ func (m Module) Verify(ctx context.Context) error { } func (m Module) PlanForce(ctx context.Context) (module.Plan, error) { - return module.Plan{}, errors.New("PlanForce not implemented") + var actions []module.Action + for _, b := range m.cfg.Blocks { + b := b + actions = append(actions, module.Action{ + Description: fmt.Sprintf("rewrite block %s into %s (force)", b.Name, b.File), + Kind: module.ActionFileWrite, + Exec: func(ctx context.Context) error { + p, err := resolveFile(b.File) + if err != nil { + return err + } + return platform.EnsureBlock(p, b.Name, b.Content) + }, + }) + } + return module.Plan{Actions: actions}, nil } diff --git a/internal/modules/shellconfig/shellconfig_test.go b/internal/modules/shellconfig/shellconfig_test.go index 423169e..db3b111 100644 --- a/internal/modules/shellconfig/shellconfig_test.go +++ b/internal/modules/shellconfig/shellconfig_test.go @@ -55,4 +55,42 @@ func TestShellConfigIdempotent(t *testing.T) { } } +func TestPlanForceRewritesBlocks(t *testing.T) { + home := t.TempDir() + t.Setenv("HOME", home) + cfg := config.ShellConfigSection{Blocks: []config.ShellConfigBlock{ + {Name: "path", File: ".zshrc", Content: `export PATH="$HOME/.local/bin:$PATH"`}, + }} + m := New(cfg) + st, _ := m.Detect(context.Background()) + p, _ := m.Plan(context.Background(), st) + if err := m.Apply(context.Background(), p, nop{}); err != nil { + t.Fatal(err) + } + st2, _ := m.Detect(context.Background()) + p2, _ := m.Plan(context.Background(), st2) + if !p2.IsEmpty() { + t.Fatalf("precondition: regular plan should be empty") + } + plan, err := m.PlanForce(context.Background()) + if err != nil { + t.Fatalf("PlanForce: %v", err) + } + if plan.IsEmpty() { + t.Fatal("force plan must not be empty") + } + for _, a := range plan.Actions { + if err := a.Exec(context.Background()); err != nil { + t.Fatalf("Exec: %v", err) + } + } + data, _ := os.ReadFile(filepath.Join(home, ".zshrc")) + if !strings.Contains(string(data), `export PATH="$HOME/.local/bin:$PATH"`) { + t.Fatalf("content missing after force: %q", data) + } + if !strings.Contains(string(data), ">>> path >>>") { + t.Fatalf("block marker missing: %q", data) + } +} + var _ module.Module = Module{}