package config import ( "errors" "fmt" "log/slog" "os" "strings" "github.com/spf13/cobra" "github.com/spf13/viper" "git.avsdev.uk/Go/job-search/internal/config" ) func commandInit() *cobra.Command { cmd := &cobra.Command{ Use: "init [config_path]", Args: cobra.MatchAll(cobra.MinimumNArgs(0), cobra.MaximumNArgs(1)), Short: "Create a configuration file holding the defaults", Long: `Create a configuration file holding the defaults, at config_path or, without one, the first config.yaml found in /etc/job-search then the working directory (/etc/job-search/config.yaml if there is none). The test sources (fts_mock and sleeper) are left out unless --all is given.`, PersistentPreRun: func(cmd *cobra.Command, args []string) { slog.Debug("command persistent pre-run", "command", "config.init") }, PersistentPostRun: func(cmd *cobra.Command, args []string) { slog.Debug("command persistent post-run", "command", "config.init") }, RunE: func(cmd *cobra.Command, args []string) error { slog.Debug("command run", "command", "config.init") if err := cobra.MinimumNArgs(0)(cmd, args); err != nil { return err } if err := cobra.MaximumNArgs(1)(cmd, args); err != nil { return err } // Flags and arguments are valid from here on, so errors are runtime // failures: report them without cobra's usage text. cmd.SilenceUsage = true // With no path, the default locations are used. configFile := "" if len(args) > 0 { configFile = strings.TrimSpace(args[0]) } if configFile != "" { // Use the config file given. viper.SetConfigFile(configFile) } else { // Search config in /etc or local directory. viper.AddConfigPath(config.EtcConfigPath) viper.AddConfigPath(".") viper.SetConfigName("config") } err := viper.ReadInConfig() if err == nil { return errors.New("configuration file already exists") } var notFound viper.ConfigFileNotFoundError if !errors.Is(err, os.ErrNotExist) && !errors.As(err, ¬Found) { // Config file was found but another error was produced return err } path := viper.ConfigFileUsed() if path == "" { path = config.EtcConfigPath + "/config.yaml" } // A Viper of its own, holding only the keys to write: the global // one would write every default, the hidden sections' included. all, _ := cmd.Flags().GetBool("all") generated := viper.New() for key, value := range config.InitDefaults(all) { generated.Set(key, value) } generated.SetConfigFile(path) fmt.Print("writing default configuration to file: " + path + "\n") return generated.WriteConfig() }, } cmd.Flags().Bool( "all", false, "also write the test sources (fts_mock and sleeper)", ) // Config flag should be hidden as the config path should be provided cmd.SetHelpFunc(func(command *cobra.Command, args []string) { command.Flags().MarkHidden("config") //nolint:errcheck,gosec // a root flag, so it always exists command.Parent().HelpFunc()(command, args) }) return cmd }