The init-generated config now ships with a comprehensive home snapshot exclude list (caches, trash, cloud-synced data, rebuildable app state, device backups) derived from a battle-tested rsync backup script, plus an apps snapshot for /Applications excluding Apple-redownloadable apps (Safari, GarageBand, iWork, iMovie) and large third-party installs. Obsolete pre-Catalina app entries (Dashboard, iTunes, DVD Player, etc.) were dropped — OS apps live in /System/Applications on modern macOS and never appear in /Applications. Adds a test asserting the template parses as valid YAML with the expected snapshot structure.
222 lines
7.2 KiB
Go
222 lines
7.2 KiB
Go
package cli
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
const defaultConfigTemplate = `# vaultik configuration
|
|
# Documentation: https://git.eeqj.de/sneak/vaultik
|
|
|
|
# ─── REQUIRED ────────────────────────────────────────────────────────────────
|
|
|
|
# Age recipient public keys for encryption.
|
|
# Backups are encrypted to ALL listed recipients. Any one of the corresponding
|
|
# private keys can decrypt. Generate a keypair with:
|
|
# age-keygen -o key.txt && grep 'public key' key.txt
|
|
age_recipients:
|
|
- age1REPLACE_WITH_YOUR_PUBLIC_KEY
|
|
|
|
# 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:
|
|
- "~"
|
|
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).
|
|
#
|
|
# S3-compatible:
|
|
# storage_url: "s3://mybucket/backups?endpoint=s3.example.com®ion=us-east-1"
|
|
# (also set s3.access_key_id and s3.secret_access_key below)
|
|
#
|
|
# Local filesystem:
|
|
# storage_url: "file:///mnt/backups/vaultik"
|
|
#
|
|
# Rclone (requires rclone configured separately):
|
|
# storage_url: "rclone://myremote/path/to/backups"
|
|
storage_url: ""
|
|
|
|
# ─── S3 CREDENTIALS (required for s3:// storage_url) ────────────────────────
|
|
|
|
# s3:
|
|
# access_key_id: YOUR_ACCESS_KEY
|
|
# secret_access_key: YOUR_SECRET_KEY
|
|
# # region: us-east-1 # Default: us-east-1
|
|
# # use_ssl: true # Default: true
|
|
# # part_size: 5MB # Multipart upload part size. Default: 5MB
|
|
|
|
# ─── OPTIONAL ────────────────────────────────────────────────────────────────
|
|
|
|
# Global exclude patterns applied to ALL snapshots.
|
|
# Snapshot-specific excludes are additive.
|
|
# exclude:
|
|
# - "*.log"
|
|
# - "*.tmp"
|
|
# - ".git"
|
|
# - "node_modules"
|
|
|
|
# Average chunk size for content-defined chunking (FastCDC).
|
|
# Smaller = better deduplication but more metadata overhead.
|
|
# Accepts: 1MB, 10M, 64KB, etc.
|
|
# Default: 10MB
|
|
# chunk_size: 10MB
|
|
|
|
# Maximum blob size before splitting into a new blob.
|
|
# Accepts: 1GB, 10G, 500MB, etc.
|
|
# Default: 10GB
|
|
# blob_size_limit: 10GB
|
|
|
|
# Zstd compression level (1-19). Higher = better ratio but slower.
|
|
# Default: 3
|
|
# compression_level: 3
|
|
|
|
# Hostname used in snapshot IDs. Default: system hostname.
|
|
# hostname: myserver
|
|
|
|
# Path to the local SQLite index database.
|
|
# Default: ~/.local/share/berlin.sneak.app.vaultik/index.sqlite
|
|
# index_path: /path/to/index.sqlite
|
|
`
|
|
|
|
// NewInitCommand creates the init command that writes a default config file.
|
|
func NewInitCommand() *cobra.Command {
|
|
cmd := &cobra.Command{
|
|
Use: "init",
|
|
Short: "Write a default config file",
|
|
Long: `Creates a default configuration file with commented explanations
|
|
for every setting. If a config file already exists at the target path,
|
|
the command refuses to overwrite it.
|
|
|
|
The config is written to the path from --config, $VAULTIK_CONFIG, or
|
|
the platform default config directory (e.g. ~/Library/Application Support/
|
|
on macOS, ~/.config/ on Linux, /etc/vaultik/ as root).`,
|
|
Args: cobra.NoArgs,
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
path := configPathForInit()
|
|
|
|
if _, err := os.Stat(path); err == nil {
|
|
return fmt.Errorf("config file already exists: %s", path)
|
|
}
|
|
|
|
dir := filepath.Dir(path)
|
|
if err := os.MkdirAll(dir, 0o755); err != nil {
|
|
return fmt.Errorf("creating config directory %s: %w", dir, err)
|
|
}
|
|
|
|
if err := os.WriteFile(path, []byte(defaultConfigTemplate), 0o600); err != nil {
|
|
return fmt.Errorf("writing config file: %w", err)
|
|
}
|
|
|
|
fmt.Printf("Config written to %s\n", path)
|
|
fmt.Println("Edit it to set your age_recipients, snapshots, and storage_url.")
|
|
return nil
|
|
},
|
|
}
|
|
|
|
return cmd
|
|
}
|
|
|
|
// configPathForInit returns the config path to write, checking --config flag,
|
|
// VAULTIK_CONFIG env, and the platform default.
|
|
func configPathForInit() string {
|
|
if rootFlags.ConfigPath != "" {
|
|
return rootFlags.ConfigPath
|
|
}
|
|
if envPath := os.Getenv("VAULTIK_CONFIG"); envPath != "" {
|
|
return envPath
|
|
}
|
|
return DefaultConfigPath()
|
|
}
|