diff --git a/internal/cli/init.go b/internal/cli/init.go index 27884af..dc76c54 100644 --- a/internal/cli/init.go +++ b/internal/cli/init.go @@ -22,13 +22,98 @@ age_recipients: # Named snapshots. Each snapshot backs up one or more paths and can have its # own exclude patterns in addition to the global excludes below. +# +# Exclude pattern semantics: +# - Patterns starting with / are anchored to the snapshot path root +# (e.g. "/Library/Caches" matches only ~/Library/Caches in a ~ snapshot) +# - Patterns without a leading / match anywhere in the tree +# (e.g. ".cache" matches any directory named .cache at any depth) +# - Globs are supported: *, **, ? snapshots: home: paths: - - ~/Documents - - ~/Pictures - # exclude: - # - "*.cache" + - "~" + exclude: + # Trash, temp, and filesystem metadata + - "/.Trash" + - "/.Trashes" + - "/.fseventsd" + - "/.Spotlight-V100" + - "/.TemporaryItems" + - "/tmp" + - "/.rnd" + - ".DS_Store" + # Caches and package manager state (rebuildable) + - ".cache" + - ".bundle" + - "/.cpan/build" + - "/.cpan/sources" + - "/.gradle/caches" + - "/.docker" + - "/.dropbox" + - "/.minikube/cache" + - "/.local/share/containers/podman/machine" + - "/.persepolis" + - "/Library/Caches" + - "/Library/Logs" + - "/Library/Cookies" + - "/Library/Metadata" + - "/Library/Suggestions" + - "/Library/PubSub" + - "/Library/Homebrew" + - "/Library/Developer" + - "/Library/Parallels" + - "/Library/Google/GoogleSoftwareUpdate" + - "/Library/Preferences/Macromedia/Flash Player" + - "/Library/Preferences/SDMHelpData" + - "/Library/VoiceTrigger/SAT" + # Cloud-synced or restorable-from-server data + - "/Library/Mail" + - "/Library/Mail Downloads" + - "/Library/Safari" + - "/Library/Application Support/Evernote" + - "/Library/Application Support/MobileSync" + - "/Library/Application Support/SyncServices" + - "/Library/Application Support/protonmail/bridge/cache" + - "/Library/Application Support/Syncthing/index-*" + - "/Library/Syncthing/folders" + - "/Documents/Dropbox/.dropbox.cache" + # Large rebuildable app data (games, media caches, device backups) + - "/Applications/Fortnite" + - "/Documents/Steam Content" + - "/Library/Application Support/Ableton" + - "/Library/Application Support/CrossOver Games" + - "/Library/Application Support/SecondLife/cache" + - "/Library/Application Support/Steam/SteamApps" + - "/Library/Containers/com.docker.docker" + - "/Library/Group Containers/group.com.apple.secure-control-center-preferences" + - "/Library/iTunes/iPad Software Updates" + - "/Library/iTunes/iPhone Software Updates" + - "/Movies/CacheClip" + - "/Movies/ProxyMedia" + - "/Music/iTunes/Album Artwork" + - "/Pictures/iPod Photo Cache" + + # Third-party applications. OS-provided apps live in /System/Applications + # on modern macOS and are never in /Applications, but Apple-installed + # App Store apps (Safari, GarageBand, iWork, iMovie) are excluded since + # they are re-downloadable. + apps: + paths: + - /Applications + exclude: + - ".DS_Store" + - "/Safari.app" + - "/GarageBand.app" + - "/iMovie.app" + - "/Keynote.app" + - "/Numbers.app" + - "/Pages.app" + - "/Xcode.app" + - "/Spotify.app" + - "/Steam.app" + - "/VirtualBox.app" + - "/Utilities/Adobe Installers" # Storage backend (pick ONE of the three forms below). # diff --git a/internal/cli/init_test.go b/internal/cli/init_test.go new file mode 100644 index 0000000..d42e6a9 --- /dev/null +++ b/internal/cli/init_test.go @@ -0,0 +1,43 @@ +package cli + +import ( + "testing" + + "git.eeqj.de/sneak/vaultik/internal/config" + "gopkg.in/yaml.v3" +) + +// TestDefaultConfigTemplateParses ensures the init template is valid YAML +// that unmarshals into the Config struct with the expected snapshots. +func TestDefaultConfigTemplateParses(t *testing.T) { + var cfg config.Config + if err := yaml.Unmarshal([]byte(defaultConfigTemplate), &cfg); err != nil { + t.Fatalf("default config template is not valid YAML: %v", err) + } + + if len(cfg.AgeRecipients) != 1 { + t.Errorf("expected 1 placeholder age recipient, got %d", len(cfg.AgeRecipients)) + } + + home, ok := cfg.Snapshots["home"] + if !ok { + t.Fatal("expected 'home' snapshot in default config") + } + if len(home.Paths) == 0 { + t.Error("home snapshot should have at least one path") + } + if len(home.Exclude) == 0 { + t.Error("home snapshot should have exclude patterns") + } + + apps, ok := cfg.Snapshots["apps"] + if !ok { + t.Fatal("expected 'apps' snapshot in default config") + } + if len(apps.Paths) != 1 || apps.Paths[0] != "/Applications" { + t.Errorf("apps snapshot should back up /Applications, got %v", apps.Paths) + } + if len(apps.Exclude) == 0 { + t.Error("apps snapshot should have exclude patterns") + } +}