Skip to content

Commit 4a2f388

Browse files
feat: complete snapshot restore with git and shell config
Snapshot import now restores git user.name/email, Oh-My-Zsh theme/plugins, and shows shell/git info in the restore UI. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
1 parent 6c8d632 commit 4a2f388

File tree

3 files changed

+135
-12
lines changed

3 files changed

+135
-12
lines changed

internal/cli/snapshot.go

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,22 @@ func runSnapshotImport(importPath string, dryRun bool) error {
371371
snapBoldStyle.Render("Packages:"),
372372
len(snap.Packages.Formulae), len(snap.Packages.Casks),
373373
len(snap.Packages.Npm), len(snap.Packages.Taps))
374+
if snap.Git.UserName != "" || snap.Git.UserEmail != "" {
375+
fmt.Fprintf(os.Stderr, " %s %s <%s>\n",
376+
snapBoldStyle.Render("Git:"), snap.Git.UserName, snap.Git.UserEmail)
377+
}
378+
if snap.Shell.OhMyZsh {
379+
theme := snap.Shell.Theme
380+
if theme == "" {
381+
theme = "default"
382+
}
383+
plugins := "none"
384+
if len(snap.Shell.Plugins) > 0 {
385+
plugins = strings.Join(snap.Shell.Plugins, ", ")
386+
}
387+
fmt.Fprintf(os.Stderr, " %s Oh-My-Zsh (theme: %s, plugins: %s)\n",
388+
snapBoldStyle.Render("Shell:"), theme, plugins)
389+
}
374390
fmt.Fprintln(os.Stderr)
375391

376392
edited, confirmed, err := ui.RunSnapshotEditor(snap)
@@ -425,32 +441,43 @@ func runSnapshotImport(importPath string, dryRun bool) error {
425441
}
426442
}
427443

428-
cfg := &config.Config{DryRun: dryRun}
429-
cfg.SelectedPkgs = make(map[string]bool)
444+
importCfg := &config.Config{DryRun: dryRun}
445+
importCfg.SelectedPkgs = make(map[string]bool)
430446

431447
for _, name := range edited.Packages.Formulae {
432448
if catalogSet[name] {
433-
cfg.SelectedPkgs[name] = true
449+
importCfg.SelectedPkgs[name] = true
434450
} else {
435-
cfg.OnlinePkgs = append(cfg.OnlinePkgs, config.Package{Name: name})
451+
importCfg.OnlinePkgs = append(importCfg.OnlinePkgs, config.Package{Name: name})
436452
}
437453
}
438454
for _, name := range edited.Packages.Casks {
439455
if catalogSet[name] {
440-
cfg.SelectedPkgs[name] = true
456+
importCfg.SelectedPkgs[name] = true
441457
} else {
442-
cfg.OnlinePkgs = append(cfg.OnlinePkgs, config.Package{Name: name, IsCask: true})
458+
importCfg.OnlinePkgs = append(importCfg.OnlinePkgs, config.Package{Name: name, IsCask: true})
443459
}
444460
}
445461
for _, name := range edited.Packages.Npm {
446462
if catalogSet[name] {
447-
cfg.SelectedPkgs[name] = true
463+
importCfg.SelectedPkgs[name] = true
448464
} else {
449-
cfg.OnlinePkgs = append(cfg.OnlinePkgs, config.Package{Name: name, IsNpm: true})
465+
importCfg.OnlinePkgs = append(importCfg.OnlinePkgs, config.Package{Name: name, IsNpm: true})
450466
}
451467
}
452468

453-
cfg.SnapshotTaps = edited.Packages.Taps
469+
importCfg.SnapshotTaps = edited.Packages.Taps
470+
471+
importCfg.SnapshotGit = &config.SnapshotGitConfig{
472+
UserName: edited.Git.UserName,
473+
UserEmail: edited.Git.UserEmail,
474+
}
475+
476+
importCfg.SnapshotShell = &config.SnapshotShellConfig{
477+
OhMyZsh: edited.Shell.OhMyZsh,
478+
Theme: edited.Shell.Theme,
479+
Plugins: edited.Shell.Plugins,
480+
}
454481

455-
return installer.RunFromSnapshot(cfg)
482+
return installer.RunFromSnapshot(importCfg)
456483
}

