From 481a0782c5dbe871bd429266a59a3720281b6fbf Mon Sep 17 00:00:00 2001 From: David Allen Date: Mon, 15 Sep 2025 15:52:42 -0600 Subject: [PATCH] feat: added config implementation using viper --- .gitmodules | 3 ++ cmd/config.go | 115 +++++++++++++++++++++++++++++++++++++++ cmd/download.go | 3 ++ cmd/root.go | 141 ++++++++++++++++++++++++++++-------------------- cmd/serve.go | 16 +++--- makeshift.wiki | 1 + 6 files changed, 215 insertions(+), 64 deletions(-) create mode 100644 .gitmodules create mode 100644 cmd/config.go create mode 160000 makeshift.wiki diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..395de78 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "makeshift.wiki"] + path = makeshift.wiki + url = https://git.towk2.me/towk/makeshift.wiki.git diff --git a/cmd/config.go b/cmd/config.go new file mode 100644 index 0000000..14b8ede --- /dev/null +++ b/cmd/config.go @@ -0,0 +1,115 @@ +package cmd + +import ( + "os" + + "git.towk2.me/towk/makeshift/pkg/util" + "github.com/rs/zerolog/log" + "github.com/spf13/cobra" +) + +const CONFIG_FILE = `--- +# +# Makeshift Config +# +# Repository: https://git.towk2.me/towk/makeshift +# +# Default configuration file for 'makeshift' CLI and service. +# This file was autogenerated using 'makeshift config new' command. +# + +# Set the service host. +host: http://localhost:5050 + +# Set the path to the file or directory to download. +path: help.txt + +# Set the log file path. Logs will be written to this location. +log-file: logs/makeshift.log + +# Set the log level. Possible values include 'debug', 'info', 'warn', +# 'error', 'disabled', and 'trace'. +log-level: info + +# Set the plugins to use when downloading files and/or directories. +plugins: + - smd + - jinja2 + +# Set the positional arguments to pass to ALL plugins when downloading +# files and directories. NOTE: These arguments may be ignored or not +# used within certain plugins. +plugin-args: + +# Set the key-word arguments stored in the data that is passed to ALL +# plugins when downloading files and directories. NOTE: These arguments +# may be ignored or not used within certain plugins. +plugin-kwargs: + +# Set the profiles to use when downloading files and/or directories. +profiles: + - default + +# Set whether to extract the archive when downloading directories. +extract: false + +# Set whether to remove an archive after downloading and extracting. +# This requires that the 'extract' flag be set to 'true'. +remove-archive: false + +# Set the path to a CA certificate. +cacert: "" + +# Set the path to a CA key file. +keyfile: "" +` + +var configCmd = &cobra.Command{ + Use: "config", + Short: "Manage makeshift config file", +} + +var configNewCmd = &cobra.Command{ + Use: "new [path]", + Example: ` + # create a new default config at specified path + makeshift config new configs/makeshift.yaml +`, + Args: cobra.ExactArgs(1), + Short: "Create a new config file", + Run: func(cmd *cobra.Command, args []string) { + for _, path := range args { + var ( + overwrite, _ = cmd.Flags().GetBool("overwrite") + + exists bool + err error + ) + if exists, err = util.PathExists(path); err != nil { + log.Error(). + Err(err). + Str("path", path). + Msg("failed to determine if path exists") + } else { + if exists && !overwrite { + log.Error().Msg("file exists and '--overwrite' flag not passed") + return + } + err := os.WriteFile(path, []byte(CONFIG_FILE), 0o644) + if err != nil { + log.Error(). + Err(err). + Str("path", path). + Msg("failed to write diefault config file to path") + continue + } + } + } + }, +} + +func init() { + configNewCmd.Flags().BoolP("overwrite", "f", false, "Set whether to overwrite an existing file") + configCmd.AddCommand(configNewCmd) + rootCmd.AddCommand(configCmd) +} diff --git a/cmd/download.go b/cmd/download.go index fdb2233..6c50c3f 100644 --- a/cmd/download.go +++ b/cmd/download.go @@ -49,6 +49,7 @@ var downloadCmd = cobra.Command{ host, _ = cmd.Flags().GetString("host") path, _ = cmd.Flags().GetString("path") outputPath, _ = cmd.Flags().GetString("output") + configPath, _ = cmd.Flags().GetString("config") cacertPath, _ = cmd.Flags().GetString("cacert") pluginNames, _ = cmd.Flags().GetStringSlice("plugins") profileIDs, _ = cmd.Flags().GetStringSlice("profiles") @@ -62,6 +63,7 @@ var downloadCmd = cobra.Command{ err error ) + // build download query URL query = fmt.Sprintf("/download/%s?", path) if len(pluginNames) > 0 { query += "plugins=" + url.QueryEscape(strings.Join(pluginNames, ",")) @@ -79,6 +81,7 @@ var downloadCmd = cobra.Command{ log.Debug(). Str("host", host). Str("path", path). + Str("config", configPath). Str("query", query). Str("output", outputPath). Strs("profiles", profileIDs). diff --git a/cmd/root.go b/cmd/root.go index 9f233f6..c55700c 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -4,6 +4,7 @@ import ( "fmt" "net/http" "os" + "path/filepath" "strings" logger "git.towk2.me/towk/makeshift/pkg/log" @@ -20,14 +21,14 @@ var ( var rootCmd = cobra.Command{ Use: "makeshift", Short: "Extensible file cobbler", - PersistentPreRun: func(cmd *cobra.Command, args []string) { + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { var ( - logFile string - err error + logFile, _ = cmd.Flags().GetString("log-file") + configPath, _ = cmd.Flags().GetString("config") + err error ) // initialize the logger - logFile, _ = cmd.Flags().GetString("log-file") err = logger.InitWithLogLevel(loglevel, logFile) if err != nil { log.Error().Err(err).Msg("failed to initialize logger") @@ -35,12 +36,13 @@ var rootCmd = cobra.Command{ } // You can bind cobra and viper in a few locations, but PersistencePreRunE on the root command works well - err = initializeConfig(cmd) + return initConfig(cmd, configPath) }, Run: func(cmd *cobra.Command, args []string) { // try and set flags using env vars setenv(cmd, "log-file", "MAKESHIFT_LOG_FILE") setenv(cmd, "log-level", "MAKESHIFT_LOG_LEVEL") + setenv(cmd, "config", "MAKESHIFT_CONFIG_FILE") if len(args) == 0 { err := cmd.Help() if err != nil { @@ -54,6 +56,7 @@ var rootCmd = cobra.Command{ err := logger.LogFile.Close() if err != nil { log.Error().Err(err).Msg("failed to close log file") + os.Exit(1) } }, } @@ -71,8 +74,83 @@ func init() { initLogger, ) // initialize the config a single time - rootCmd.PersistentFlags().VarP(&loglevel, "log-level", "l", "Set the log level output") + rootCmd.PersistentFlags().VarP(&loglevel, "log-level", "l", "Set the log level output (can be set with MAKESHIFT_LOG_LEVEL)") rootCmd.PersistentFlags().String("log-file", "", "Set the log file path (can be set with MAKESHIFT_LOG_FILE)") + rootCmd.PersistentFlags().StringP("config", "c", "", "Set the config file path (can be set with MAKESHIFT_CONFIG_FILE)") +} + +func initLogger() { + // initialize the logger + logfile, _ := rootCmd.PersistentFlags().GetString("log-file") + err := logger.InitWithLogLevel(loglevel, logfile) + if err != nil { + log.Error().Err(err).Msg("failed to initialize logger") + os.Exit(1) + } +} + +func initConfig(cmd *cobra.Command, path string) error { + + // Dissect the path to separate config name from its directory + var ( + isFlagSet = cmd.Flags().Changed("config") + filename = filepath.Base(path) + ext = filepath.Ext(filename) + directory = filepath.Dir(path) + v = viper.New() + ) + + // The 'config' flag not set, so don't continue + if !isFlagSet { + return nil + } + + // Only use specified YAML file from --config or -c flag + v.SetConfigName(strings.TrimSuffix(filename, ext)) + v.SetConfigType("yaml") + v.AddConfigPath(directory) + + // Attempt to read the config file. Return an error if we cannot parse + // the config file or if it is not found. + if err := v.ReadInConfig(); err != nil { + if isFlagSet { + // It's okay if there isn't a config file when no path is provided + // if _, ok := err.(viper.ConfigFileNotFoundError); !ok { + // return err + // } + switch err.(type) { + case viper.ConfigFileNotFoundError: + log.Error(). + Err(err). + Str("path", path). + Msg("failed to read config") + os.Exit(1) + default: + log.Error().Err(err).Msg("failed to read config") + os.Exit(1) + } + } + } + + // When we bind flags to environment variables expect that the + // environment variables are prefixed, e.g. a flag like --number + // binds to an environment variable STING_NUMBER. This helps + // avoid conflicts. + v.SetEnvPrefix("MAKESHIFT") + + // Environment variables can't have dashes in them, so bind them to their equivalent + // keys with underscores, e.g. --favorite-color to STING_FAVORITE_COLOR + v.SetEnvKeyReplacer(strings.NewReplacer("-", "_")) + + // Bind to environment variables + // Works great for simple config names, but needs help for names + // like --favorite-color which we fix in the bindFlags function + v.AutomaticEnv() + + // Bind the current command's flags to viper + bindFlags(cmd, v) + + return nil } func setenv(cmd *cobra.Command, varname string, envvar string) { @@ -95,16 +173,6 @@ func setenvp(cmd *cobra.Command, varname string, envvar string) { } } -func initLogger() { - // initialize the logger - logfile, _ := rootCmd.PersistentFlags().GetString("log-file") - err := logger.InitWithLogLevel(loglevel, logfile) - if err != nil { - log.Error().Err(err).Msg("failed to initialize logger") - os.Exit(1) - } -} - func handleResponseError(res *http.Response, host, query string, err error) { if err != nil { log.Error().Err(err). @@ -147,44 +215,3 @@ func bindFlags(cmd *cobra.Command, v *viper.Viper) { } }) } - -func initializeConfig(cmd *cobra.Command) error { - v := viper.New() - - // Set the base name of the config file, without the file extension. - v.SetConfigName("makeshift") - - // Set as many paths as you like where viper should look for the - // config file. We are only looking in the current working directory. - v.AddConfigPath(".") - - // Attempt to read the config file, gracefully ignoring errors - // caused by a config file not being found. Return an error - // if we cannot parse the config file. - if err := v.ReadInConfig(); err != nil { - // It's okay if there isn't a config file - if _, ok := err.(viper.ConfigFileNotFoundError); !ok { - return err - } - } - - // When we bind flags to environment variables expect that the - // environment variables are prefixed, e.g. a flag like --number - // binds to an environment variable STING_NUMBER. This helps - // avoid conflicts. - v.SetEnvPrefix("MAKESHIFT") - - // Environment variables can't have dashes in them, so bind them to their equivalent - // keys with underscores, e.g. --favorite-color to STING_FAVORITE_COLOR - v.SetEnvKeyReplacer(strings.NewReplacer("-", "_")) - - // Bind to environment variables - // Works great for simple config names, but needs help for names - // like --favorite-color which we fix in the bindFlags function - v.AutomaticEnv() - - // Bind the current command's flags to viper - bindFlags(cmd, v) - - return nil -} diff --git a/cmd/serve.go b/cmd/serve.go index 9c8c046..b723a02 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -19,17 +19,18 @@ var serveCmd = &cobra.Command{ makeshift serve --root ./test --init -l debug `, Args: cobra.NoArgs, - PreRun: func(cmd *cobra.Command, args []string) { - setenv(cmd, "host", "MAKESHIFT_HOST") - setenv(cmd, "root", "MAKESHIFT_ROOT") - setenv(cmd, "timeout", "MAKESHIFT_TIMEOUT") - setenv(cmd, "cacert", "MAKESHIFT_CACERT") - setenv(cmd, "keyfile", "MAKESHIFT_KEYFILE") - }, + // PreRun: func(cmd *cobra.Command, args []string) { + // setenv(cmd, "host", "MAKESHIFT_HOST") + // setenv(cmd, "root", "MAKESHIFT_ROOT") + // setenv(cmd, "timeout", "MAKESHIFT_TIMEOUT") + // setenv(cmd, "cacert", "MAKESHIFT_CACERT") + // setenv(cmd, "keyfile", "MAKESHIFT_KEYFILE") + // }, Run: func(cmd *cobra.Command, args []string) { var ( host, _ = cmd.Flags().GetString("host") rootPath, _ = cmd.Flags().GetString("root") + configPath, _ = cmd.Flags().GetString("config") cacertPath, _ = cmd.Flags().GetString("cacert") keyfile, _ = cmd.Flags().GetString("keyfile") timeout, _ = cmd.Flags().GetInt("timeout") @@ -60,6 +61,7 @@ var serveCmd = &cobra.Command{ Str("host", parsed.Host). Any("paths", map[string]string{ "root": rootPath, + "config": configPath, "cacert": cacertPath, "keyfile": keyfile, "data": server.PathForData(), diff --git a/makeshift.wiki b/makeshift.wiki new file mode 160000 index 0000000..6409089 --- /dev/null +++ b/makeshift.wiki @@ -0,0 +1 @@ +Subproject commit 64090892b46ffea8d1775cc37b995182aeacf31d