package cli import ( "fmt" "os" "path/filepath" "github.com/adrg/xdg" "github.com/spf13/cobra" ) // RootFlags holds global flags that apply to all commands. // These flags are defined on the root command and inherited by all subcommands. type RootFlags struct { ConfigPath string Verbose bool Debug bool Quiet bool } var rootFlags RootFlags // NewRootCommand creates the root cobra command for the vaultik CLI. // It sets up the command structure, global flags, and adds all subcommands. // This is the main entry point for the CLI command hierarchy. func NewRootCommand() *cobra.Command { cmd := &cobra.Command{ Use: "vaultik", Short: "Secure incremental backup tool with asymmetric encryption", Long: `vaultik is a secure incremental backup tool that encrypts data using age public keys and uploads to S3-compatible storage. No private keys are needed on the source system.`, SilenceUsage: true, } // Add global flags cmd.PersistentFlags().StringVar(&rootFlags.ConfigPath, "config", "", "Path to config file (default: $VAULTIK_CONFIG or platform config dir)") cmd.PersistentFlags().BoolVarP(&rootFlags.Verbose, "verbose", "v", false, "Enable verbose output") cmd.PersistentFlags().BoolVar(&rootFlags.Debug, "debug", false, "Enable debug output") cmd.PersistentFlags().BoolVarP(&rootFlags.Quiet, "quiet", "q", false, "Suppress non-error output") // Add subcommands cmd.AddCommand( NewConfigCommand(), NewRestoreCommand(), NewPruneCommand(), NewStoreCommand(), NewSnapshotCommand(), NewInfoCommand(), NewVersionCommand(), NewRemoteCommand(), NewDatabaseCommand(), ) return cmd } // GetRootFlags returns the global flags that were parsed from the command line. // This allows subcommands to access global flag values like verbosity and config path. func GetRootFlags() RootFlags { return rootFlags } // ResolveConfigPath resolves the config file path from flags, environment, or default. // Search order: --config flag, VAULTIK_CONFIG env, XDG config dir, /etc/vaultik/config.yml. func ResolveConfigPath() (string, error) { if rootFlags.ConfigPath != "" { return rootFlags.ConfigPath, nil } if envPath := os.Getenv("VAULTIK_CONFIG"); envPath != "" { return envPath, nil } for _, path := range defaultConfigPaths() { if _, err := os.Stat(path); err == nil { return path, nil } } return "", fmt.Errorf("no config file found; run 'vaultik config init' to create one, or specify with --config") } // defaultConfigPaths returns the ordered list of config paths to search. // On macOS: ~/Library/Application Support/vaultik/config.yml // On Linux: ~/.config/vaultik/config.yml // Fallback: /etc/vaultik/config.yml func defaultConfigPaths() []string { return []string{ filepath.Join(xdg.ConfigHome, "vaultik", "config.yml"), "/etc/vaultik/config.yml", } } // DefaultConfigPath returns the platform-appropriate default config path. // Used by the init command and in help text. func DefaultConfigPath() string { if os.Getuid() == 0 { return "/etc/vaultik/config.yml" } return filepath.Join(xdg.ConfigHome, "vaultik", "config.yml") }