internal/config/config.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,21 @@ type Config struct {
4242
User string
4343
RemoteConfig *RemoteConfig
4444
PackagesOnly bool
45+
46+
SnapshotShell *SnapshotShellConfig
47+
SnapshotGit *SnapshotGitConfig
48+
SnapshotDotfiles string
49+
}
50+
51+
type SnapshotShellConfig struct {
52+
OhMyZsh bool
53+
Theme string
54+
Plugins []string
55+
}
56+
57+
type SnapshotGitConfig struct {
58+
UserName string
59+
UserEmail string
4560
}
4661

4762
type RemoteConfig struct {

internal/installer/installer.go

Lines changed: 83 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -707,8 +707,20 @@ func RunFromSnapshot(cfg *config.Config) error {
707707
ui.Error(fmt.Sprintf("npm package installation failed: %v", err))
708708
}
709709

710-
if err := stepShell(cfg); err != nil {
711-
ui.Error(fmt.Sprintf("Shell setup failed: %v", err))
710+
if cfg.SnapshotGit != nil {
711+
if err := stepRestoreGit(cfg); err != nil {
712+
ui.Error(fmt.Sprintf("Git restore failed: %v", err))
713+
}
714+
}
715+
716+
if cfg.SnapshotShell != nil && cfg.SnapshotShell.OhMyZsh {
717+
if err := stepRestoreShell(cfg); err != nil {
718+
ui.Error(fmt.Sprintf("Shell restore failed: %v", err))
719+
}
720+
} else {
721+
if err := stepShell(cfg); err != nil {
722+
ui.Error(fmt.Sprintf("Shell setup failed: %v", err))
723+
}
712724
}
713725

714726
if err := stepMacOS(cfg); err != nil {
@@ -719,6 +731,75 @@ func RunFromSnapshot(cfg *config.Config) error {
719731
return nil
720732
}
721733

734+
func stepRestoreGit(cfg *config.Config) error {
735+
ui.Header("Restore: Git Configuration")
736+
fmt.Println()
737+
738+
git := cfg.SnapshotGit
739+
if git.UserName == "" && git.UserEmail == "" {
740+
ui.Muted("No git config in snapshot, skipping")
741+
fmt.Println()
742+
return nil
743+
}
744+
745+
existingName, existingEmail := system.GetExistingGitConfig()
746+
747+
if existingName != "" && existingEmail != "" {
748+
ui.Success(fmt.Sprintf("✓ Already configured: %s <%s>", existingName, existingEmail))
749+
fmt.Println()
750+
return nil
751+
}
752+
753+
if cfg.DryRun {
754+
if existingName == "" && git.UserName != "" {
755+
fmt.Printf("[DRY-RUN] Would set git user.name = %s\n", git.UserName)
756+
}
757+
if existingEmail == "" && git.UserEmail != "" {
758+
fmt.Printf("[DRY-RUN] Would set git user.email = %s\n", git.UserEmail)
759+
}
760+
fmt.Println()
761+
return nil
762+
}
763+
764+
if existingName == "" && git.UserName != "" {
765+
if err := system.ConfigureGit(git.UserName, git.UserEmail); err != nil {
766+
return fmt.Errorf("failed to restore git config: %w", err)
767+
}
768+
}
769+
770+
ui.Success(fmt.Sprintf("Git restored: %s <%s>", git.UserName, git.UserEmail))
771+
fmt.Println()
772+
return nil
773+
}
774+
775+
func stepRestoreShell(cfg *config.Config) error {
776+
ui.Header("Restore: Shell Configuration")
777+
fmt.Println()
778+
779+
shellCfg := cfg.SnapshotShell
780+
781+
if cfg.DryRun {
782+
fmt.Println("[DRY-RUN] Would restore shell config from snapshot")
783+
}
784+
785+
ui.Info(fmt.Sprintf("Theme: %s, Plugins: %v", shellCfg.Theme, shellCfg.Plugins))
786+
fmt.Println()
787+
788+
if err := shell.RestoreFromSnapshot(shellCfg.OhMyZsh, shellCfg.Theme, shellCfg.Plugins, cfg.DryRun); err != nil {
789+
return err
790+
}
791+
792+
if err := shell.ConfigureZshrc(cfg.DryRun); err != nil {
793+
return fmt.Errorf("failed to configure .zshrc: %w", err)
794+
}
795+
796+
if !cfg.DryRun {
797+
ui.Success("Shell configuration restored")
798+
}
799+
fmt.Println()
800+
return nil
801+
}
802+
722803
func runUpdate(cfg *config.Config) error {
723804
ui.Header("OpenBoot Update")
724805
fmt.Println()

0 commit comments

Comments
 (0)