Skip to content

Commit d252766

Browse files
pablopunkopencode
andauthored
Persist active profiles before installation so user-specified profiles are saved even if component installs fail. Add regression test to ensure dot <profile> persists profiles on failure. (#27)
Co-authored-by: opencode <noreply@opencode.ai>
1 parent 727d938 commit d252766

File tree

2 files changed

+63
-0
lines changed

2 files changed

+63
-0
lines changed

cmd/dot/main.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,18 @@ func (a *App) Run(args []string) error {
335335
return nil
336336
}
337337

338+
// Persist active profiles early when provided by user (ensures they are saved even if installs fail hard)
339+
if !a.DryRun && profilesFromUser {
340+
stateManager, err := state.NewManager()
341+
if err != nil {
342+
return fmt.Errorf("failed to create state manager: %w", err)
343+
}
344+
stateManager.SetActiveProfiles(activeProfiles)
345+
if err := stateManager.Save(); err != nil {
346+
return fmt.Errorf("failed to save state: %w", err)
347+
}
348+
}
349+
338350
// Main install operation
339351
if a.Verbose {
340352
fmt.Printf("🚀 Starting %s operation...\n", "installation")

cmd/dot/main_test.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"testing"
1111

1212
"github.com/pablopunk/dot/internal/component"
13+
"github.com/pablopunk/dot/internal/state"
1314
)
1415

1516
func TestApp_Run(t *testing.T) {
@@ -483,3 +484,53 @@ func TestFlagParsing(t *testing.T) {
483484
})
484485
}
485486
}
487+
488+
// New test: profiles are saved even if tool installation fails
489+
func TestProfileSavedEvenIfInstallFails(t *testing.T) {
490+
// Setup temp workspace and HOME
491+
tempDir := t.TempDir()
492+
oldWd, _ := os.Getwd()
493+
defer os.Chdir(oldWd)
494+
os.Chdir(tempDir)
495+
496+
originalHome := os.Getenv("HOME")
497+
os.Setenv("HOME", tempDir)
498+
defer os.Setenv("HOME", originalHome)
499+
500+
// Config with a profile whose install always fails
501+
configContent := `
502+
profiles:
503+
failp:
504+
broken:
505+
install:
506+
sh: "false"
507+
`
508+
if err := os.WriteFile(filepath.Join(tempDir, "dot.yaml"), []byte(configContent), 0644); err != nil {
509+
t.Fatalf("Failed to write config: %v", err)
510+
}
511+
512+
// Run the app with the failing profile
513+
app := &App{Verbose: false, DryRun: false}
514+
if err := app.Run([]string{"failp"}); err != nil {
515+
// The app should not return an error for component failures
516+
t.Fatalf("App.Run returned error: %v", err)
517+
}
518+
519+
// Verify state saved the active profile
520+
stateManager, err := state.NewManager()
521+
if err != nil {
522+
t.Fatalf("Failed to create state manager: %v", err)
523+
}
524+
525+
profiles := stateManager.GetActiveProfiles()
526+
found := false
527+
for _, p := range profiles {
528+
if p == "failp" {
529+
found = true
530+
break
531+
}
532+
}
533+
if !found {
534+
t.Fatalf("expected active profiles to include 'failp', got %v", profiles)
535+
}
536+
}

0 commit comments

Comments
 (0)